From 1f1b538ed68666b02855191a16f3acd8021efc20 Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Wed, 6 Aug 2014 21:17:47 -0700 Subject: [PATCH 1/9] Make client.rb cleaner and scalable Also add some checks for null node attributes and a python plugin to collect rabbitmq metrics Change-Id: Ia294ad7b53bbd13258002450cc4557963e2678db --- chef/cookbooks/collectd/attributes/default.rb | 1 + .../collectd/files/default/rabbitmq_info.py | 150 ++++++++++++++++++ chef/cookbooks/collectd/metadata.rb | 1 - chef/cookbooks/collectd/recipes/client.rb | 54 ++----- chef/cookbooks/collectd/recipes/default.rb | 4 - chef/cookbooks/collectd/recipes/kairosdb.rb | 39 +++++ chef/cookbooks/collectd/recipes/rabbitmq.rb | 28 ++++ chef/roles/os-ops-messaging.rb | 3 +- 8 files changed, 237 insertions(+), 43 deletions(-) create mode 100644 chef/cookbooks/collectd/files/default/rabbitmq_info.py create mode 100644 chef/cookbooks/collectd/recipes/kairosdb.rb create mode 100644 chef/cookbooks/collectd/recipes/rabbitmq.rb diff --git a/chef/cookbooks/collectd/attributes/default.rb b/chef/cookbooks/collectd/attributes/default.rb index 16575d1..ef211b5 100644 --- a/chef/cookbooks/collectd/attributes/default.rb +++ b/chef/cookbooks/collectd/attributes/default.rb @@ -41,6 +41,7 @@ default[:collectd][:plugins] = {"cpu"=>{}, "memory"=>"", "match_regex"=>"" } +default[:collectd][:included_plugins] = {"kairosdb"=>{}} default[:collectd][:server][:host] = "10.145.81.250" default[:collectd][:server][:port] = "4242" default[:collectd][:server][:protocol] = "tcp" diff --git a/chef/cookbooks/collectd/files/default/rabbitmq_info.py b/chef/cookbooks/collectd/files/default/rabbitmq_info.py new file mode 100644 index 0000000..fe93e45 --- /dev/null +++ b/chef/cookbooks/collectd/files/default/rabbitmq_info.py @@ -0,0 +1,150 @@ +# Name: rabbitmq-collectd-plugin - rabbitmq_info.py +# Author: https://github.com/phrawzty/rabbitmq-collectd-plugin/commits/master +# Description: This plugin uses Collectd's Python plugin to obtain RabbitMQ metrics. +# +# Copyright 2012 Daniel Maher +# +# 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. + +## copied from https://github.com/phrawzty/rabbitmq-collectd-plugin + +import collectd +import subprocess +import re + + +NAME = 'rabbitmq_info' +# Override in config by specifying 'RmqcBin'. +RABBITMQCTL_BIN = '/usr/sbin/rabbitmqctl' +# Override in config by specifying 'PmapBin' +PMAP_BIN = '/usr/bin/pmap' +# Override in config by specifying 'PidofBin'. +PIDOF_BIN = '/bin/pidof' +# Override in config by specifying 'Verbose'. +VERBOSE_LOGGING = False + +# Wasn't specified for some reason... +PID_FILE = '/var/run/rabbitmq/pid' + + + +# Obtain the interesting statistical info +def get_stats(): + stats = {} + stats['ctl_messages'] = 0 + stats['ctl_memory'] = 0 + stats['ctl_consumers'] = 0 + stats['pmap_mapped'] = 0 + stats['pmap_used'] = 0 + stats['pmap_shared'] = 0 + + # call rabbitmqctl + try: + p = subprocess.Popen([RABBITMQCTL_BIN, '-q', 'list_queues', 'messages', 'memory', 'consumers'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except: + logger('err', 'Failed to run %s' % RABBITMQCTL_BIN) + return None + + for line in p.stdout.readlines(): + if re.match('\d', line): + ctl_stats = line.split() + stats['ctl_messages'] += int(ctl_stats[0]) + stats['ctl_memory'] += int(ctl_stats[1]) + stats['ctl_consumers'] += int(ctl_stats[2]) + + if not stats['ctl_memory'] > 0: + logger('warn', '%s reports 0 memory usage. This is probably incorrect.' % RABBITMQCTL_BIN) + + # get the pid of rabbitmq + try: + with open(PID_FILE, 'r') as f: + pid = f.read().strip() + except: + logger('err', 'Unable to read %s' % PID_FILE) + return None + + # use pmap to get proper memory stats + try: + p = subprocess.Popen([PMAP_BIN, '-d', pid], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except: + logger('err', 'Failed to run %s' % PMAP_BIN) + return None + + line = p.stdout.readlines()[-1].strip() + if re.match('mapped', line): + m = re.match(r"\D+(\d+)\D+(\d+)\D+(\d+)", line) + stats['pmap_mapped'] = int(m.group(1)) + stats['pmap_used'] = int(m.group(2)) + stats['pmap_shared'] = int(m.group(3)) + else: + logger('warn', '%s returned something strange.' % PMAP_BIN) + return None + + # Verbose output + logger('verb', '[rmqctl] Messages: %i, Memory: %i, Consumers: %i' % (stats['ctl_messages'], stats['ctl_memory'], stats['ctl_consumers'])) + logger('verb', '[pmap] Mapped: %i, Used: %i, Shared: %i' % (stats['pmap_mapped'], stats['pmap_used'], stats['pmap_shared'])) + + return stats + + +# Config data from collectd +def configure_callback(conf): + global RABBITMQCTL_BIN, PMAP_BIN, PID_FILE, VERBOSE_LOGGING + for node in conf.children: + if node.key == 'RmqcBin': + RABBITMQCTL_BIN = node.values[0] + elif node.key == 'PmapBin': + PMAP_BIN = node.values[0] + elif node.key == 'PidFile': + PID_FILE = node.values[0] + elif node.key == 'Verbose': + VERBOSE_LOGGING = bool(node.values[0]) + else: + logger('warn', 'Unknown config key: %s' % node.key) + + +# Send info to collectd +def read_callback(): + logger('verb', 'read_callback') + info = get_stats() + + if not info: + logger('err', 'No information received - very bad.') + return + + logger('verb', 'About to trigger the dispatch..') + + # send values + for key in info: + logger('verb', 'Dispatching %s : %i' % (key, info[key])) + val = collectd.Values(plugin=NAME) + val.type = 'gauge' + val.type_instance = key + val.values = [int(info[key])] + val.dispatch() + + +# Send log messages (via collectd) +def logger(t, msg): + if t == 'err': + collectd.error('%s: %s' % (NAME, msg)) + if t == 'warn': + collectd.warning('%s: %s' % (NAME, msg)) + elif t == 'verb' and VERBOSE_LOGGING == True: + collectd.info('%s: %s' % (NAME, msg)) + + +# Runtime +collectd.register_config(configure_callback) +collectd.warning('Initialising rabbitmq_info') +collectd.register_read(read_callback) diff --git a/chef/cookbooks/collectd/metadata.rb b/chef/cookbooks/collectd/metadata.rb index af1a1ee..8c9a510 100644 --- a/chef/cookbooks/collectd/metadata.rb +++ b/chef/cookbooks/collectd/metadata.rb @@ -7,5 +7,4 @@ long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version "1.0.3" supports "ubuntu" supports "centos" -depends "apt" depends "yum" diff --git a/chef/cookbooks/collectd/recipes/client.rb b/chef/cookbooks/collectd/recipes/client.rb index 3b132c3..3ab309c 100644 --- a/chef/cookbooks/collectd/recipes/client.rb +++ b/chef/cookbooks/collectd/recipes/client.rb @@ -2,7 +2,7 @@ # Cookbook Name:: collectd # Recipe:: client # -# Copyright 2010, Atari, Inc +# Copyright 2014, Huawei Technologies Co,ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,33 +18,17 @@ # include_recipe "collectd" -#servers = [] -#search(:node, 'recipes:collectd\\:\\:server') do |n| -# servers << n['fqdn'] -#end - -#if servers.empty? -# raise "No servers found. Please configure at least one node with collectd::server." -#end - -#collectd_plugin "network" do -# options :server=>servers -#end - -cookbook_file "#{node['collectd']['plugin_dir']}/kairosdb_writer.py" do - source "kairosdb_writer.py" - owner "root" - group "root" - mode 00644 - notifies :restart, "service[collectd]" - action :create_if_missing -end - -case node["platform_family"] -when "rhel" - node.override["collectd"]["plugins"]=node["collectd"]["rhel"]["plugins"].to_hash -when "debian" - node.override["collectd"]["plugins"]=node["collectd"]["debian"]["plugins"].to_hash +if node["collectd"].attribute?("rhel") or node["collectd"].attribute?("debian") + case node["platform_family"] + when "rhel" + if not node["collectd"]["rhel"]["plugins"].nil? + node.override["collectd"]["plugins"]=node["collectd"]["rhel"]["plugins"].to_hash + end + when "debian" + if not node["collectd"]["debian"]["plugins"].nil? + node.override["collectd"]["plugins"]=node["collectd"]["debian"]["plugins"].to_hash + end + end end node["collectd"]["plugins"].each_pair do |plugin_key, options| @@ -53,13 +37,9 @@ node["collectd"]["plugins"].each_pair do |plugin_key, options| end end -collectd_python_plugin "kairosdb_writer" do - opts = {"KairosDBHost"=>node['collectd']['server']['host'], - "KairosDBPort"=>node['collectd']['server']['port'], - "KairosDBProtocol"=>node['collectd']['server']['protocol'], - "LowercaseMetricNames"=>"true", - "Tags" => "host=#{node['fqdn']}\" \"role=OSROLE\" \"location=China.Beijing.TsingHua\" \"cluster=#{node['cluster']}", - "TypesDB" => node['collectd']['types_db'] - } - options(opts) +#for python plugins or more complicated ones, use seperate recipe to deploy them +if node["collectd"].attribute?("included_plugins") and not node["collectd"]["included_plugins"].nil? + node["collectd"]["included_plugins"].each_pair do |plugin_key, options| + include_recipe("collectd::#{plugin_key}") + end end diff --git a/chef/cookbooks/collectd/recipes/default.rb b/chef/cookbooks/collectd/recipes/default.rb index 7598f8d..f18dbda 100644 --- a/chef/cookbooks/collectd/recipes/default.rb +++ b/chef/cookbooks/collectd/recipes/default.rb @@ -17,10 +17,6 @@ # limitations under the License. # case node["platform_family"] -when "debian" - package "ubuntu-cloud-keyring" do - action :install - end when "rhel" include_recipe "yum::epel" execute "yum-update" do diff --git a/chef/cookbooks/collectd/recipes/kairosdb.rb b/chef/cookbooks/collectd/recipes/kairosdb.rb new file mode 100644 index 0000000..65031ae --- /dev/null +++ b/chef/cookbooks/collectd/recipes/kairosdb.rb @@ -0,0 +1,39 @@ +# +# Cookbook Name:: collectd +# Recipe:: kairosdb +# +# Copyright 2014, Huawei Technologies, Co,ltd +# +# 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. +# +cookbook_file "#{node['collectd']['plugin_dir']}/kairosdb_writer.py" do + source "kairosdb_writer.py" + owner "root" + group "root" + mode 00644 + action :create_if_missing +end + +if ! node['cluster'] + node.set['cluster'] = "no_cluster_defined" +end +collectd_python_plugin "kairosdb_writer" do + opts = {"KairosDBHost"=>node['collectd']['server']['host'], + "KairosDBPort"=>node['collectd']['server']['port'], + "KairosDBProtocol"=>node['collectd']['server']['protocol'], + "LowercaseMetricNames"=>"true", + "Tags" => "host=#{node['fqdn']}\" \"role=OSROLE\" \"location=China.Beijing.TsingHua\" \"cluster=#{node['cluster']}", + "TypesDB" => node['collectd']['types_db'] + } + options(opts) +end diff --git a/chef/cookbooks/collectd/recipes/rabbitmq.rb b/chef/cookbooks/collectd/recipes/rabbitmq.rb new file mode 100644 index 0000000..2ac08fb --- /dev/null +++ b/chef/cookbooks/collectd/recipes/rabbitmq.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: collectd-plugins +# Recipe:: rabbitmq +# +# Copyright 2012, Rackspace Hosting, Inc +# +# 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. +# + + +cookbook_file File.join(node['collectd']['plugin_dir'], "rabbitmq_info.py") do + source "rabbitmq_info.py" + owner "root" + group "root" + mode "0755" +end + +collectd_python_plugin "rabbitmq_info" diff --git a/chef/roles/os-ops-messaging.rb b/chef/roles/os-ops-messaging.rb index 0b679c5..2b5da61 100644 --- a/chef/roles/os-ops-messaging.rb +++ b/chef/roles/os-ops-messaging.rb @@ -14,7 +14,8 @@ override_attributes( "plugins" => { "processes" => { "Process" => ["rabbitmq-server"] } } - } + }, + "included_plugins" => {"rabbitmq"=>{}} } ) run_list( From be92856f204856f77b8b1c3cafa99c45ff16a234 Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Tue, 12 Aug 2014 14:46:03 -0700 Subject: [PATCH 2/9] Use ProcessMatch to filter processnames ProcessMatch provides more flexibility in handling process names, especially in scenarios across platforms and openstack releases. To do: generalize process regex across linux distros and openstack releases. Change-Id: I7dae9a90407134f01d5c9fbb722db62844197e47 --- chef/roles/os-block-storage-api.rb | 2 +- chef/roles/os-block-storage-scheduler.rb | 2 +- chef/roles/os-block-storage-worker.rb | 2 +- chef/roles/os-compute-api-metadata.rb | 2 +- chef/roles/os-compute-api-os-compute.rb | 2 +- chef/roles/os-compute-cert.rb | 2 +- chef/roles/os-compute-scheduler.rb | 2 +- chef/roles/os-compute-vncproxy.rb | 2 +- chef/roles/os-compute-worker.rb | 2 +- chef/roles/os-dashboard.rb | 2 +- chef/roles/os-ha.rb | 2 +- chef/roles/os-identity.rb | 2 +- chef/roles/os-image-api.rb | 2 +- chef/roles/os-image-registry.rb | 2 +- chef/roles/os-network-server.rb | 2 +- chef/roles/os-network.rb | 5 ++++- chef/roles/os-ops-database.rb | 2 +- chef/roles/os-ops-messaging.rb | 2 +- 18 files changed, 21 insertions(+), 18 deletions(-) diff --git a/chef/roles/os-block-storage-api.rb b/chef/roles/os-block-storage-api.rb index e0ee610..a0b5818 100644 --- a/chef/roles/os-block-storage-api.rb +++ b/chef/roles/os-block-storage-api.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-cinder-api"] } + "processes" => { "ProcessMatch" => ["cinder-api\" \"cinder-api"] } } } } diff --git a/chef/roles/os-block-storage-scheduler.rb b/chef/roles/os-block-storage-scheduler.rb index 5e78e88..c254690 100644 --- a/chef/roles/os-block-storage-scheduler.rb +++ b/chef/roles/os-block-storage-scheduler.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-cinder-scheduler"] } + "processes" => { "ProcessMatch" => ["cinder-scheduler\" \"cinder-scheduler"] } } } } diff --git a/chef/roles/os-block-storage-worker.rb b/chef/roles/os-block-storage-worker.rb index cd6ad23..5d6e01d 100644 --- a/chef/roles/os-block-storage-worker.rb +++ b/chef/roles/os-block-storage-worker.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-cinder-volume", "iscsid", "multipathd"] } + "processes" => { "ProcessMatch" => ["cinder-volume\" \"cinder-volume", "iscsid\" \"iscsid", "multipathd\" \"multipathd"] } } } } diff --git a/chef/roles/os-compute-api-metadata.rb b/chef/roles/os-compute-api-metadata.rb index 87a65f1..36ec59b 100644 --- a/chef/roles/os-compute-api-metadata.rb +++ b/chef/roles/os-compute-api-metadata.rb @@ -4,7 +4,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-nova-metadata-api"] } + "processes" => { "ProcessMatch" => ["nova-metadata-api\" \"nova-metadata-api"] } } } } diff --git a/chef/roles/os-compute-api-os-compute.rb b/chef/roles/os-compute-api-os-compute.rb index c148ab7..5c1bf31 100644 --- a/chef/roles/os-compute-api-os-compute.rb +++ b/chef/roles/os-compute-api-os-compute.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-nova-api"] } + "processes" => { "ProcessMatch" => ["nova-api\" \"nova-api"] } } } } diff --git a/chef/roles/os-compute-cert.rb b/chef/roles/os-compute-cert.rb index 5eb6d7b..d6ca8f2 100644 --- a/chef/roles/os-compute-cert.rb +++ b/chef/roles/os-compute-cert.rb @@ -4,7 +4,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-nova-cert"] } + "processes" => { "ProcessMatch" => ["nova-cert\" \"nova-cert"] } } } } diff --git a/chef/roles/os-compute-scheduler.rb b/chef/roles/os-compute-scheduler.rb index 9ffa272..3881389 100644 --- a/chef/roles/os-compute-scheduler.rb +++ b/chef/roles/os-compute-scheduler.rb @@ -14,7 +14,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-nova-scheduler", "openstack-nova-conductor"] } + "processes" => { "ProcessMatch" => ["nova-scheduler\" \"nova-scheduler", "nova-conductor\" \"nova-conductor"] } } } } diff --git a/chef/roles/os-compute-vncproxy.rb b/chef/roles/os-compute-vncproxy.rb index 2a1dec5..2b08850 100644 --- a/chef/roles/os-compute-vncproxy.rb +++ b/chef/roles/os-compute-vncproxy.rb @@ -4,7 +4,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-nova-xvpvncproxy", "openstack-nova-novncproxy"] } + "processes" => { "ProcessMatch" => ["nova-xvpvncproxy\" \"nova-xvpvncproxy", "nova-novncproxy\" \"nova-novncproxy"] } } } } diff --git a/chef/roles/os-compute-worker.rb b/chef/roles/os-compute-worker.rb index cdf1f84..7128ef1 100644 --- a/chef/roles/os-compute-worker.rb +++ b/chef/roles/os-compute-worker.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-nova-compute"] } + "processes" => { "ProcessMatch" => ["nova-compute\" \"nova-compute"] } } } } diff --git a/chef/roles/os-dashboard.rb b/chef/roles/os-dashboard.rb index 23f64ab..43a3062 100644 --- a/chef/roles/os-dashboard.rb +++ b/chef/roles/os-dashboard.rb @@ -4,7 +4,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["httpd"]} + "processes" => { "ProcessMatch" => ["httpd\" \"httpd"]} } } } diff --git a/chef/roles/os-ha.rb b/chef/roles/os-ha.rb index a8c8ea4..af1fc52 100644 --- a/chef/roles/os-ha.rb +++ b/chef/roles/os-ha.rb @@ -4,7 +4,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["haproxy", "keepalived"]} + "processes" => { "ProcessMatch" => ["haproxy\" \"haproxy", "keepalived\" \"keepalived"]} } } } diff --git a/chef/roles/os-identity.rb b/chef/roles/os-identity.rb index 7faf10b..ee374cc 100644 --- a/chef/roles/os-identity.rb +++ b/chef/roles/os-identity.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-keystone"] } + "processes" => { "ProcessMatch" => ["keystone\" \"keystone"] } } } } diff --git a/chef/roles/os-image-api.rb b/chef/roles/os-image-api.rb index cb7014f..d533cff 100644 --- a/chef/roles/os-image-api.rb +++ b/chef/roles/os-image-api.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-glance-api"] } + "processes" => { "ProcessMatch" => ["glance-api\" \"glance-api"] } } } } diff --git a/chef/roles/os-image-registry.rb b/chef/roles/os-image-registry.rb index b79ecdf..c4eebd0 100644 --- a/chef/roles/os-image-registry.rb +++ b/chef/roles/os-image-registry.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["openstack-glance-registry"] } + "processes" => { "ProcessMatch" => ["glance-registry\" \"glance-registry"] } } } } diff --git a/chef/roles/os-network-server.rb b/chef/roles/os-network-server.rb index e2f894c..0b64c6b 100644 --- a/chef/roles/os-network-server.rb +++ b/chef/roles/os-network-server.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["quantum-server"] } + "processes" => { "ProcessMatch" => ["quantum-server\" \"quantum-server"] } } } } diff --git a/chef/roles/os-network.rb b/chef/roles/os-network.rb index 0083344..806f6f1 100644 --- a/chef/roles/os-network.rb +++ b/chef/roles/os-network.rb @@ -16,7 +16,10 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["quantum-dhcp-agent", "quantum-l3-agent", "quantum-openvswitch-agent", "quantum-metadata-agent"] } + "processes" => { "ProcessMatch" => ["quantum-dhcp-agent\" \"quantum-dhcp-agent", + "quantum-l3-agent\" \"quantum-l3-agent", + "quantum-openvswitch-agent\" \"quantum-openvswitch-agent", + "quantum-metadata-agent\" \"quantum-metadata-agent"] } } } } diff --git a/chef/roles/os-ops-database.rb b/chef/roles/os-ops-database.rb index 7388caa..ff243af 100644 --- a/chef/roles/os-ops-database.rb +++ b/chef/roles/os-ops-database.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["mysqld"] } + "processes" => { "ProcessMatch" => ["mysqld\" \"mysqld"] } } } } diff --git a/chef/roles/os-ops-messaging.rb b/chef/roles/os-ops-messaging.rb index 2b5da61..d380c10 100644 --- a/chef/roles/os-ops-messaging.rb +++ b/chef/roles/os-ops-messaging.rb @@ -12,7 +12,7 @@ override_attributes( "collectd" => { "rhel" => { "plugins" => { - "processes" => { "Process" => ["rabbitmq-server"] } + "processes" => { "ProcessMatch" => ["rabbitmq-server\" \"rabbitmq-server"] } } }, "included_plugins" => {"rabbitmq"=>{}} From fc6fbb41882b5f1edb0ccba6070dbe2c4f65dad7 Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Wed, 13 Aug 2014 14:48:35 -0700 Subject: [PATCH 3/9] Timeout dstat command in 2h Stop dstat command in 2h, by which, tempest would have already finished, so dstat log would not fill up the /var space if leave a cluster running for long. Change-Id: I72b54f60832107ceec09d5689865ae5c0dd6261a --- chef/cookbooks/rsyslog/recipes/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chef/cookbooks/rsyslog/recipes/client.rb b/chef/cookbooks/rsyslog/recipes/client.rb index c3a77b7..655e4e2 100644 --- a/chef/cookbooks/rsyslog/recipes/client.rb +++ b/chef/cookbooks/rsyslog/recipes/client.rb @@ -25,7 +25,7 @@ package "dstat" do end execute "dstat" do - command "dstat -tcmndp --top-cpu >>/var/log/dstat.log &" + command "timeout 7200s dstat -tcmndp --top-cpu >>/var/log/dstat.log &" action :run end From 1349af23567563d7f22cd18c3235514366cc6261 Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Thu, 21 Aug 2014 19:18:09 -0700 Subject: [PATCH 4/9] enable system log /var/log/messages Change-Id: I4a6510e0d092991aef17b6ec496bb236ee5cc846 --- cobbler/snippets/kickstart_rsyslog.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cobbler/snippets/kickstart_rsyslog.conf b/cobbler/snippets/kickstart_rsyslog.conf index 4451037..5b827a0 100644 --- a/cobbler/snippets/kickstart_rsyslog.conf +++ b/cobbler/snippets/kickstart_rsyslog.conf @@ -34,7 +34,7 @@ cat << EOL > /etc/rsyslog.conf #### RULES #### - +syslog.*,daemon.* /var/log/messages mail.* -/var/log/maillog # Log cron stuff From 9b6bfcb921e779053aa40ce50ba49e1cff5e7b72 Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Thu, 21 Aug 2014 14:52:22 -0700 Subject: [PATCH 5/9] change rabbitmqctl to http api rabbitmqctl fail to spawn in python plugin environment somehow so switch to http api to get metrics from rabbitmq. Change-Id: Iff4b8a67a7d113bb664883057118897672503a8c --- chef/cookbooks/collectd/attributes/default.rb | 3 +- .../collectd/files/default/rabbitmq_info.py | 92 ++++++++++++++----- chef/cookbooks/collectd/recipes/kairosdb.rb | 1 + chef/cookbooks/collectd/recipes/rabbitmq.rb | 30 +++++- .../openstack-common/recipes/databag.rb | 2 +- chef/databags/openstack/openstack.json | 3 +- 6 files changed, 102 insertions(+), 29 deletions(-) diff --git a/chef/cookbooks/collectd/attributes/default.rb b/chef/cookbooks/collectd/attributes/default.rb index ef211b5..4e65421 100644 --- a/chef/cookbooks/collectd/attributes/default.rb +++ b/chef/cookbooks/collectd/attributes/default.rb @@ -44,4 +44,5 @@ default[:collectd][:plugins] = {"cpu"=>{}, default[:collectd][:included_plugins] = {"kairosdb"=>{}} default[:collectd][:server][:host] = "10.145.81.250" default[:collectd][:server][:port] = "4242" -default[:collectd][:server][:protocol] = "tcp" +default[:collectd][:server][:protocol] = "udp" +default[:collectd][:mq][:vhost] = "/" diff --git a/chef/cookbooks/collectd/files/default/rabbitmq_info.py b/chef/cookbooks/collectd/files/default/rabbitmq_info.py index fe93e45..d492d48 100644 --- a/chef/cookbooks/collectd/files/default/rabbitmq_info.py +++ b/chef/cookbooks/collectd/files/default/rabbitmq_info.py @@ -1,9 +1,10 @@ # Name: rabbitmq-collectd-plugin - rabbitmq_info.py # Author: https://github.com/phrawzty/rabbitmq-collectd-plugin/commits/master -# Description: This plugin uses Collectd's Python plugin to obtain RabbitMQ metrics. +# Description: This plugin uses Collectd's Python plugin to obtain RabbitMQ +# metrics. # # Copyright 2012 Daniel Maher -# +# Copyright 2014 Xinyu Zhao # 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 @@ -16,26 +17,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -## copied from https://github.com/phrawzty/rabbitmq-collectd-plugin - import collectd import subprocess import re - +import requests NAME = 'rabbitmq_info' # Override in config by specifying 'RmqcBin'. RABBITMQCTL_BIN = '/usr/sbin/rabbitmqctl' +RABBITMQ_API = 'http://localhost:15672/api/queues' # Override in config by specifying 'PmapBin' PMAP_BIN = '/usr/bin/pmap' # Override in config by specifying 'PidofBin'. PIDOF_BIN = '/bin/pidof' +# Override in config by specifying 'PidFile. +PID_FILE = "/var/run/rabbitmq/pid" +# Override in config by specifying 'Vhost'. +VHOST = "/" # Override in config by specifying 'Verbose'. VERBOSE_LOGGING = False - -# Wasn't specified for some reason... -PID_FILE = '/var/run/rabbitmq/pid' - +USER = 'guest' +PASS = 'guest' # Obtain the interesting statistical info @@ -48,34 +50,62 @@ def get_stats(): stats['pmap_used'] = 0 stats['pmap_shared'] = 0 - # call rabbitmqctl + # call http api instead of rabbitmqctl to collect statistics due to issue: + # https://github.com/phrawzty/rabbitmq-collectd-plugin/issues/5 try: - p = subprocess.Popen([RABBITMQCTL_BIN, '-q', 'list_queues', 'messages', 'memory', 'consumers'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + r = requests.get('%s/%s' % (RABBITMQ_API, VHOST), + auth=('%s' % USER, '%s' % PASS)) +# p = subprocess.Popen([RABBITMQCTL_BIN, '-q', '-p', VHOST, +# 'list_queues', 'name', 'messages', 'memory', 'consumers'], +# shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except: - logger('err', 'Failed to run %s' % RABBITMQCTL_BIN) + logger('err', 'Failed to run curl %s/%s' % (RABBITMQ_API, VHOST)) return None - for line in p.stdout.readlines(): - if re.match('\d', line): - ctl_stats = line.split() - stats['ctl_messages'] += int(ctl_stats[0]) - stats['ctl_memory'] += int(ctl_stats[1]) - stats['ctl_consumers'] += int(ctl_stats[2]) - +# for line in p.stdout.readlines(): +# ctl_stats = line.split() +# try: +# ctl_stats[1] = int(ctl_stats[1]) +# ctl_stats[2] = int(ctl_stats[2]) +# ctl_stats[3] = int(ctl_stats[3]) +# except: +# continue +# queue_name = ctl_stats[0] +# stats['ctl_messages'] += ctl_stats[1] +# stats['ctl_memory'] += ctl_stats[2] +# stats['ctl_consumers'] += ctl_stats[3] +# stats['ctl_messages_%s' % queue_name] = ctl_stats[1] +# stats['ctl_memory_%s' % queue_name] = ctl_stats[2] +# stats['ctl_consumers_%s' % queue_name] = ctl_stats[3] + try: + resp = r.json() + except: + logger('err', 'No result found for this vhost') + return None + for i in resp: + if "messages" in i: + stats['ctl_messages'] += i['messages'] + stats['ctl_memory'] += i['memory'] + stats['ctl_consumers'] += i['consumers'] + stats['ctl_messages_%s' % i['name']] = i['messages'] + stats['ctl_memory_%s' % i['name']] = i['memory'] + stats['ctl_consumers_%s' % i['name']] = i['consumers'] if not stats['ctl_memory'] > 0: - logger('warn', '%s reports 0 memory usage. This is probably incorrect.' % RABBITMQCTL_BIN) + logger('warn', '%s reports 0 memory usage. This is probably incorrect.' + % RABBITMQ_API) # get the pid of rabbitmq try: with open(PID_FILE, 'r') as f: - pid = f.read().strip() + pid = f.read().strip() except: logger('err', 'Unable to read %s' % PID_FILE) return None # use pmap to get proper memory stats try: - p = subprocess.Popen([PMAP_BIN, '-d', pid], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p = subprocess.Popen([PMAP_BIN, '-d', pid], shell=False, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except: logger('err', 'Failed to run %s' % PMAP_BIN) return None @@ -91,8 +121,11 @@ def get_stats(): return None # Verbose output - logger('verb', '[rmqctl] Messages: %i, Memory: %i, Consumers: %i' % (stats['ctl_messages'], stats['ctl_memory'], stats['ctl_consumers'])) - logger('verb', '[pmap] Mapped: %i, Used: %i, Shared: %i' % (stats['pmap_mapped'], stats['pmap_used'], stats['pmap_shared'])) + logger('verb', '[rmqctl] Messages: %i, Memory: %i, Consumers: %i' % + (stats['ctl_messages'], stats['ctl_memory'], + stats['ctl_consumers'])) + logger('verb', '[pmap] Mapped: %i, Used: %i, Shared: %i' % + (stats['pmap_mapped'], stats['pmap_used'], stats['pmap_shared'])) return stats @@ -100,6 +133,7 @@ def get_stats(): # Config data from collectd def configure_callback(conf): global RABBITMQCTL_BIN, PMAP_BIN, PID_FILE, VERBOSE_LOGGING + global VHOST, RABBITMQ_API, USER, PASS for node in conf.children: if node.key == 'RmqcBin': RABBITMQCTL_BIN = node.values[0] @@ -109,6 +143,14 @@ def configure_callback(conf): PID_FILE = node.values[0] elif node.key == 'Verbose': VERBOSE_LOGGING = bool(node.values[0]) + elif node.key == 'Vhost': + VHOST = node.values[0] + elif node.key == 'User': + USER = node.values[0] + elif node.key == 'Pass': + PASS = node.values[0] + elif node.key == 'Api': + RABBITMQ_API == node.values[0] else: logger('warn', 'Unknown config key: %s' % node.key) @@ -140,7 +182,7 @@ def logger(t, msg): collectd.error('%s: %s' % (NAME, msg)) if t == 'warn': collectd.warning('%s: %s' % (NAME, msg)) - elif t == 'verb' and VERBOSE_LOGGING == True: + elif t == 'verb' and VERBOSE_LOGGING is True: collectd.info('%s: %s' % (NAME, msg)) diff --git a/chef/cookbooks/collectd/recipes/kairosdb.rb b/chef/cookbooks/collectd/recipes/kairosdb.rb index 65031ae..c935f25 100644 --- a/chef/cookbooks/collectd/recipes/kairosdb.rb +++ b/chef/cookbooks/collectd/recipes/kairosdb.rb @@ -22,6 +22,7 @@ cookbook_file "#{node['collectd']['plugin_dir']}/kairosdb_writer.py" do group "root" mode 00644 action :create_if_missing + notifies :restart, resources(:service => "collectd") end if ! node['cluster'] diff --git a/chef/cookbooks/collectd/recipes/rabbitmq.rb b/chef/cookbooks/collectd/recipes/rabbitmq.rb index 2ac08fb..854d739 100644 --- a/chef/cookbooks/collectd/recipes/rabbitmq.rb +++ b/chef/cookbooks/collectd/recipes/rabbitmq.rb @@ -17,12 +17,40 @@ # limitations under the License. # +defaultbag = "openstack" +if !Chef::DataBag.list.key?(defaultbag) + Chef::Application.fatal!("databag '#{defaultbag}' doesn't exist.") + return +end + +myitem = node.attribute?('cluster')? node['cluster']:"env_default" + +if !search(defaultbag, "id:#{myitem}") + Chef::Application.fatal!("databagitem '#{myitem}' doesn't exist.") + return +end + +package "python-requests" do + action :install +end + +mydata = data_bag_item(defaultbag, myitem) cookbook_file File.join(node['collectd']['plugin_dir'], "rabbitmq_info.py") do source "rabbitmq_info.py" owner "root" group "root" mode "0755" + notifies :restart, resources(:service => "collectd") end -collectd_python_plugin "rabbitmq_info" +node.override["collectd"]["mq"]["vhost"] = mydata["mq"]["rabbitmq"]["vhost"] + +collectd_python_plugin "rabbitmq_info" do + opts = { "Vhost" => node["collectd"]["mq"]["vhost"], + "Api" => "http://localhost:15672/api/queues", + "User" => "#{mydata["credential"]["mq"]["rabbitmq"]["username"]}", + "Pass" => "#{mydata["credential"]["mq"]["rabbitmq"]["password"]}" + } + options(opts) +end diff --git a/chef/cookbooks/openstack-common/recipes/databag.rb b/chef/cookbooks/openstack-common/recipes/databag.rb index f5be911..b50f406 100644 --- a/chef/cookbooks/openstack-common/recipes/databag.rb +++ b/chef/cookbooks/openstack-common/recipes/databag.rb @@ -244,7 +244,7 @@ node.override['openstack']['mq']['bind_address'] = mydata['mq']["#{node['opensta node.override['openstack']['mq']['port'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['port'] node.override['openstack']['mq']['user'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['username'] node.override['openstack']['mq']['password'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['password'] -#node.override['openstack']['mq']['vhost'] = "/" +node.override['openstack']['mq']['vhost'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['vhost'] diff --git a/chef/databags/openstack/openstack.json b/chef/databags/openstack/openstack.json index 8299312..9130d7a 100644 --- a/chef/databags/openstack/openstack.json +++ b/chef/databags/openstack/openstack.json @@ -112,7 +112,8 @@ }, "metadata" : { "password" : "Hello_Openstack" }, "mq" : { "rabbitmq" : { "password" : "guest", - "username" : "guest" + "username" : "guest", + "vhost" : "/" } }, "mysql" : { "compute" : { "password" : "admin", "username" : "nova" From 04a642bb4ea1c61a3af06f49c0f5160317950964 Mon Sep 17 00:00:00 2001 From: xiaodongwang Date: Mon, 20 Oct 2014 14:20:10 -0700 Subject: [PATCH 6/9] merge from dev/experimental Change-Id: I9133bbcbad86f242f8cb232cf03f8f54be5b1b24 --- chef/cookbooks/apache2/CHANGELOG.md | 350 +- chef/cookbooks/apache2/Gemfile | 10 - chef/cookbooks/apache2/README.md | 17 +- chef/cookbooks/apache2/attributes/default.rb | 248 +- .../apache2/attributes/mod_auth_cas.rb | 23 +- .../apache2/attributes/mod_auth_openid.rb | 19 +- .../attributes/mod_fastcgi.rb} | 6 +- .../attributes/mod_pagespeed.rb} | 20 +- chef/cookbooks/apache2/attributes/mod_ssl.rb | 9 +- .../apache2/definitions/apache_conf.rb | 8 +- .../apache2/definitions/apache_module.rb | 29 +- .../apache2/definitions/apache_site.rb | 14 +- chef/cookbooks/apache2/definitions/web_app.rb | 28 +- .../default/apache2_module_conf_generate.pl | 2 +- .../default/tests/minitest/default_test.rb | 9 +- .../tests/minitest/god_monitor_test.rb | 6 +- .../default/tests/minitest/mod_apreq2_test.rb | 6 +- .../tests/minitest/mod_auth_cas_test.rb | 7 +- .../tests/minitest/mod_auth_openid_test.rb | 23 +- .../default/tests/minitest/mod_cgi_test.rb | 1 - .../tests/minitest/mod_dav_svn_test.rb | 5 +- .../default/tests/minitest/mod_fastcgi.rb | 5 +- .../tests/minitest/mod_include_test.rb | 3 +- .../default/tests/minitest/mod_perl_test.rb | 3 +- .../default/tests/minitest/mod_php5_test.rb | 4 +- .../default/tests/minitest/mod_python_test.rb | 3 +- .../default/tests/minitest/mod_ssl_test.rb | 15 +- .../default/tests/minitest/support/helpers.rb | 20 +- chef/cookbooks/apache2/metadata.json | 367 + chef/cookbooks/apache2/metadata.rb | 409 +- chef/cookbooks/apache2/recipes/default.rb | 191 +- chef/cookbooks/apache2/recipes/god_monitor.rb | 18 +- chef/cookbooks/apache2/recipes/iptables.rb | 4 +- chef/cookbooks/apache2/recipes/logrotate.rb | 4 +- chef/cookbooks/apache2/recipes/mod_actions.rb | 4 +- chef/cookbooks/apache2/recipes/mod_alias.rb | 6 +- chef/cookbooks/apache2/recipes/mod_apreq2.rb | 33 +- .../apache2/recipes/mod_auth_basic.rb | 6 +- .../cookbooks/apache2/recipes/mod_auth_cas.rb | 58 +- .../apache2/recipes/mod_auth_digest.rb | 6 +- .../apache2/recipes/mod_auth_openid.rb | 110 +- .../apache2/recipes/mod_authn_file.rb | 6 +- .../apache2/recipes/mod_authnz_ldap.rb | 6 +- .../apache2/recipes/mod_authz_default.rb | 11 +- .../apache2/recipes/mod_authz_groupfile.rb | 6 +- .../apache2/recipes/mod_authz_host.rb | 6 +- .../apache2/recipes/mod_authz_user.rb | 6 +- .../apache2/recipes/mod_autoindex.rb | 6 +- chef/cookbooks/apache2/recipes/mod_cgi.rb | 4 +- .../mod_cgi.rb => recipes/mod_cloudflare.rb} | 27 +- chef/cookbooks/apache2/recipes/mod_dav.rb | 4 +- chef/cookbooks/apache2/recipes/mod_dav_fs.rb | 6 +- chef/cookbooks/apache2/recipes/mod_dav_svn.rb | 18 +- chef/cookbooks/apache2/recipes/mod_deflate.rb | 6 +- chef/cookbooks/apache2/recipes/mod_dir.rb | 6 +- chef/cookbooks/apache2/recipes/mod_env.rb | 6 +- chef/cookbooks/apache2/recipes/mod_expires.rb | 6 +- chef/cookbooks/apache2/recipes/mod_fastcgi.rb | 36 +- chef/cookbooks/apache2/recipes/mod_fcgid.rb | 24 +- chef/cookbooks/apache2/recipes/mod_filter.rb | 2 +- chef/cookbooks/apache2/recipes/mod_headers.rb | 6 +- chef/cookbooks/apache2/recipes/mod_include.rb | 4 +- .../recipes/mod_info.rb} | 8 +- .../recipes/mod_jk.rb} | 21 +- chef/cookbooks/apache2/recipes/mod_ldap.rb | 6 +- .../apache2/recipes/mod_log_config.rb | 8 +- chef/cookbooks/apache2/recipes/mod_logio.rb | 8 +- chef/cookbooks/apache2/recipes/mod_mime.rb | 6 +- .../apache2/recipes/mod_negotiation.rb | 6 +- .../mod_pagespeed.rb} | 39 +- chef/cookbooks/apache2/recipes/mod_perl.rb | 20 +- chef/cookbooks/apache2/recipes/mod_php5.rb | 65 +- chef/cookbooks/apache2/recipes/mod_proxy.rb | 6 +- .../apache2/recipes/mod_proxy_ajp.rb | 8 +- .../apache2/recipes/mod_proxy_balancer.rb | 6 +- .../apache2/recipes/mod_proxy_connect.rb | 6 +- .../apache2/recipes/mod_proxy_http.rb | 6 +- chef/cookbooks/apache2/recipes/mod_python.rb | 17 +- chef/cookbooks/apache2/recipes/mod_rewrite.rb | 6 +- .../cookbooks/apache2/recipes/mod_setenvif.rb | 6 +- chef/cookbooks/apache2/recipes/mod_ssl.rb | 24 +- chef/cookbooks/apache2/recipes/mod_status.rb | 6 +- .../recipes/mod_userdir.rb} | 6 +- chef/cookbooks/apache2/recipes/mod_wsgi.rb | 18 +- .../apache2/recipes/mod_xsendfile.rb | 18 +- .../apache2/templates/default/a2enmod.erb | 4 +- .../templates/default/apache2.conf.erb | 23 +- .../templates/default/default-site.erb | 94 +- .../templates/default/etc-sysconfig-httpd.erb | 6 +- .../templates/default/mods/alias.conf.erb | 33 +- .../templates/default/mods/auth_cas.conf.erb | 2 +- .../templates/default/mods/autoindex.conf.erb | 175 +- .../templates/default/mods/deflate.conf.erb | 28 +- .../templates/default/mods/dir.conf.erb | 4 +- .../templates/default/mods/fastcgi.conf.erb | 2 +- .../templates/default/mods/fcgid.conf.erb | 2 +- .../templates/default/mods/info.conf.erb | 14 + .../templates/default/mods/mime.conf.erb | 375 +- .../default/mods/negotiation.conf.erb | 29 +- .../templates/default/mods/pagespeed.conf.erb | 293 + .../templates/default/mods/proxy.conf.erb | 26 +- .../templates/default/mods/setenvif.conf.erb | 48 +- .../templates/default/mods/ssl.conf.erb | 139 +- .../templates/default/mods/status.conf.erb | 41 +- .../apache2/templates/default/ports.conf.erb | 11 +- .../apache2/templates/default/security.erb | 10 +- .../templates/default/web_app.conf.erb | 2 +- .../apache2/test/features/alias_paths.feature | 12 - .../test/features/authenticate_basic.feature | 20 - .../test/features/authenticate_digest.feature | 20 - .../test/features/authenticate_openid.feature | 11 - .../test/features/authorize_groupfile.feature | 16 - .../test/features/authorize_host.feature | 17 - .../test/features/authorize_ldap.feature | 16 - .../test/features/authorize_users.feature | 16 - .../test/features/basic_web_app.feature | 11 - .../test/features/basic_webserver.feature | 16 - .../features/compress_server_response.feature | 16 - .../test/features/control_caching.feature | 11 - .../test/features/directory_listing.feature | 19 - .../test/features/host_cgi_scripts.feature | 11 - .../features/host_perl_applications.feature | 11 - .../features/host_php_applications.feature | 11 - .../features/host_python_applications.feature | 11 - .../host_source_control_repositories.feature | 12 - .../features/proxy_java_applications.feature | 12 - .../test/features/secure_requests.feature | 11 - .../features/step_definitions/svn_steps.rb | 19 - .../step_definitions/webserver_steps.rb | 156 - .../apache2/test/features/support/env.rb | 3 - .../test/features/support/svn_helpers.rb | 24 - .../test/features/support/web_helpers.rb | 86 - .../features/support_older_browsers.feature | 11 - .../apache2/test/kitchen/Kitchenfile | 50 - .../apache2/test/kitchen/cookbooks/Cheffile | 4 - .../kitchen/cookbooks/apache2_test/README.md | 82 - .../apache2_test/attributes/default.rb | 35 - .../files/default/ssl/ldap.example.com.pem | 49 - .../default/tests/minitest/modules_test.rb | 34 - .../default/tests/minitest/support/helpers.rb | 50 - .../cookbooks/apache2_test/metadata.rb | 88 - .../apache2_test/recipes/basic_web_app.rb | 38 - .../apache2_test/recipes/god_monitor.rb | 19 - .../apache2_test/recipes/mod_auth_basic.rb | 35 - .../apache2_test/recipes/mod_auth_digest.rb | 37 - .../apache2_test/recipes/mod_authnz_ldap.rb | 63 - .../recipes/mod_authz_groupfile.rb | 46 - .../recipes/mod_authz_unlisted_host.rb | 30 - .../apache2_test/recipes/mod_authz_user.rb | 41 - .../apache2_test/recipes/mod_dav_svn.rb | 45 - .../apache2_test/recipes/mod_expires.rb | 30 - .../apache2_test/recipes/mod_perl.rb | 66 - .../apache2_test/recipes/mod_php5.rb | 50 - .../apache2_test/recipes/mod_python.rb | 54 - .../cookbooks/apache2_test/recipes/mod_ssl.rb | 54 - .../apache2_test/recipes/mod_status_remote.rb | 26 - .../cookbooks/apache2_test/recipes/modules.rb | 43 - .../cookbooks/apache2_test/recipes/setup.rb | 18 - .../templates/default/auth_basic.conf.erb | 6 - .../templates/default/auth_digest.conf.erb | 7 - .../templates/default/auth_openid.conf.erb | 8 - .../templates/default/authnz_ldap.conf.erb | 9 - .../default/authz_groupfile.conf.erb | 7 - .../templates/default/authz_host.conf.erb | 4 - .../templates/default/authz_user.conf.erb | 6 - .../templates/default/cache_test.conf.erb | 4 - .../templates/default/entries.ldif.erb | 18 - .../templates/default/java_env.conf.erb | 6 - .../templates/default/perl_env.conf.erb | 9 - .../templates/default/php_env.conf.erb | 7 - .../templates/default/python_env.conf.erb | 8 - .../templates/default/ssl.conf.erb | 13 - .../templates/default/status.conf.erb | 9 - .../templates/default/svn_repo.conf.erb | 6 - chef/cookbooks/apt/.kitchen.yml | 68 - chef/cookbooks/apt/Berksfile | 8 - chef/cookbooks/apt/CHANGELOG.md | 54 + chef/cookbooks/apt/README.md | 315 +- chef/cookbooks/apt/TESTING.md | 44 - chef/cookbooks/apt/attributes/default.rb | 22 + chef/cookbooks/apt/libraries/helpers.rb | 48 + chef/cookbooks/apt/libraries/matchers.rb | 17 + .../libraries/network.rb} | 21 +- chef/cookbooks/apt/metadata.json | 54 + chef/cookbooks/apt/metadata.rb | 46 +- chef/cookbooks/apt/providers/preference.rb | 20 +- chef/cookbooks/apt/providers/repository.rb | 50 +- chef/cookbooks/apt/recipes/cacher-client.rb | 27 +- chef/cookbooks/apt/recipes/cacher-ng.rb | 22 +- chef/cookbooks/apt/recipes/default.rb | 50 +- chef/cookbooks/apt/resources/preference.rb | 4 +- chef/cookbooks/apt/resources/repository.rb | 13 +- chef/cookbooks/apt/spec/cacher-client_spec.rb | 31 - chef/cookbooks/apt/spec/cacher-ng_spec.rb | 27 - chef/cookbooks/apt/spec/default_spec.rb | 22 - chef/cookbooks/apt/spec/spec_helper.rb | 13 - .../apt/templates/default/01proxy.erb | 3 + .../apt/test/cookbooks/apt_test/README.md | 1 - .../tests/minitest/cacher-ng-client_test.rb | 41 - .../default/tests/minitest/lwrps_test.rb | 48 - .../apt/test/cookbooks/apt_test/metadata.rb | 6 - .../cookbooks/apt_test/recipes/cacher-ng.rb | 20 - .../test/cookbooks/apt_test/recipes/lwrps.rb | 66 - chef/cookbooks/aws/CHANGELOG.md | 34 + chef/cookbooks/aws/CONTRIBUTING | 29 - chef/cookbooks/aws/README.md | 97 +- chef/cookbooks/aws/attributes/default.rb | 4 +- chef/cookbooks/aws/libraries/ec2.rb | 21 +- chef/cookbooks/aws/metadata.json | 30 + chef/cookbooks/aws/metadata.rb | 2 +- chef/cookbooks/aws/providers/ebs_raid.rb | 55 +- chef/cookbooks/aws/providers/ebs_volume.rb | 9 +- chef/cookbooks/aws/providers/elastic_ip.rb | 51 +- chef/cookbooks/aws/providers/s3_file.rb | 5 +- chef/cookbooks/aws/resources/ebs_raid.rb | 42 +- chef/cookbooks/aws/resources/ebs_volume.rb | 13 + chef/cookbooks/aws/resources/elastic_ip.rb | 8 +- chef/cookbooks/aws/resources/elastic_lb.rb | 3 + chef/cookbooks/aws/resources/resource_tag.rb | 8 +- chef/cookbooks/aws/resources/s3_file.rb | 11 + chef/cookbooks/build-essential/.kitchen.yml | 55 - chef/cookbooks/build-essential/CONTRIBUTING | 29 - chef/cookbooks/build-essential/LICENSE | 201 - chef/cookbooks/build-essential/TESTING.md | 25 - .../build-essential/attributes/default.rb | 1 - chef/cookbooks/build-essential/metadata.json | 1 + .../{build-essential => ceph}/Berksfile | 2 +- chef/cookbooks/ceph/CHANGELOG.md | 17 + chef/cookbooks/ceph/Gemfile | 14 + chef/cookbooks/ceph/LICENSE | 201 + chef/cookbooks/ceph/README.md | 102 + chef/cookbooks/ceph/Rakefile | 48 + chef/cookbooks/ceph/attributes/cephfs.rb | 10 + chef/cookbooks/ceph/attributes/conf.rb | 7 + chef/cookbooks/ceph/attributes/default.rb | 24 + chef/cookbooks/ceph/attributes/mds.rb | 12 + chef/cookbooks/ceph/attributes/mon.rb | 15 + chef/cookbooks/ceph/attributes/osd.rb | 14 + chef/cookbooks/ceph/attributes/radosgw.rb | 43 + .../ceph/attributes/radosgw_apache2.rb | 6 + chef/cookbooks/ceph/attributes/repo.rb | 52 + chef/cookbooks/ceph/conf.rb | 26 + chef/cookbooks/ceph/infrastructure.yml | 6 + chef/cookbooks/ceph/libraries/default.rb | 336 + chef/cookbooks/ceph/libraries/utils.rb | 14 + chef/cookbooks/ceph/metadata.rb | 12 + chef/cookbooks/ceph/providers/client.rb | 80 + .../{git => ceph/recipes}/.gitignore | 13 +- chef/cookbooks/ceph/recipes/.kitchen.yml | 63 + chef/cookbooks/ceph/recipes/.rubocop.yml | 28 + chef/cookbooks/ceph/recipes/.travis.yml | 7 + chef/cookbooks/ceph/recipes/_common.rb | 6 + .../cookbooks/ceph/recipes/_common_install.rb | 1 + chef/cookbooks/ceph/recipes/all_in_one.rb | 6 + chef/cookbooks/ceph/recipes/apt.rb | 27 + chef/cookbooks/ceph/recipes/cephfs.rb | 46 + chef/cookbooks/ceph/recipes/cephfs_install.rb | 5 + chef/cookbooks/ceph/recipes/conf.rb | 27 + chef/cookbooks/ceph/recipes/install.rb | 53 + chef/cookbooks/ceph/recipes/mds.rb | 70 + chef/cookbooks/ceph/recipes/mds_install.rb | 5 + chef/cookbooks/ceph/recipes/mon.rb | 180 + chef/cookbooks/ceph/recipes/mon_install.rb | 5 + .../ceph/recipes/openstack_config_mon.rb | 76 + .../ceph/recipes/openstack_config_radosgw.rb | 100 + chef/cookbooks/ceph/recipes/osd.rb | 193 + chef/cookbooks/ceph/recipes/osd_install.rb | 5 + chef/cookbooks/ceph/recipes/radosgw.rb | 103 + .../cookbooks/ceph/recipes/radosgw_apache2.rb | 104 + .../ceph/recipes/radosgw_apache2_repo.rb | 33 + .../cookbooks/ceph/recipes/radosgw_install.rb | 5 + chef/cookbooks/ceph/recipes/repo.rb | 8 + chef/cookbooks/ceph/recipes/rpm.rb | 37 + chef/cookbooks/ceph/recipes/tgt.rb | 51 + chef/cookbooks/ceph/resources/client.rb | 24 + .../templates/default/ceph.client.keyring.erb | 2 + .../ceph/templates/default/ceph.conf.erb | 59 + .../templates/default/mods/fastcgi.conf.erb | 6 + .../ceph/templates/default/rgw.conf.erb | 39 + .../ceph/templates/default/s3gw.fcgi.erb | 2 + .../ceph/test/integration/Vagrantfile.erb | 10 + .../integration/aio/bats/ceph-running.bats | 19 + chef/cookbooks/chef_handler/CHANGELOG.md | 48 +- chef/cookbooks/chef_handler/CONTRIBUTING | 29 - chef/cookbooks/chef_handler/LICENSE | 201 - chef/cookbooks/chef_handler/README.md | 2 +- .../libraries/matchers.rb} | 20 +- chef/cookbooks/chef_handler/metadata.json | 29 + chef/cookbooks/chef_handler/metadata.rb | 2 +- .../chef_handler/providers/default.rb | 6 +- chef/cookbooks/collectd/attributes/default.rb | 28 +- .../collectd/files/default/kairosdb_writer.py | 1 + .../collectd/files/default/rabbitmq_info.py | 19 +- chef/cookbooks/collectd/recipes/client.rb | 10 +- chef/cookbooks/collectd/recipes/default.rb | 15 +- chef/cookbooks/collectd/recipes/kairosdb.rb | 4 +- chef/cookbooks/collectd/recipes/rabbitmq.rb | 21 +- chef/cookbooks/database/CHANGELOG.md | 150 +- chef/cookbooks/database/CONTRIBUTING | 29 - chef/cookbooks/database/LICENSE | 201 - chef/cookbooks/database/README.md | 756 +- .../libraries/provider_database_mysql.rb | 3 +- .../libraries/provider_database_mysql_user.rb | 9 +- .../provider_database_postgresql_schema.rb | 73 + .../provider_database_postgresql_user.rb | 16 +- .../provider_database_sql_server_user.rb | 18 + .../database/libraries/resource_database.rb | 4 +- .../libraries/resource_database_user.rb | 16 +- .../libraries/resource_mysql_database.rb | 6 +- .../libraries/resource_mysql_database_user.rb | 6 +- .../libraries/resource_postgresql_database.rb | 4 +- .../resource_postgresql_database_schema.rb | 44 + .../resource_postgresql_database_user.rb | 15 +- .../libraries/resource_sql_server_database.rb | 6 +- .../resource_sql_server_database_user.rb | 16 +- chef/cookbooks/database/metadata.json | 46 + chef/cookbooks/database/metadata.rb | 2 +- .../database/templates/default/s3cfg.erb | 4 +- chef/cookbooks/dmg/CHANGELOG.md | 48 + chef/cookbooks/dmg/CONTRIBUTING | 29 - chef/cookbooks/dmg/LICENSE | 201 - chef/cookbooks/dmg/README.md | 183 +- chef/cookbooks/dmg/attributes/default.rb | 6 +- chef/cookbooks/dmg/metadata.json | 30 + chef/cookbooks/dmg/metadata.rb | 15 +- chef/cookbooks/dmg/providers/package.rb | 45 +- chef/cookbooks/dmg/recipes/default.rb | 2 +- chef/cookbooks/dmg/resources/package.rb | 6 +- chef/cookbooks/erlang/.kitchen.yml | 54 - chef/cookbooks/erlang/Berksfile | 8 - chef/cookbooks/erlang/CHANGELOG.md | 22 +- chef/cookbooks/erlang/CONTRIBUTING.md | 257 - chef/cookbooks/erlang/LICENSE | 201 - chef/cookbooks/erlang/TESTING.md | 25 - chef/cookbooks/erlang/attributes/default.rb | 6 +- chef/cookbooks/erlang/chefignore | 96 - .../default/tests/minitest/default_test.rb | 39 - .../files/default/tests/minitest/esl_test.rb | 35 - .../default/tests/minitest/gui_tools_test.rb | 30 - .../default/tests/minitest/support/helpers.rb | 29 - chef/cookbooks/erlang/metadata.json | 45 + chef/cookbooks/erlang/metadata.rb | 30 +- chef/cookbooks/erlang/recipes/esl.rb | 62 +- chef/cookbooks/erlang/recipes/package.rb | 36 +- chef/cookbooks/erlang/recipes/source.rb | 20 +- chef/cookbooks/git/.kitchen.yml | 46 - chef/cookbooks/git/Berksfile | 8 - chef/cookbooks/git/CHANGELOG.md | 60 + chef/cookbooks/git/CONTRIBUTING | 29 - chef/cookbooks/git/Gemfile | 3 - chef/cookbooks/git/LICENSE | 201 - chef/cookbooks/git/README.md | 3 +- chef/cookbooks/git/TESTING.md | 25 - chef/cookbooks/git/attributes/default.rb | 32 +- chef/cookbooks/git/metadata.json | 69 + chef/cookbooks/git/metadata.rb | 48 +- chef/cookbooks/git/providers/config.rb | 45 + chef/cookbooks/git/recipes/default.rb | 33 +- chef/cookbooks/git/recipes/server.rb | 65 +- chef/cookbooks/git/recipes/source.rb | 31 +- chef/cookbooks/git/recipes/windows.rb | 18 +- chef/cookbooks/git/resources/config.rb | 11 + .../git/templates/default/git-xinetd.d.erb | 2 +- chef/cookbooks/haproxy/attributes/default.rb | 131 +- chef/cookbooks/haproxy/providers/lb.rb | 6 +- chef/cookbooks/haproxy/recipes/tcp_lb.rb | 83 +- .../haproxy/templates/default/haproxy.cfg.erb | 24 +- chef/cookbooks/homebrew/CHANGELOG.md | 71 + chef/cookbooks/homebrew/README.md | 144 + chef/cookbooks/homebrew/attributes/default.rb | 22 + .../homebrew/libraries/homebrew_mixin.rb | 53 + .../homebrew/libraries/homebrew_package.rb | 109 + chef/cookbooks/homebrew/libraries/matchers.rb | 11 + chef/cookbooks/homebrew/metadata.json | 32 + chef/cookbooks/homebrew/metadata.rb | 10 + chef/cookbooks/homebrew/providers/cask.rb | 33 + chef/cookbooks/homebrew/providers/tap.rb | 54 + chef/cookbooks/homebrew/recipes/default.rb | 46 + chef/cookbooks/homebrew/resources/cask.rb | 14 + .../key.rb => homebrew/resources/tap.rb} | 22 +- chef/cookbooks/iptables/CHANGELOG.md | 36 +- chef/cookbooks/iptables/CONTRIBUTING | 29 - chef/cookbooks/iptables/LICENSE | 201 - chef/cookbooks/iptables/README.md | 2 +- .../iptables/files/default/rebuild-iptables | 284 - chef/cookbooks/iptables/metadata.json | 7 +- chef/cookbooks/iptables/metadata.rb | 4 +- chef/cookbooks/iptables/recipes/default.rb | 8 +- .../templates/default/rebuild-iptables.erb | 131 + .../keepalived/attributes/default.rb | 19 +- .../cookbooks/keepalived/libraries/default.rb | 33 + chef/cookbooks/keepalived/metadata.rb | 4 + chef/cookbooks/keepalived/recipes/default.rb | 47 +- chef/cookbooks/logrotate/.gitignore | 26 + chef/cookbooks/logrotate/.kitchen.yml | 20 + chef/cookbooks/logrotate/.rubocop.yml | 14 + chef/cookbooks/logrotate/.travis.yml | 9 + chef/cookbooks/logrotate/Berksfile | 7 + chef/cookbooks/logrotate/CHANGELOG.md | 65 + chef/cookbooks/logrotate/CONTRIBUTING.md | 16 + chef/cookbooks/logrotate/Gemfile | 11 + chef/cookbooks/{apache2 => logrotate}/LICENSE | 0 chef/cookbooks/logrotate/README.md | 168 + chef/cookbooks/logrotate/TESTING.md | 49 + .../attributes/default.rb | 29 +- .../logrotate/definitions/logrotate_app.rb | 79 + .../logrotate/libraries/logrotate_config.rb | 93 + chef/cookbooks/logrotate/metadata.json | 1 + chef/cookbooks/logrotate/metadata.rb | 18 + .../recipes/default.rb | 6 +- .../recipes/global.rb} | 18 +- .../templates/default/logrotate-global.erb | 29 + .../logrotate/templates/default/logrotate.erb | 58 + chef/cookbooks/memcached/.kitchen.yml | 40 - chef/cookbooks/memcached/Berksfile | 9 - chef/cookbooks/memcached/CHANGELOG.md | 36 + chef/cookbooks/memcached/CONTRIBUTING | 29 - chef/cookbooks/memcached/Gemfile | 3 - chef/cookbooks/memcached/LICENSE | 201 - chef/cookbooks/memcached/README.md | 89 +- chef/cookbooks/memcached/TESTING.md | 25 - .../cookbooks/memcached/attributes/default.rb | 20 +- .../definitions/memcached_instance.rb | 22 +- chef/cookbooks/memcached/metadata.json | 67 + chef/cookbooks/memcached/metadata.rb | 74 +- chef/cookbooks/memcached/recipes/default.rb | 96 +- .../templates/default/memcached.conf.erb | 1 + .../default/memcached.sysconfig.redhat.erb | 2 +- .../default/memcached.sysconfig.suse.erb | 2 +- .../templates/default/sv-memcached-run.erb | 2 +- .../test/cookbooks/memcached_test/README.md | 1 - .../test/cookbooks/memcached_test/metadata.rb | 6 - chef/cookbooks/mysql/.kitchen.yml | 48 - chef/cookbooks/mysql/Berksfile | 11 - chef/cookbooks/mysql/CHANGELOG.md | 115 + chef/cookbooks/mysql/CONTRIBUTING | 29 - chef/cookbooks/mysql/LICENSE | 201 - chef/cookbooks/mysql/README.md | 61 +- chef/cookbooks/mysql/TESTING.md | 25 - chef/cookbooks/mysql/attributes/client.rb | 34 +- .../mysql/attributes/percona_repo.rb | 23 +- chef/cookbooks/mysql/attributes/server.rb | 268 +- .../mysql/attributes/server_debian.rb | 32 + .../mysql/attributes/server_freebsd.rb | 17 + .../mysql/attributes/server_mac_os_x.rb | 9 + .../cookbooks/mysql/attributes/server_rhel.rb | 45 + .../cookbooks/mysql/attributes/server_suse.rb | 16 + .../mysql/attributes/server_windows.rb | 22 + chef/cookbooks/mysql/libraries/helpers.rb | 22 +- chef/cookbooks/mysql/metadata.json | 167 + chef/cookbooks/mysql/metadata.rb | 231 +- .../cookbooks/mysql/recipes/_server_debian.rb | 134 + .../mysql/recipes/_server_mac_os_x.rb | 40 + chef/cookbooks/mysql/recipes/_server_rhel.rb | 86 + .../mysql/recipes/_server_windows.rb | 64 + chef/cookbooks/mysql/recipes/client.rb | 19 +- chef/cookbooks/mysql/recipes/default.rb | 4 +- chef/cookbooks/mysql/recipes/percona_repo.rb | 48 - chef/cookbooks/mysql/recipes/ruby.rb | 36 +- chef/cookbooks/mysql/recipes/server.rb | 223 +- chef/cookbooks/mysql/recipes/server_ec2.rb | 28 +- .../mysql/templates/default/debian.cnf.erb | 4 +- .../mysql/templates/default/grants.sql.erb | 6 +- .../templates/default/init-mysql.conf.erb | 44 + .../mysql/templates/default/my.cnf.erb | 103 +- .../templates/default/usr.sbin.mysqld.erb | 40 + .../templates/ubuntu-10/init-mysql.conf.erb | 42 + .../mysql/templates/windows/my.cnf.erb | 61 - .../mysql/templates/windows/my.ini.erb | 85 + .../mysql/test/cookbooks/mysql_test/README.md | 63 - .../default/tests/minitest/server_test.rb | 36 - .../default/tests/minitest/support/helpers.rb | 11 - .../test/cookbooks/mysql_test/metadata.rb | 9 - .../cookbooks/mysql_test/recipes/server.rb | 62 - .../test/features/query_database.feature | 26 - .../features/step_definitions/mysql_steps.rb | 47 - .../mysql/test/features/support/env.rb | 3 - .../test/features/support/mysql_helpers.rb | 51 - chef/cookbooks/openssh/.gitignore | 21 + chef/cookbooks/openssh/.kitchen.cloud.yml | 71 + chef/cookbooks/openssh/.kitchen.yml | 14 + chef/cookbooks/openssh/.rubocop.yml | 11 + chef/cookbooks/openssh/.travis.yml | 9 + chef/cookbooks/{xfs => openssh}/Berksfile | 3 +- chef/cookbooks/openssh/CHANGELOG.md | 8 + .../{apache2 => openssh}/CONTRIBUTING.md | 0 chef/cookbooks/openssh/Gemfile | 13 + chef/cookbooks/{apt => openssh}/LICENSE | 0 chef/cookbooks/openssh/TESTING.md | 53 + chef/cookbooks/openssh/attributes/default.rb | 14 +- chef/cookbooks/openssh/libraries/default.rb | 43 + chef/cookbooks/openssh/metadata.json | 41 - chef/cookbooks/openssh/metadata.rb | 3 +- chef/cookbooks/openssh/providers/key.rb | 59 + chef/cookbooks/openssh/recipes/default.rb | 2 +- .../cookbooks/openssh/recipes/passwordless.rb | 4 + chef/cookbooks/openssh/resources/key.rb | 14 + chef/cookbooks/openssh/spec/spec_helper.rb | 2 + .../openssh/spec/unit/recipes/default_spec.rb | 51 + .../openssh/templates/default/sshd_config.erb | 25 +- .../default/bats/check_service.bats | 3 + chef/cookbooks/openssl/CONTRIBUTING | 29 - chef/cookbooks/openssl/LICENSE | 201 - .../openstack-block-storage/.rubocop.yml | 24 + .../cookbooks/openstack-block-storage/.tailor | 25 - .../openstack-block-storage/Berksfile.lock | 68 - .../openstack-block-storage/CHANGELOG.md | 87 +- .../cookbooks/openstack-block-storage/Gemfile | 18 +- .../openstack-block-storage/Gemfile.lock | 251 +- .../openstack-block-storage/README.md | 149 +- .../openstack-block-storage/Strainerfile | 2 +- .../openstack-block-storage/TESTING.md | 29 + .../attributes/default.rb | 345 +- .../files/default/cinder-volumes.sh | 11 - .../openstack-block-storage/metadata.rb | 37 +- .../openstack-block-storage/recipes/api.rb | 84 +- .../recipes/cinder-common.rb | 88 +- .../recipes/cinder-config-ceph.rb | 75 + .../openstack-block-storage/recipes/client.rb | 32 + .../recipes/identity_registration.rb | 54 +- .../recipes/scheduler.rb | 53 +- .../openstack-block-storage/recipes/test.rb | 20 - .../openstack-block-storage/recipes/volume.rb | 251 +- .../spec/api-opensuse_spec.rb | 51 +- .../spec/api-redhat_spec.rb | 49 +- .../openstack-block-storage/spec/api_spec.rb | 225 +- .../spec/cinder_common-opensuse_spec.rb | 18 - .../spec/cinder_common-redhat_spec.rb | 29 +- .../spec/cinder_common-suse_spec.rb | 19 + .../spec/cinder_common_spec.rb | 649 +- .../spec/client-redhat_spec.rb | 16 + .../spec/client_spec.rb | 16 + .../spec/default_spec.rb | 8 +- .../spec/identity_registration_spec.rb | 152 +- .../spec/scheduler-opensuse_spec.rb | 44 - .../spec/scheduler-redhat_spec.rb | 62 +- .../spec/scheduler-suse_spec.rb | 42 + .../spec/scheduler_spec.rb | 127 +- .../spec/spec_helper.rb | 163 +- .../spec/volume-opensuse_spec.rb | 65 - .../spec/volume-redhat_spec.rb | 225 +- .../spec/volume-suse_spec.rb | 75 + .../spec/volume_spec.rb | 419 +- .../templates/default/api-paste.ini.erb | 8 +- .../templates/default/cinder-group-active.erb | 47 + .../templates/default/cinder.conf.erb | 340 +- .../default/cinder_emc_config.xml.erb | 10 + .../templates/default/nfs_shares.conf.erb | 3 + .../templates/default/policy.json.erb | 34 - .../templates/default/shares.conf.erb | 3 +- .../templates/default/targets.conf.erb | 6 +- chef/cookbooks/openstack-common/.rubocop.yml | 24 + chef/cookbooks/openstack-common/.tailor | 25 - chef/cookbooks/openstack-common/CHANGELOG.md | 99 + chef/cookbooks/openstack-common/Gemfile | 16 +- chef/cookbooks/openstack-common/Gemfile.lock | 254 +- chef/cookbooks/openstack-common/README.md | 104 +- chef/cookbooks/openstack-common/Strainerfile | 2 +- chef/cookbooks/openstack-common/TESTING.md | 42 + .../openstack-common/attributes/database.rb | 188 + .../openstack-common/attributes/default.rb | 570 +- .../openstack-common/attributes/messaging.rb | 139 + .../files/default/RPM-GPG-KEY-RDO-Icehouse | 52 + .../openstack-common/libraries/cli.rb | 90 + .../openstack-common/libraries/database.rb | 52 +- .../openstack-common/libraries/endpoints.rb | 93 +- .../openstack-common/libraries/network.rb | 14 +- .../openstack-common/libraries/parse.rb | 27 +- .../openstack-common/libraries/passwords.rb | 60 +- .../openstack-common/libraries/search.rb | 52 +- .../openstack-common/libraries/uri.rb | 48 +- chef/cookbooks/openstack-common/metadata.rb | 28 +- .../openstack-common/recipes/ceph_client.rb | 48 + .../openstack-common/recipes/client.rb | 32 + .../openstack-common/recipes/databag.rb | 388 - .../openstack-common/recipes/default.rb | 163 +- .../openstack-common/recipes/environ.rb | 342 - .../openstack-common/recipes/logging.rb | 15 +- .../openstack-common/recipes/openrc.rb | 58 + .../recipes/set_endpoints_by_interface.rb | 31 + .../recipes/sysctl.rb} | 25 +- .../openstack-common/spec/ceph_spec.rb | 70 + .../openstack-common/spec/cli_spec.rb | 104 + .../spec/client-redhat_spec.rb | 18 + .../openstack-common/spec/client_spec.rb | 18 + .../openstack-common/spec/database_spec.rb | 69 +- .../spec/default-redhat_spec.rb | 53 + .../spec/default-suse_spec.rb | 47 +- .../openstack-common/spec/default_spec.rb | 40 +- .../openstack-common/spec/endpoints_spec.rb | 364 +- .../openstack-common/spec/logging_spec.rb | 88 +- .../openstack-common/spec/network_spec.rb | 80 +- .../openstack-common/spec/openrc_spec.rb | 58 + .../openstack-common/spec/parse_spec.rb | 95 +- .../openstack-common/spec/password_spec.rb | 176 +- .../openstack-common/spec/search_spec.rb | 243 +- .../openstack-common/spec/spec_helper.rb | 57 +- .../openstack-common/spec/sysctl_spec.rb | 32 + .../openstack-common/spec/uri_spec.rb | 153 +- .../templates/default/60-openstack.conf.erb | 5 + .../templates/default/ceph.client.keyring.erb | 2 + .../templates/default/ceph.conf.erb | 8 + .../templates/default/logging.conf.erb | 6 +- .../templates/default/openrc.erb | 15 + chef/cookbooks/openstack-compute/.rubocop.yml | 24 + chef/cookbooks/openstack-compute/.tailor | 25 - .../openstack-compute/Berksfile.lock | 65 - chef/cookbooks/openstack-compute/CHANGELOG.md | 103 + chef/cookbooks/openstack-compute/Gemfile | 17 +- chef/cookbooks/openstack-compute/Gemfile.lock | 206 +- chef/cookbooks/openstack-compute/README.md | 196 +- chef/cookbooks/openstack-compute/Strainerfile | 2 +- chef/cookbooks/openstack-compute/TESTING.md | 42 + .../openstack-compute/attributes/default.rb | 621 +- .../files/default/add_floaters.py | 14 +- .../files/default/nova-compute.conf | 7 +- chef/cookbooks/openstack-compute/metadata.rb | 52 +- .../openstack-compute/recipes/api-ec2.rb | 66 +- .../openstack-compute/recipes/api-metadata.rb | 70 +- .../recipes/api-os-compute.rb | 72 +- .../openstack-compute/recipes/client.rb | 32 + .../recipes/compute-config-ceph.rb | 108 + .../openstack-compute/recipes/compute.rb | 83 +- .../openstack-compute/recipes/conductor.rb | 19 +- .../openstack-compute/recipes/default.rb | 1 + .../recipes/identity_registration.rb | 88 +- .../openstack-compute/recipes/libvirt.rb | 182 +- .../openstack-compute/recipes/libvirt_rbd.rb | 68 + .../openstack-compute/recipes/network.rb | 23 +- .../openstack-compute/recipes/nova-cert.rb | 19 +- .../openstack-compute/recipes/nova-common.rb | 250 +- .../openstack-compute/recipes/nova-setup.rb | 109 +- .../openstack-compute/recipes/scheduler.rb | 25 +- .../openstack-compute/recipes/vncproxy.rb | 39 +- .../spec/api-ec2-redhat_spec.rb | 28 +- .../openstack-compute/spec/api-ec2_spec.rb | 39 +- .../spec/api-metadata-redhat_spec.rb | 28 +- .../spec/api-metadata_spec.rb | 39 +- .../spec/api-os-compute-redhat_spec.rb | 28 +- .../spec/api-os-compute_spec.rb | 61 +- .../spec/client-redhat_spec.rb | 18 + .../openstack-compute/spec/client_spec.rb | 18 + .../spec/compute-opensuse_spec.rb | 16 - .../spec/compute-redhat_spec.rb | 60 +- .../spec/compute-suse_spec.rb | 18 + .../openstack-compute/spec/compute_spec.rb | 132 +- .../spec/conductor_redhat_spec.rb | 34 +- .../openstack-compute/spec/conductor_spec.rb | 35 +- .../openstack-compute/spec/default_spec.rb | 6 +- .../spec/identity_registration_spec.rb | 226 +- .../spec/libvirt-opensuse_spec.rb | 96 - .../spec/libvirt-redhat_spec.rb | 77 +- .../spec/libvirt-suse_spec.rb | 70 + .../spec/libvirt_rbd_spec.rb | 64 + .../openstack-compute/spec/libvirt_spec.rb | 137 +- .../spec/network-redhat_spec.rb | 31 +- .../openstack-compute/spec/network_spec.rb | 60 +- .../spec/nova-cert-redhat_spec.rb | 28 +- .../openstack-compute/spec/nova-cert_spec.rb | 31 +- .../spec/nova-common-redhat_spec.rb | 60 +- .../spec/nova-common_spec.rb | 740 +- .../openstack-compute/spec/nova-setup_spec.rb | 112 +- .../spec/scheduler-redhat_spec.rb | 33 +- .../openstack-compute/spec/scheduler_spec.rb | 38 +- .../openstack-compute/spec/spec_helper.rb | 256 +- .../spec/vncproxy-redhat_spec.rb | 42 +- .../openstack-compute/spec/vncproxy_spec.rb | 47 +- .../templates/default/api-paste.ini.erb | 20 +- .../templates/default/libvirtd.conf.erb | 12 +- .../templates/default/nova.conf.erb | 503 +- .../templates/default/openrc.erb | 23 - .../rootwrap.d/api-metadata.filters.erb | 15 - .../default/rootwrap.d/compute.filters.erb | 203 - .../default/rootwrap.d/network.filters.erb | 77 - .../templates/default/secret.xml.erb | 6 + .../openstack-dashboard/.rubocop.yml | 24 + chef/cookbooks/openstack-dashboard/.tailor | 25 - .../openstack-dashboard/CHANGELOG.md | 28 + chef/cookbooks/openstack-dashboard/Gemfile | 16 +- .../openstack-dashboard/Gemfile.lock | 254 +- chef/cookbooks/openstack-dashboard/README.md | 73 +- .../openstack-dashboard/Strainerfile | 2 +- chef/cookbooks/openstack-dashboard/TESTING.md | 42 + .../openstack-dashboard/attributes/default.rb | 202 +- .../cookbooks/openstack-dashboard/metadata.rb | 18 +- .../openstack-dashboard/recipes/default.rb | 1 + .../openstack-dashboard/recipes/server.rb | 284 +- .../openstack-dashboard/spec/default_spec.rb | 4 - .../spec/server-fedora_spec.rb | 59 +- .../spec/server-opensuse_spec.rb | 65 - .../spec/server-redhat_spec.rb | 182 +- .../spec/server-suse_spec.rb | 81 + .../openstack-dashboard/spec/server_spec.rb | 909 +- .../openstack-dashboard/spec/spec_helper.rb | 83 +- .../templates/default/dash-site.erb | 38 +- .../templates/default/local_settings.py.erb | 319 +- .../cookbooks/openstack-identity/.rubocop.yml | 31 + chef/cookbooks/openstack-identity/.tailor | 25 - .../openstack-identity/Berksfile.lock | 37 - .../cookbooks/openstack-identity/CHANGELOG.md | 57 + chef/cookbooks/openstack-identity/Gemfile | 16 +- .../cookbooks/openstack-identity/Gemfile.lock | 230 +- chef/cookbooks/openstack-identity/README.md | 93 +- .../cookbooks/openstack-identity/Strainerfile | 4 +- chef/cookbooks/openstack-identity/TESTING.md | 42 + .../openstack-identity/attributes/default.rb | 266 +- .../files/default/keystone_plugin.py | 96 - .../openstack-identity/libraries/default.rb | 43 + .../openstack-identity/libraries/matchers.rb | 51 + chef/cookbooks/openstack-identity/metadata.rb | 19 +- .../openstack-identity/providers/register.rb | 229 +- .../openstack-identity/recipes/client.rb | 32 + .../openstack-identity/recipes/default.rb | 1 + .../recipes/registration.rb | 218 +- .../openstack-identity/recipes/server.rb | 319 +- .../openstack-identity/resources/register.rb | 43 +- .../spec/client-redhat_spec.rb | 18 + .../openstack-identity/spec/client_spec.rb | 18 + .../openstack-identity/spec/default_spec.rb | 4 - .../openstack-identity/spec/register_spec.rb | 672 +- .../spec/registration_spec.rb | 230 + .../spec/server-opensuse_spec.rb | 115 - .../spec/server-redhat_spec.rb | 54 +- .../spec/server-suse_spec.rb | 86 + .../openstack-identity/spec/server_spec.rb | 953 +- .../openstack-identity/spec/spec_helper.rb | 67 +- .../templates/default/keystone.conf.erb | 275 +- chef/cookbooks/openstack-image/.rubocop.yml | 24 + chef/cookbooks/openstack-image/.tailor | 25 - chef/cookbooks/openstack-image/Berksfile.lock | 41 - chef/cookbooks/openstack-image/CHANGELOG.md | 76 + chef/cookbooks/openstack-image/Gemfile | 17 +- chef/cookbooks/openstack-image/Gemfile.lock | 206 +- chef/cookbooks/openstack-image/README.md | 95 +- chef/cookbooks/openstack-image/Strainerfile | 2 +- chef/cookbooks/openstack-image/TESTING.md | 42 + .../openstack-image/attributes/default.rb | 206 +- chef/cookbooks/openstack-image/metadata.rb | 23 +- .../openstack-image/providers/image.rb | 56 +- chef/cookbooks/openstack-image/recipes/api.rb | 318 +- .../openstack-image/recipes/client.rb | 32 + .../recipes/glance-config-ceph.rb | 45 + .../recipes/identity_registration.rb | 38 +- .../openstack-image/recipes/image_upload.rb | 61 + .../openstack-image/recipes/registry.rb | 150 +- .../openstack-image/resources/image.rb | 15 +- .../openstack-image/spec/api-redhat_spec.rb | 35 +- .../openstack-image/spec/api_spec.rb | 536 +- .../spec/client-redhat_spec.rb | 18 + .../openstack-image/spec/client_spec.rb | 18 + .../openstack-image/spec/default_spec.rb | 4 - .../spec/identity_registration_spec.rb | 145 +- .../openstack-image/spec/image_upload_spec.rb | 57 + .../spec/registry-redhat_spec.rb | 62 +- .../openstack-image/spec/registry_spec.rb | 230 +- .../openstack-image/spec/spec_helper.rb | 211 +- .../templates/default/glance-api.conf.erb | 155 +- .../templates/default/glance-cache.conf.erb | 42 +- .../default/glance-registry.conf.erb | 16 +- .../templates/default/policy.json.erb | 4 - .../templates/default/tinyimage.sh.erb | 38 - .../openstack-metering/Berksfile.lock | 41 - .../cookbooks/openstack-metering/CHANGELOG.md | 19 - chef/cookbooks/openstack-metering/Gemfile | 9 - chef/cookbooks/openstack-metering/README.md | 81 - .../openstack-metering/attributes/default.rb | 73 - .../files/default/policy.json | 3 - chef/cookbooks/openstack-metering/metadata.rb | 21 - .../openstack-metering/recipes/collector.rb | 37 - .../openstack-metering/recipes/common.rb | 89 - .../recipes/identity_registration.rb | 51 - .../spec/agent-central-opensuse_spec.rb | 19 - .../spec/agent-central_spec.rb | 21 - .../spec/agent-compute-opensuse_spec.rb | 19 - .../spec/agent-compute_spec.rb | 21 - .../spec/api-opensuse_spec.rb | 19 - .../openstack-metering/spec/api_spec.rb | 35 - .../spec/collector-opensuse_spec.rb | 19 - .../openstack-metering/spec/collector_spec.rb | 22 - .../spec/common-opensuse_spec.rb | 15 - .../openstack-metering/spec/common_spec.rb | 88 - .../spec/identity_registration_spec.rb | 42 - .../openstack-metering/spec/spec_helper.rb | 37 - .../templates/default/ceilometer.conf.erb | 36 - chef/cookbooks/openstack-network/.rubocop.yml | 24 + chef/cookbooks/openstack-network/Berksfile | 2 - .../openstack-network/Berksfile.lock | 41 - chef/cookbooks/openstack-network/CHANGELOG.md | 89 + chef/cookbooks/openstack-network/Gemfile | 18 +- chef/cookbooks/openstack-network/Gemfile.lock | 201 +- chef/cookbooks/openstack-network/README.md | 105 +- chef/cookbooks/openstack-network/Strainerfile | 2 +- chef/cookbooks/openstack-network/TESTING.md | 42 + .../openstack-network/attributes/default.rb | 883 +- .../etc/quantum/rootwrap.d/debug.filters | 14 - .../etc/quantum/rootwrap.d/dhcp.filters | 40 - .../rootwrap.d/iptables-firewall.filters | 21 - .../default/etc/quantum/rootwrap.d/l3.filters | 43 - .../quantum/rootwrap.d/lbaas-haproxy.filters | 29 - .../rootwrap.d/linuxbridge-plugin.filters | 21 - .../etc/quantum/rootwrap.d/nec-plugin.filters | 15 - .../rootwrap.d/openvswitch-plugin.filters | 29 - .../etc/quantum/rootwrap.d/ryu-plugin.filters | 25 - .../files/default/neutron-ha-tool.py | 549 + .../files/default/ovs-dpctl-top | 1687 ++ .../files/default/quantum-ha-tool.py | 424 - chef/cookbooks/openstack-network/metadata.rb | 27 +- .../openstack-network/recipes/balancer.rb | 40 +- .../openstack-network/recipes/bigswitch.rb | 5 +- .../openstack-network/recipes/brocade.rb | 5 +- .../recipes/build_openvswitch_source.rb | 99 + .../openstack-network/recipes/cisco.rb | 5 +- .../openstack-network/recipes/client.rb | 32 + .../openstack-network/recipes/common.rb | 573 +- .../openstack-network/recipes/dhcp_agent.rb | 95 +- .../openstack-network/recipes/hyperv.rb | 5 +- .../recipes/identity_registration.rb | 38 +- .../openstack-network/recipes/l3_agent.rb | 62 +- .../openstack-network/recipes/linuxbridge.rb | 41 +- .../recipes/metadata_agent.rb | 50 +- .../openstack-network/recipes/metaplugin.rb | 5 +- .../openstack-network/recipes/midonet.rb | 5 +- .../openstack-network/recipes/nec.rb | 5 +- .../openstack-network/recipes/nicira.rb | 5 +- .../openstack-network/recipes/openvswitch.rb | 274 +- .../openstack-network/recipes/plumgrid.rb | 5 +- .../openstack-network/recipes/ryu.rb | 5 +- .../openstack-network/recipes/server.rb | 112 +- .../spec/balancer-redhat_spec.rb | 26 + .../spec/balancer-suse_spec.rb | 26 + .../openstack-network/spec/balancer_spec.rb | 69 +- .../spec/build_openvswitch_source_spec.rb | 61 + .../spec/client-redhat_spec.rb | 16 + .../openstack-network/spec/client_spec.rb | 17 + .../spec/common-redhat_spec.rb | 28 + .../openstack-network/spec/common_spec.rb | 88 +- .../spec/dhcp_agent-opensuse_spec.rb | 37 - .../spec/dhcp_agent-suse_spec.rb | 59 + .../openstack-network/spec/dhcp_agent_spec.rb | 170 +- .../openstack-network/spec/hyperv_spec.rb | 27 + .../spec/identity_registration_spec.rb | 157 +- .../openstack-network/spec/l3_agent_spec.rb | 103 +- .../spec/linuxbridge-opensuse_spec.rb | 23 - .../spec/linuxbridge-redhat_spec.rb | 62 +- .../spec/linuxbridge-suse_spec.rb | 32 + .../spec/linuxbridge_spec.rb | 87 +- .../spec/metadata_agent_spec.rb | 104 +- .../spec/openvswitch-opensuse_spec.rb | 26 - .../spec/openvswitch-redhat_spec.rb | 38 + .../spec/openvswitch-suse_spec.rb | 34 + .../spec/openvswitch_spec.rb | 258 +- .../spec/server-opensuse_spec.rb | 63 - .../spec/server-redhat_spec.rb | 40 +- .../spec/server-suse_spec.rb | 61 + .../openstack-network/spec/server_spec.rb | 791 +- .../openstack-network/spec/spec_helper.rb | 136 +- .../templates/default/api-paste.ini.erb | 39 +- .../templates/default/dhcp_agent.ini.erb | 12 +- .../templates/default/l3_agent.ini.erb | 8 +- .../templates/default/lbaas_agent.ini.erb | 14 +- .../templates/default/metadata_agent.ini.erb | 13 +- ...{quantum-server.erb => neutron-server.erb} | 6 +- .../templates/default/neutron.conf.erb | 443 + ...um.sysconfig.erb => neutron.sysconfig.erb} | 2 +- .../plugins/bigswitch/restproxy.ini.erb | 23 - .../default/plugins/brocade/brocade.ini.erb | 18 +- .../plugins/cisco/cisco_plugins.ini.erb | 10 - ....ini.erb => hyperv_neutron_plugin.ini.erb} | 27 +- .../linuxbridge/linuxbridge_conf.ini.erb | 76 +- .../plugins/metaplugin/metaplugin.ini.erb | 27 +- .../default/plugins/midonet/midonet.ini.erb | 23 - .../default/plugins/ml2/ml2_conf.ini.erb | 65 + .../templates/default/plugins/nec/nec.ini.erb | 29 +- .../default/plugins/nicira/nvp.ini.erb | 37 +- .../plugins/openvswitch/openvswitch.erb | 104 - ...gin.ini.erb => ovs_neutron_plugin.ini.erb} | 57 +- .../default/plugins/plumgrid/plumgrid.ini.erb | 23 - .../templates/default/plugins/ryu/ryu.ini.erb | 16 +- .../templates/default/policy.json.erb | 75 - .../default/quantum-server.start.erb | 95 - .../templates/default/quantum.conf.erb | 332 - .../templates/default/rootwrap.conf.erb | 2 +- .../openstack-object-storage/.rubocop.yml | 24 + .../openstack-object-storage/Berksfile | 4 +- .../openstack-object-storage/Berksfile.lock | 42 - .../openstack-object-storage/CHANGELOG.md | 76 + .../openstack-object-storage/Gemfile | 16 +- .../openstack-object-storage/Gemfile.lock | 202 +- .../openstack-object-storage/README.md | 77 +- .../openstack-object-storage/Strainerfile | 3 +- .../openstack-object-storage/TESTING.md | 42 + .../attributes/default.rb | 370 +- .../files/default/git-daemon.default | 16 + .../default/swift-container-sync.conf.upstart | 19 + .../libraries/drive_utils.rb | 11 +- .../libraries/ip_utils.rb | 25 +- .../openstack-object-storage/metadata.rb | 29 +- .../providers/disk.rb | 143 +- .../providers/mounts.rb | 105 +- .../providers/ring_script.rb | 122 +- .../recipes/account-server.rb | 93 +- .../recipes/client.rb | 14 +- .../recipes/common.rb | 121 +- .../recipes/container-server.rb | 120 +- .../openstack-object-storage/recipes/disks.rb | 34 +- .../recipes/management-server.rb | 77 +- .../recipes/memcached.rb | 5 +- .../recipes/object-server.rb | 99 +- .../recipes/proxy-server.rb | 142 +- .../recipes/ring-repo.rb | 150 +- .../openstack-object-storage/recipes/rsync.rb | 80 +- .../openstack-object-storage/recipes/setup.rb | 59 +- .../recipes/storage-common.rb | 29 +- .../recipes/swift-config-ceph.rb | 100 + .../recipes/swiftclient-patch.rb} | 22 +- .../resources/disk.rb | 35 +- .../resources/mounts.rb | 76 +- .../resources/ring_script.rb | 34 +- .../spec/account_spec.rb | 83 +- .../spec/client_spec.rb | 16 + .../spec/common_spec.rb | 139 +- .../spec/container_spec.rb | 114 +- .../spec/disks_spec.rb | 50 +- .../spec/management_spec.rb | 71 +- .../spec/object_spec.rb | 87 +- .../spec/proxy_spec.rb | 133 +- .../spec/ring-repo_spec.rb | 48 +- .../spec/rsync_spec.rb | 55 +- .../spec/setup_spec.rb | 17 + .../spec/spec_helper.rb | 102 +- .../spec/storage-common_spec.rb | 70 +- .../templates/default/account-server.conf.erb | 6 +- .../default/container-server.conf.erb | 26 +- .../templates/default/object-server.conf.erb | 6 +- .../templates/default/proxy-server.conf.erb | 110 +- .../default/swift-statsd-publish.py.erb | 157 + .../openstack-ops-database/.rubocop.yml | 24 + .../openstack-ops-database/Berksfile | 4 + .../openstack-ops-database/CHANGELOG.md | 21 +- chef/cookbooks/openstack-ops-database/Gemfile | 9 + .../openstack-ops-database/Gemfile.lock | 237 + .../openstack-ops-database/README.md | 60 +- .../Strainerfile | 2 +- .../openstack-ops-database/TESTING.md | 42 + .../attributes/default.rb | 39 +- .../openstack-ops-database/metadata.rb | 29 +- .../openstack-ops-database/recipes/client.rb | 4 +- .../recipes/mysql-client.rb | 10 +- .../recipes/mysql-server.rb | 98 +- .../recipes/openstack-db.rb | 50 +- .../recipes/postgresql-client.rb | 7 +- .../recipes/postgresql-server.rb | 11 +- .../openstack-ops-database/recipes/server.rb | 4 +- .../spec/client_spec.rb | 23 + .../spec/mysql-client-suse_spec.rb | 15 + .../spec/mysql-client_spec.rb | 21 + .../spec/mysql-server-redhat_spec.rb | 24 + .../spec/mysql-server_spec.rb | 84 + .../spec/openstack-db_spec.rb | 69 + .../spec/postgresql-server_spec.rb | 26 + .../spec/server_spec.rb | 34 + .../spec/spec_helper.rb | 35 + .../openstack-ops-messaging/.rubocop.yml | 24 + .../cookbooks/openstack-ops-messaging/.tailor | 25 - .../openstack-ops-messaging/Berksfile.lock | 46 - .../openstack-ops-messaging/CHANGELOG.md | 28 + .../cookbooks/openstack-ops-messaging/Gemfile | 17 +- .../openstack-ops-messaging/Gemfile.lock | 230 +- .../openstack-ops-messaging/README.md | 34 +- .../openstack-ops-messaging/Strainerfile | 4 +- .../openstack-ops-messaging/TESTING.md | 33 + .../attributes/default.rb | 9 +- .../openstack-ops-messaging/metadata.rb | 23 +- .../recipes/rabbitmq-server.rb | 70 +- .../recipes/rabbitmq.rb | 58 - .../openstack-ops-messaging/recipes/server.rb | 7 +- .../spec/rabbitmq-server-redhat_spec.rb | 17 + .../spec/rabbitmq-server_spec.rb | 208 +- .../spec/server_spec.rb | 16 +- .../spec/spec_helper.rb | 54 +- .../openstack-orchestration/.rubocop.yml | 24 + .../Berksfile | 4 +- .../openstack-orchestration/CHANGELOG.md | 36 + .../cookbooks/openstack-orchestration/Gemfile | 10 + .../openstack-orchestration/Gemfile.lock | 237 + .../openstack-orchestration/README.md | 154 + .../openstack-orchestration/Strainerfile | 5 + .../openstack-orchestration/TESTING.md | 42 + .../attributes/default.rb | 118 + .../openstack-orchestration/metadata.rb | 21 + .../recipes/api-cfn.rb | 47 + .../recipes/api-cloudwatch.rb | 51 + .../openstack-orchestration/recipes/api.rb | 47 + .../openstack-orchestration/recipes/client.rb | 32 + .../openstack-orchestration/recipes/common.rb | 133 + .../recipes/default.rb | 7 +- .../openstack-orchestration/recipes/engine.rb | 38 + .../recipes/identity_registration.rb | 130 + .../spec/api-cfn-redhat_spec.rb | 28 + .../spec/api-cloudwatch-redhat_spec.rb | 28 + .../spec/api-redhat_spec.rb | 29 + .../spec/client-redhat_spec.rb | 18 + .../spec/client_spec.rb | 18 + .../spec/common-redhat_spec.rb | 126 + .../spec/common_spec.rb | 122 + .../spec/engine-redhat_spec.rb | 23 + .../spec/identity_registration_spec.rb | 124 + .../spec/spec_helper.rb | 88 + .../templates/default/api-paste.ini.erb | 96 + .../templates/default/default.yaml.erb | 10 + .../templates/default/heat.conf.erb | 1229 + .../openstack-telemetry/.rubocop.yml | 24 + chef/cookbooks/openstack-telemetry/Berksfile | 12 + .../openstack-telemetry/CHANGELOG.md | 59 + chef/cookbooks/openstack-telemetry/Gemfile | 10 + .../openstack-telemetry/Gemfile.lock | 238 + chef/cookbooks/openstack-telemetry/README.md | 119 + .../openstack-telemetry/Strainerfile | 5 + chef/cookbooks/openstack-telemetry/TESTING.md | 42 + .../openstack-telemetry/attributes/default.rb | 122 + .../cookbooks/openstack-telemetry/metadata.rb | 26 + .../recipes/agent-central.rb | 21 +- .../recipes/agent-compute.rb | 27 +- .../recipes/agent-notification.rb | 36 + .../recipes/alarm-evaluator.rb | 36 + .../recipes/alarm-notifier.rb} | 33 +- .../recipes/api.rb | 27 +- .../recipes/client.rb} | 20 +- .../openstack-telemetry/recipes/collector.rb | 56 + .../openstack-telemetry/recipes/common.rb | 104 + .../recipes/identity_registration.rb | 88 + .../spec/agent-central-rhel_spec.rb | 22 + .../spec/agent-central-suse_spec.rb | 21 + .../spec/agent-central_spec.rb | 29 + .../spec/agent-compute-rhel_spec.rb | 22 + .../spec/agent-compute-suse_spec.rb | 21 + .../spec/agent-compute_spec.rb | 29 + .../spec/agent-notification-rhel_spec.rb | 22 + .../spec/agent-notification-suse_spec.rb | 21 + .../spec/agent-notification_spec.rb | 29 + .../spec/alarm-evaluator-rhel_spec.rb | 22 + .../spec/alarm-evaluator-suse_spec.rb | 21 + .../spec/alarm-evaluator_spec.rb | 29 + .../spec/alarm-notifier-rhel_spec.rb | 22 + .../spec/alarm-notifier-suse_spec.rb | 21 + .../spec/alarm-notifier_spec.rb | 29 + .../openstack-telemetry/spec/api-rhel_spec.rb | 30 + .../openstack-telemetry/spec/api-suse_spec.rb | 21 + .../openstack-telemetry/spec/api_spec.rb | 37 + .../spec/client-redhat_spec.rb | 15 + .../openstack-telemetry/spec/client_spec.rb | 16 + .../spec/collector-rhel_spec.rb | 27 + .../spec/collector-suse_spec.rb | 21 + .../spec/collector_spec.rb | 44 + .../spec/common-rhel_spec.rb | 33 + .../spec/common-suse_spec.rb | 26 + .../openstack-telemetry/spec/common_spec.rb | 181 + .../spec/identity_registration_spec.rb | 83 + .../openstack-telemetry/spec/spec_helper.rb | 52 + .../templates/default/ceilometer.conf.erb | 76 + chef/cookbooks/pacman/.gitignore | 1 + chef/cookbooks/pacman/.travis.yml | 8 + chef/cookbooks/pacman/CHANGELOG.md | 20 + chef/cookbooks/{apt => pacman}/CONTRIBUTING | 0 chef/cookbooks/pacman/Gemfile | 11 + chef/cookbooks/pacman/Gemfile.lock | 113 + chef/cookbooks/{aws => pacman}/LICENSE | 0 chef/cookbooks/pacman/README.md | 68 + chef/cookbooks/pacman/metadata.rb | 7 + chef/cookbooks/pacman/providers/aur.rb | 138 + chef/cookbooks/pacman/providers/group.rb | 50 + .../recipes/default.rb | 10 +- .../resources/aur.rb} | 23 +- .../instance.rb => pacman/resources/group.rb} | 18 +- chef/cookbooks/pacman/spec/spec_helper.rb | 0 chef/cookbooks/postgresql/.kitchen.yml | 144 - chef/cookbooks/postgresql/Berksfile | 7 - chef/cookbooks/postgresql/CHANGELOG.md | 39 + chef/cookbooks/postgresql/CONTRIBUTING.md | 257 - chef/cookbooks/postgresql/LICENSE | 201 - chef/cookbooks/postgresql/README.md | 24 +- chef/cookbooks/postgresql/TESTING.md | 25 - .../postgresql/attributes/default.rb | 103 +- .../minitest/apt_pgdg_postgresql_test.rb | 8 +- .../default/tests/minitest/server_test.rb | 2 +- chef/cookbooks/postgresql/metadata.json | 47 + chef/cookbooks/postgresql/metadata.rb | 2 +- .../postgresql/recipes/apt_pgdg_postgresql.rb | 2 +- chef/cookbooks/postgresql/recipes/client.rb | 4 + chef/cookbooks/postgresql/recipes/ruby.rb | 17 +- chef/cookbooks/postgresql/recipes/server.rb | 6 +- .../postgresql/recipes/server_redhat.rb | 2 +- .../templates/default/pg_hba.conf.erb | 14 +- chef/cookbooks/python/.gitignore | 2 - chef/cookbooks/python/.travis.yml | 10 + chef/cookbooks/python/Berksfile.lock | 28 + chef/cookbooks/python/CHANGELOG.md | 26 + chef/cookbooks/python/CONTRIBUTING | 29 - chef/cookbooks/python/Gemfile | 16 +- chef/cookbooks/python/Gemfile.lock | 215 + chef/cookbooks/python/NOTICE.txt | 2 + chef/cookbooks/python/README.md | 11 +- chef/cookbooks/python/Rakefile | 5 + chef/cookbooks/python/ZPL.txt | 59 + chef/cookbooks/python/attributes/default.rb | 9 +- .../cookbooks/python/files/default/get-pip.py | 21531 ++++++++++++++++ chef/cookbooks/python/libraries/matchers.rb | 25 + chef/cookbooks/python/metadata.json | 43 + chef/cookbooks/python/metadata.rb | 8 +- chef/cookbooks/python/providers/pip.rb | 11 +- chef/cookbooks/python/providers/virtualenv.rb | 9 +- chef/cookbooks/python/recipes/package.rb | 7 +- chef/cookbooks/python/recipes/pip.rb | 25 +- chef/cookbooks/python/recipes/source.rb | 18 +- chef/cookbooks/python/recipes/virtualenv.rb | 3 +- chef/cookbooks/python/resources/pip.rb | 1 + .../calculating_with_dictionaries/example.py | 25 - .../example.py | 25 - .../example.py | 23 - .../src/1/filtering_list_elements/example.py | 43 - .../example.py | 20 - .../example.py | 20 - .../grouping.py | 33 - .../implementing_a_priority_queue/example.py | 35 - .../src/1/keeping_the_last_n_items/example.py | 17 - .../1/keeping_the_last_n_items/somefile.txt | 86 - .../example1.py | 22 - .../example.py | 15 - .../example2.py | 23 - .../example.py | 27 - .../example.py | 14 - .../example.py | 24 - .../example.py | 21 - .../example.py | 51 - .../explicit_load.py | 23 - .../metaexample.py | 16 - .../pathexample.py | 18 - .../testcode/fib.py | 7 - .../testcode/grok/__init__.py | 1 - .../testcode/grok/blah.py | 1 - .../testcode/spam.py | 4 - .../urlimport.py | 227 - .../bar-package/spam/grok.py | 1 - .../example.py | 4 - .../foo-package/spam/blah.py | 1 - .../example1.py | 8 - .../example2.py | 22 - .../postimport.py | 40 - .../example.py | 6 - .../mymodule/__init__.py | 5 - .../mymodule/a.py | 6 - .../mymodule/b.py | 8 - .../echoclient.py | 25 - .../adding_ssl_to_network_servers/echoserv.py | 38 - .../makecerts.sh | 4 - .../ssl_xmlrpc_client.py | 35 - .../ssl_xmlrpc_server.py | 49 - .../adding_ssl_to_network_servers/sslmixin.py | 26 - .../client1.py | 7 - .../example1.py | 47 - .../resty.py | 24 - .../11/creating_a_tcp_server/echoclient.py | 9 - .../src/11/creating_a_tcp_server/echoserv.py | 15 - .../src/11/creating_a_tcp_server/echoserv1.py | 15 - .../src/11/creating_a_tcp_server/echoserv2.py | 14 - .../src/11/creating_a_tcp_server/echoserv3.py | 21 - .../src/11/creating_a_tcp_server/echoserv4.py | 22 - .../src/11/creating_a_tcp_server/echoserv5.py | 23 - .../11/creating_a_tcp_server/threadedserv.py | 20 - .../src/11/creating_a_udp_server/client.py | 5 - .../src/11/creating_a_udp_server/timeserv1.py | 14 - .../src/11/creating_a_udp_server/timeserv2.py | 14 - .../event_driven_io_explained/eventhandler.py | 32 - .../11/event_driven_io_explained/tcpclient.py | 7 - .../11/event_driven_io_explained/tcpserver.py | 61 - .../event_driven_io_explained/threadpool.py | 65 - .../thrpoolclient.py | 6 - .../11/event_driven_io_explained/udpclient.py | 7 - .../11/event_driven_io_explained/udpserver.py | 29 - .../jsonrpclient.py | 22 - .../jsonrpcserver.py | 50 - .../rpcclient.py | 24 - .../rpcserver.py | 50 - .../example1.py | 26 - .../example2.py | 26 - .../example3.py | 25 - .../example4.py | 15 - .../client1.py | 9 - .../server.py | 38 - .../server1.py | 42 - .../servermp.py | 29 - .../worker.py | 37 - .../workermp.py | 28 - .../simple_authentication_of_clients/auth.py | 26 - .../client.py | 11 - .../server.py | 26 - .../echoclient.py | 9 - .../echoserv.py | 21 - .../client.py | 9 - .../keyserv.py | 32 - .../client.py | 12 - .../server.py | 13 - .../zerocopy.py | 13 - .../src/12/defining_an_actor_task/actor.py | 75 - .../src/12/defining_an_actor_task/tagged.py | 24 - .../src/12/defining_an_actor_task/worker.py | 36 - .../example1.py | 43 - .../example2.py | 49 - .../how_to_create_a_thread_pool/example1.py | 27 - .../how_to_create_a_thread_pool/example2.py | 36 - .../how_to_create_a_thread_pool/example3.py | 16 - .../example1.py | 23 - .../example2.py | 53 - .../example3.py | 27 - .../how_to_lock_critical_sections/example1.py | 45 - .../how_to_start_and_stop_threads/example.py | 26 - .../exchange1.py | 46 - .../exchange2.py | 55 - .../daemon.py | 94 - .../deadlock.py | 28 - .../example1.py | 29 - .../example2.py | 34 - .../example3.py | 26 - .../polling_multiple_thread_queues/pqueue.py | 63 - .../simple_parallel_programming/findrobots.py | 37 - .../findrobots_par.py | 38 - .../logs/20121217.log.gz | Bin 139539 -> 0 bytes .../logs/20121218.log.gz | Bin 144283 -> 0 bytes .../logs/20121219.log.gz | Bin 138211 -> 0 bytes .../logs/20121220.log.gz | Bin 148719 -> 0 bytes .../logs/20121221.log.gz | Bin 137687 -> 0 bytes .../logs/20121222.log.gz | Bin 113107 -> 0 bytes .../logs/20121223.log.gz | Bin 115180 -> 0 bytes .../logs/20121224.log.gz | Bin 119009 -> 0 bytes .../logs/20121225.log.gz | Bin 121461 -> 0 bytes .../logs/20121226.log.gz | Bin 126602 -> 0 bytes .../logs/20121227.log.gz | Bin 131302 -> 0 bytes .../logs/20121228.log.gz | Bin 128106 -> 0 bytes .../logs/20121229.log.gz | Bin 119375 -> 0 bytes .../logs/20121230.log.gz | Bin 121378 -> 0 bytes .../storing_thread_specific_state/example1.py | 45 - .../storing_thread_specific_state/example2.py | 60 - .../actorsched.py | 59 - .../netsched.py | 159 - .../simple.py | 49 - .../13/adding_logging_to_libraries/somelib.py | 10 - .../example1.py | 9 - .../example2.py | 20 - .../src/13/finding_files/modified_within.py | 27 - .../example.py | 26 - .../13/getting_the_terminal_size/example.py | 4 - .../src/13/making_a_stopwatch/stopwatch.py | 50 - .../13/parsing_command_line_options/search.py | 32 - .../example.py | 7 - .../example.py | 18 - .../13/reading_configuration_files/config.ini | 24 - .../reading_configuration_files/example1.py | 9 - .../13/simple_logging_for_scripts/example1.py | 24 - .../13/simple_logging_for_scripts/example2.py | 22 - .../simple_logging_for_scripts/logconfig.ini | 21 - .../14/logging_test_output_to_a_file/test.py | 36 - .../make_your_programs_run_faster/example.py | 35 - .../timethis.py | 23 - .../example.py | 50 - .../test.py | 26 - .../test.py | 29 - .../testing_output_sent_to_stdout/mymodule.py | 5 - .../testmymodule.py | 19 - chef/cookbooks/python/src/15/Makefile | 2 - .../accessing_c_code_using_ctypes/example.py | 9 - .../accessing_c_code_using_ctypes/sample.py | 77 - .../src/15/calling_python_from_c/Makefile | 3 - .../src/15/calling_python_from_c/embed.c | 84 - .../consuming_an_iterable_from_c/example.py | 10 - .../15/consuming_an_iterable_from_c/sample.c | 43 - .../15/consuming_an_iterable_from_c/setup.py | 10 - .../README.txt | 4 - .../example.py | 4 - .../ptexample.c | 50 - .../ptsetup.py | 11 - .../pysample.c | 87 - .../pysample.h | 31 - .../setup.py | 11 - .../diagnosing_segmentation_faults/example.py | 20 - .../diagnosing_segmentation_faults/sample.c | 31 - .../diagnosing_segmentation_faults/setup.py | 10 - .../example.py | 9 - .../pysample.c | 70 - .../setup.py | 11 - .../example.py | 15 - .../sample.c | 68 - .../setup.py | 10 - .../example.py | 4 - .../sample.c | 65 - .../setup.py | 10 - .../example.py | 6 - .../reading_file_like_objects_from_c/sample.c | 84 - .../reading_file_like_objects_from_c/setup.py | 10 - chef/cookbooks/python/src/15/sample.c | 53 - chef/cookbooks/python/src/15/sample.h | 12 - .../example.py | 16 - .../example.py | 34 - .../sample.pyx | 46 - .../setup.py | 15 - .../example.py | 4 - .../sample.c | 64 - .../setup.py | 10 - .../15/wrapping_c_code_with_swig/example.py | 12 - .../src/15/wrapping_c_code_with_swig/sample.i | 53 - .../src/15/wrapping_c_code_with_swig/setup.py | 16 - .../csample.pxd | 15 - .../example.py | 12 - .../sample.pyx | 50 - .../sample_alt.pyx | 54 - .../setup.py | 15 - .../setup_alt.py | 15 - .../example.py | 5 - .../pysample.c | 59 - .../setup.py | 11 - .../example.py | 10 - .../pysample.c | 59 - .../setup.py | 11 - .../example.py | 38 - .../example.py | 23 - .../example.py | 19 - .../example.py | 28 - .../example.py | 23 - .../example.py | 31 - .../2/searching_and_replacing_text/example.py | 22 - .../example.py | 19 - .../example.py | 29 - .../python/src/2/tokenizing_text/example.py | 26 - .../example.py | 30 - .../example.py | 12 - .../example.py | 158 - .../plyexample.py | 90 - .../determining_last_fridays_date/example.py | 15 - .../example.py | 27 - .../example.py | 63 - .../www/bar/access-log | 7298 ------ .../www/bar/access-log-0108.bz2 | Bin 46105 -> 0 bytes .../www/bar/access-log-0208.bz2 | Bin 46105 -> 0 bytes .../www/foo/access-log | 7298 ------ .../www/foo/access-log-0108.gz | Bin 72261 -> 0 bytes .../www/foo/access-log-0208.gz | Bin 72261 -> 0 bytes .../example.py | 8 - .../src/4/delegating-iteration/example.py | 26 - .../example.py | 37 - .../hardexample.py | 66 - .../src/4/generators_with_state/example.py | 29 - .../src/4/generators_with_state/somefile.txt | 4 - .../example.py | 20 - .../example.py | 11 - .../sample.dat | 6 - .../src/4/iterating_in_reverse/example.py | 28 - .../example.py | 8 - .../example.py | 8 - .../example.py | 11 - .../5/getting_a_directory_listing/example.py | 19 - .../data.bin | 1 - .../example.py | 13 - .../reading_and_writing_text_data/example.py | 30 - .../reading_and_writing_text_data/sample.txt | 2 - .../echo.py | 26 - .../5/writing_bytes_to_a_text_file/example.py | 9 - .../example.py | 42 - .../potholes.xml | 1 - .../example.py | 21 - .../pred.xml | 23 - .../src/6/parsing_simple_xml_data/example.py | 17 - .../example.py | 26 - .../sample.xml | 14 - .../readrecords.py | 14 - .../unpackrecords.py | 15 - .../writerecords.py | 18 - .../6/reading_and_writing_csv_data/example.py | 69 - .../6/reading_and_writing_csv_data/stocks.csv | 7 - .../6/reading_and_writing_csv_data/stocks.tsv | 7 - .../reading_and_writing_json_data/example.py | 61 - .../example1.py | 38 - .../example2.py | 60 - .../example3.py | 89 - .../example4.py | 117 - .../writepolys.py | 32 - .../example1.py | 28 - .../example2.py | 40 - .../example3.py | 27 - .../example.py | 93 - .../example.py | 23 - .../example.py | 21 - .../example.py | 42 - .../7/inlining_callback_functions/example.py | 61 - .../example1.py | 13 - .../example2.py | 22 - .../example3.py | 18 - .../example1.py | 12 - .../example2.py | 12 - .../example3.py | 31 - .../example4.py | 26 - .../example5.py | 25 - .../example.py | 40 - .../example.py | 9 - .../example1.py | 34 - .../example.py | 35 - .../test/cookbooks/python_test/README.md | 15 - .../default/tests/minitest/cook-3084_test.rb | 17 - .../test/cookbooks/python_test/metadata.rb | 7 - .../python_test/recipes/test_exert.rb | 35 - .../python_test/recipes/test_virtualenv.rb | 35 - .../test/integration/exert/bats/exert.bats | 13 - .../test/integration/source/bats/source.bats | 9 - .../virtualenv/bats/virtualenv.bats | 17 - chef/cookbooks/rabbitmq/.kitchen.yml | 192 - chef/cookbooks/rabbitmq/Berksfile | 10 - chef/cookbooks/rabbitmq/CHANGELOG.md | 39 + chef/cookbooks/rabbitmq/CONTRIBUTING | 29 - chef/cookbooks/rabbitmq/LICENSE | 201 - chef/cookbooks/rabbitmq/README.md | 1 + chef/cookbooks/rabbitmq/TESTING.md | 39 - chef/cookbooks/rabbitmq/attributes/default.rb | 50 +- chef/cookbooks/rabbitmq/libraries/default.rb | 42 + chef/cookbooks/rabbitmq/libraries/matchers.rb | 57 + chef/cookbooks/rabbitmq/metadata.json | 155 + chef/cookbooks/rabbitmq/metadata.rb | 164 +- chef/cookbooks/rabbitmq/providers/plugin.rb | 8 +- chef/cookbooks/rabbitmq/providers/policy.rb | 32 +- chef/cookbooks/rabbitmq/providers/user.rb | 110 +- chef/cookbooks/rabbitmq/providers/vhost.rb | 18 +- chef/cookbooks/rabbitmq/recipes/default.rb | 53 +- .../rabbitmq/recipes/mgmt_console.rb | 2 +- .../rabbitmq/recipes/plugin_management.rb | 2 +- .../rabbitmq/recipes/policy_management.rb | 2 +- .../rabbitmq/recipes/user_management.rb | 4 +- .../recipes/virtualhost_management.rb | 3 +- .../templates/default/rabbitmq.config.erb | 13 +- .../default/rabbitmq.upstart.conf.erb | 16 +- .../test/cookbooks/rabbitmq_test/README.md | 1 - .../default/tests/minitest/cook-1684_test.rb | 41 - .../default/tests/minitest/cook-1724_test.rb | 31 - .../tests/minitest/cook-2151-3489_test.rb | 37 - .../default/tests/minitest/default_test.rb | 61 - .../default/tests/minitest/lwrps_test.rb | 61 - .../tests/minitest/mgmt_console_test.rb | 30 - .../files/default/tests/minitest/ssl_test.rb | 18 - .../default/tests/minitest/support/helpers.rb | 45 - .../test/cookbooks/rabbitmq_test/metadata.rb | 7 - .../rabbitmq_test/recipes/cluster.rb | 21 - .../rabbitmq_test/recipes/cook-2151-3489.rb | 14 - .../rabbitmq_test/recipes/default.rb | 29 - .../cookbooks/rabbitmq_test/recipes/lwrps.rb | 38 - .../rabbitmq_test/recipes/mgmt_console.rb | 29 - .../cookbooks/rabbitmq_test/recipes/ssl.rb | 18 - chef/cookbooks/rsyslog/recipes/client.rb | 2 +- chef/cookbooks/runit/.kitchen.yml | 56 - chef/cookbooks/runit/Berksfile | 12 - chef/cookbooks/runit/CHANGELOG.md | 54 + chef/cookbooks/runit/CONTRIBUTING.md | 257 - chef/cookbooks/runit/Gemfile | 8 - chef/cookbooks/runit/LICENSE | 201 - chef/cookbooks/runit/README.md | 19 + chef/cookbooks/runit/Rakefile | 17 - chef/cookbooks/runit/TESTING.md | 26 - chef/cookbooks/runit/attributes/default.rb | 86 +- .../runit/libraries/provider_runit_service.rb | 128 +- .../runit/libraries/resource_runit_service.rb | 93 +- chef/cookbooks/runit/metadata.json | 42 + chef/cookbooks/runit/metadata.rb | 20 +- chef/cookbooks/runit/recipes/default.rb | 103 +- .../test/cookbooks/runit-other_test/README.md | 1 - .../cookbooks/runit-other_test/metadata.rb | 6 - .../runit-other_test/recipes/default.rb | 1 - .../sv-other-cookbook-templates-log-run.erb | 2 - .../sv-other-cookbook-templates-run.erb | 3 - .../runit/test/cookbooks/runit_test/README.md | 1 - .../default/tests/minitest/default_test.rb | 30 - .../default/tests/minitest/service_test.rb | 118 - .../default/tests/minitest/support/helpers.rb | 29 - .../test/cookbooks/runit_test/metadata.rb | 6 - .../cookbooks/runit_test/recipes/default.rb | 20 - .../cookbooks/runit_test/recipes/service.rb | 153 - .../templates/default/sv-calabash-run.erb | 3 - .../default/sv-control-signals-log-run.erb | 2 - .../default/sv-control-signals-run.erb | 3 - .../default/sv-control-signals-u.erb | 2 - .../templates/default/sv-cook-2867-run.erb | 4 - .../default/sv-default-svlog-run.erb | 3 - .../default/sv-downed-service-log-run.erb | 2 - .../default/sv-downed-service-run.erb | 3 - .../default/sv-env-files-log-run.erb | 2 - .../templates/default/sv-env-files-run.erb | 3 - .../default/sv-exist-disabled-log-run.erb | 2 - .../default/sv-exist-disabled-run.erb | 3 - .../templates/default/sv-finisher-finish.erb | 9 - .../templates/default/sv-finisher-log-run.erb | 2 - .../templates/default/sv-finisher-run.erb | 3 - .../default/sv-floyds-app-log-run.erb | 2 - .../templates/default/sv-floyds-app-run.erb | 3 - .../templates/default/sv-no-svlog-run.erb | 3 - .../default/sv-plain-defaults-log-run.erb | 2 - .../default/sv-plain-defaults-run.erb | 3 - .../default/sv-runsvdir-floyd-log-run.erb | 2 - .../default/sv-runsvdir-floyd-run.erb | 3 - .../default/sv-template-options-log-run.erb | 2 - .../default/sv-template-options-run.erb | 4 - .../default/sv-yerba-matte-finish.erb | 9 - .../default/sv-yerba-matte-log-run.erb | 2 - .../templates/default/sv-yerba-run.erb | 3 - .../libraries/provider_runit_service_spec.rb | 523 - .../libraries/resource_runit_service_spec.rb | 284 - chef/cookbooks/runit/test/spec/spec_helper.rb | 28 - chef/cookbooks/selinux/CHANGELOG.md | 44 +- chef/cookbooks/selinux/CONTRIBUTING | 29 - chef/cookbooks/selinux/LICENSE | 201 - chef/cookbooks/selinux/README.md | 111 +- chef/cookbooks/selinux/attributes/default.rb | 2 + .../libraries/selinux_service_helpers.rb | 13 + chef/cookbooks/selinux/metadata.json | 52 + chef/cookbooks/selinux/metadata.rb | 23 +- chef/cookbooks/selinux/providers/state.rb | 75 + chef/cookbooks/selinux/recipes/_common.rb | 7 + chef/cookbooks/selinux/recipes/default.rb | 16 + chef/cookbooks/selinux/recipes/disabled.rb | 16 +- chef/cookbooks/selinux/recipes/enforcing.rb | 14 +- chef/cookbooks/selinux/recipes/permissive.rb | 16 +- .../yum.rb => selinux/resources/state.rb} | 13 +- chef/cookbooks/statsd/.foodcritic-rules | 7 + chef/cookbooks/statsd/Berksfile | 1 + chef/cookbooks/statsd/Berksfile.lock | 31 + chef/cookbooks/statsd/Gemfile | 13 + .../Gemfile.lock | 149 +- chef/cookbooks/statsd/README.md | 51 + chef/cookbooks/statsd/README.rdoc | 26 - chef/cookbooks/statsd/attributes/default.rb | 9 + chef/cookbooks/statsd/attributes/statsd.rb | 3 - chef/cookbooks/statsd/files/default/statsd | 3 - .../statsd/files/default/upstart.conf | 6 +- .../statsd/files/default/upstart.start | 3 + chef/cookbooks/statsd/metadata.rb | 24 +- chef/cookbooks/statsd/recipes/default.rb | 47 +- chef/cookbooks/statsd/recipes/server.rb | 87 + .../statsd/templates/default/changelog.erb | 5 + .../statsd/templates/default/config.js.erb | 5 - .../templates/default/localConfig.js.erb | 17 + chef/cookbooks/windows/CHANGELOG.md | 176 +- chef/cookbooks/windows/CONTRIBUTING | 29 - chef/cookbooks/windows/LICENSE | 201 - chef/cookbooks/windows/README.md | 748 +- .../windows/libraries/feature_base.rb | 18 + chef/cookbooks/windows/libraries/matchers.rb | 451 + .../windows/libraries/powershell_helper.rb | 59 + .../windows/libraries/powershell_out.rb | 79 + chef/cookbooks/windows/libraries/version.rb | 1 - .../libraries/windows_architecture_helper.rb | 86 + .../{helper.rb => windows_helper.rb} | 8 +- chef/cookbooks/windows/metadata.json | 31 + chef/cookbooks/windows/metadata.rb | 16 +- .../windows/providers/feature_dism.rb | 21 +- .../windows/providers/feature_powershell.rb | 38 + .../providers/feature_servermanagercmd.rb | 2 +- chef/cookbooks/windows/providers/printer.rb | 4 +- .../windows/providers/printer_port.rb | 4 +- chef/cookbooks/windows/providers/task.rb | 13 +- chef/cookbooks/windows/providers/zipfile.rb | 8 +- chef/cookbooks/windows/resources/feature.rb | 10 +- chef/cookbooks/windows/resources/registry.rb | 1 + chef/cookbooks/windows/resources/task.rb | 2 + chef/cookbooks/xfs/.kitchen.yml | 34 - chef/cookbooks/xfs/TESTING.md | 25 - chef/cookbooks/xfs/metadata.json | 37 + chef/cookbooks/yum-epel/CHANGELOG.md | 37 + chef/cookbooks/yum-epel/README.md | 158 + .../yum-epel/attributes/epel-debuginfo.rb | 24 + .../yum-epel/attributes/epel-source.rb | 24 + .../attributes/epel-testing-debuginfo.rb | 24 + .../attributes/epel-testing-source.rb | 24 + .../yum-epel/attributes/epel-testing.rb | 24 + chef/cookbooks/yum-epel/attributes/epel.rb | 24 + chef/cookbooks/yum-epel/metadata.json | 30 + chef/cookbooks/yum-epel/metadata.rb | 9 + chef/cookbooks/yum-epel/recipes/default.rb | 56 + .../yum-epel/templates/default/epel.repo.erb | 26 + .../yum-erlang_solutions/CHANGELOG.md | 17 + chef/cookbooks/yum-erlang_solutions/README.md | 94 + .../attributes/erlang_solutions.rb | 10 + .../yum-erlang_solutions/metadata.json | 30 + .../yum-erlang_solutions/metadata.rb | 9 + .../yum-erlang_solutions/recipes/default.rb | 48 + chef/cookbooks/yum/.kitchen.yml | 22 - chef/cookbooks/yum/Berksfile | 7 - chef/cookbooks/yum/CHANGELOG.md | 84 +- chef/cookbooks/yum/CONTRIBUTING.md | 257 - chef/cookbooks/yum/LICENSE | 201 - chef/cookbooks/yum/README.md | 402 +- chef/cookbooks/yum/attributes/default.rb | 30 - chef/cookbooks/yum/attributes/elrepo.rb | 24 - chef/cookbooks/yum/attributes/epel.rb | 39 - chef/cookbooks/yum/attributes/remi.rb | 30 - .../yum/files/default/RPM-GPG-KEY-EPEL-6 | 29 - .../default/tests/minitest/support/helpers.rb | 37 - .../files/default/tests/minitest/test_test.rb | 66 - chef/cookbooks/yum/metadata.json | 4 +- chef/cookbooks/yum/metadata.rb | 45 +- chef/cookbooks/yum/providers/key.rb | 83 - chef/cookbooks/yum/providers/repository.rb | 142 +- chef/cookbooks/yum/recipes/default.rb | 23 +- chef/cookbooks/yum/recipes/elrepo.rb | 31 - chef/cookbooks/yum/recipes/epel.rb | 35 - chef/cookbooks/yum/recipes/ius.rb | 42 - chef/cookbooks/yum/recipes/remi.rb | 35 - chef/cookbooks/yum/recipes/repoforge.rb | 41 - chef/cookbooks/yum/recipes/test.rb | 39 - chef/cookbooks/yum/resources/globalconfig.rb | 2 +- chef/cookbooks/yum/resources/repository.rb | 63 +- .../templates/default/CentOS-Base.repo.erb | 57 + chef/cookbooks/yum/templates/default/main.erb | 2 +- chef/cookbooks/yum/templates/default/repo.erb | 113 +- .../yum/templates/default/yum-rhel5.conf.erb | 33 - .../yum/templates/default/yum-rhel6.conf.erb | 36 - chef/roles/allinone-compute.json | 17 + chef/roles/allinone-compute.rb | 6 - chef/roles/ceph-mds.json | 8 + chef/roles/ceph-mon.json | 8 + chef/roles/ceph-os-mon.json | 9 + chef/roles/ceph-os-radosgw.json | 9 + chef/roles/ceph-osd.json | 8 + chef/roles/ceph-radosgw.json | 8 + chef/roles/ceph-tgt.json | 8 + chef/roles/compass-base.json | 15 + chef/roles/os-base.json | 19 + chef/roles/os-base.rb | 6 - chef/roles/os-block-storage-api.json | 23 + chef/roles/os-block-storage-api.rb | 23 - chef/roles/os-block-storage-controller.json | 18 + chef/roles/os-block-storage-scheduler.json | 23 + chef/roles/os-block-storage-scheduler.rb | 23 - chef/roles/os-block-storage-volume.json | 24 + chef/roles/os-block-storage-worker.rb | 23 - chef/roles/os-block-storage.json | 19 + chef/roles/os-block-storage.rb | 8 - .../os-ceph-block-storage-controller.json | 19 + chef/roles/os-ceph-compute-worker.json | 25 + chef/roles/os-ceph-controller.json | 23 + chef/roles/os-ceph-image.json | 19 + chef/roles/os-client.json | 24 + chef/roles/os-compute-api-ec2.json | 16 + chef/roles/os-compute-api-ec2.rb | 6 - chef/roles/os-compute-api-metadata.json | 23 + chef/roles/os-compute-api-metadata.rb | 15 - chef/roles/os-compute-api-os-compute.json | 16 + chef/roles/os-compute-api-os-compute.rb | 23 - chef/roles/os-compute-api.json | 25 + chef/roles/os-compute-api.rb | 7 - chef/roles/os-compute-cert.json | 23 + chef/roles/os-compute-cert.rb | 15 - chef/roles/os-compute-conductor.json | 16 + chef/roles/os-compute-controller.json | 22 + chef/roles/os-compute-controller.rb | 11 - chef/roles/os-compute-scheduler.json | 23 + chef/roles/os-compute-scheduler.rb | 25 - chef/roles/os-compute-setup.json | 17 + ...-compute-single-controller-no-network.json | 28 + chef/roles/os-compute-single-controller.json | 29 + chef/roles/os-compute-single-controller.rb | 16 - chef/roles/os-compute-vncproxy.json | 23 + chef/roles/os-compute-vncproxy.rb | 16 - chef/roles/os-compute-worker.json | 24 + chef/roles/os-compute-worker.rb | 24 - chef/roles/os-controller.json | 21 + chef/roles/os-controller.rb | 17 - chef/roles/os-dashboard.json | 23 + chef/roles/os-dashboard.rb | 16 - chef/roles/os-ha.json | 25 + chef/roles/os-ha.rb | 15 - chef/roles/os-identity-api-admin.rb | 7 - chef/roles/os-identity-api.rb | 6 - chef/roles/os-identity.json | 27 + chef/roles/os-identity.rb | 24 - chef/roles/os-image-api.json | 25 + chef/roles/os-image-api.rb | 25 - chef/roles/os-image-registry.json | 25 + chef/roles/os-image-registry.rb | 25 - chef/roles/os-image-upload.json | 16 + chef/roles/os-image.json | 18 + chef/roles/os-image.rb | 6 - chef/roles/os-infra-caching.rb | 6 - chef/roles/os-network-dhcp-agent.json | 16 + chef/roles/os-network-l3-agent.json | 16 + chef/roles/os-network-metadata-agent.json | 16 + chef/roles/os-network-openvswitch.json | 16 + chef/roles/os-network-server.json | 26 + chef/roles/os-network-server.rb | 23 - chef/roles/os-network-worker.json | 19 + chef/roles/os-network.json | 33 + chef/roles/os-network.rb | 33 - chef/roles/os-object-storage-account.json | 16 + chef/roles/os-object-storage-account.rb | 6 - chef/roles/os-object-storage-container.json | 16 + chef/roles/os-object-storage-container.rb | 6 - chef/roles/os-object-storage-management.json | 16 + chef/roles/os-object-storage-management.rb | 6 - chef/roles/os-object-storage-object.json | 16 + chef/roles/os-object-storage-object.rb | 6 - chef/roles/os-object-storage-proxy.json | 16 + chef/roles/os-object-storage-proxy.rb | 6 - chef/roles/os-object-storage.json | 20 + chef/roles/os-object-storage.rb | 6 - chef/roles/os-ops-caching.json | 16 + chef/roles/os-ops-database.json | 26 + chef/roles/os-ops-database.rb | 24 - chef/roles/os-ops-messaging.json | 26 + chef/roles/os-ops-messaging.rb | 24 - chef/roles/os-orchestration-api-cfn.json | 16 + .../os-orchestration-api-cloudwatch.json | 16 + chef/roles/os-orchestration-api.json | 16 + chef/roles/os-orchestration-engine.json | 16 + chef/roles/os-orchestration.json | 19 + chef/roles/os-single-controller.rb | 18 - chef/roles/os-telemetry-agent-central.json | 17 + chef/roles/os-telemetry-agent-compute.json | 17 + .../os-telemetry-agent-notification.json | 17 + chef/roles/os-telemetry-alarm-evaluator.json | 17 + chef/roles/os-telemetry-alarm-notifier.json | 17 + chef/roles/os-telemetry-api.json | 17 + chef/roles/os-telemetry-collector.json | 17 + chef/roles/os-telemetry.json | 21 + chef/roles/test-synclog.rb | 6 - cobbler/conf/zone.template | 1 + cobbler/kickstarts/default.ks | 4 + cobbler/snippets/kickstart_chef | 10 +- cobbler/snippets/kickstart_chef-admin.pem | 8 + cobbler/snippets/kickstart_chef_firstrun.sh | 25 - cobbler/snippets/kickstart_chef_init | 6 +- cobbler/snippets/kickstart_chef_rerun.sh | 19 - cobbler/snippets/kickstart_chef_rsyslog.conf | 12 - cobbler/snippets/kickstart_chef_run.sh | 63 + cobbler/snippets/kickstart_client.rb | 8 +- cobbler/snippets/kickstart_hosts | 5 +- cobbler/snippets/kickstart_knife.rb | 13 + cobbler/snippets/kickstart_local_repo | 24 + .../kickstart_post_install_network_config | 608 +- .../snippets/kickstart_pre_partition_disks | 269 +- cobbler/snippets/kickstart_rsyslog.conf | 41 +- cobbler/snippets/preseed_client.rb | 5 +- cobbler/snippets/preseed_hosts | 8 +- .../preseed_post_install_network_config | 3 + cobbler/snippets/preseed_pre_partition_disks | 43 +- 1746 files changed, 71791 insertions(+), 55784 deletions(-) delete mode 100644 chef/cookbooks/apache2/Gemfile rename chef/cookbooks/{openstack-ops-messaging/recipes/default.rb => apache2/attributes/mod_fastcgi.rb} (78%) rename chef/cookbooks/{apt/test/cookbooks/apt_test/files/default/tests/minitest/support/helpers.rb => apache2/attributes/mod_pagespeed.rb} (60%) create mode 100644 chef/cookbooks/apache2/metadata.json rename chef/cookbooks/apache2/{test/kitchen/cookbooks/apache2_test/recipes/mod_cgi.rb => recipes/mod_cloudflare.rb} (59%) rename chef/cookbooks/{memcached/test/cookbooks/memcached_test/recipes/default.rb => apache2/recipes/mod_info.rb} (86%) rename chef/cookbooks/{apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-client_test.rb => apache2/recipes/mod_jk.rb} (67%) rename chef/cookbooks/apache2/{test/kitchen/cookbooks/apache2_test/recipes/mod_proxy_ajp.rb => recipes/mod_pagespeed.rb} (52%) rename chef/cookbooks/{apt/test/cookbooks/apt_test/recipes/cacher-client.rb => apache2/recipes/mod_userdir.rb} (86%) create mode 100644 chef/cookbooks/apache2/templates/default/mods/info.conf.erb create mode 100644 chef/cookbooks/apache2/templates/default/mods/pagespeed.conf.erb delete mode 100644 chef/cookbooks/apache2/test/features/alias_paths.feature delete mode 100644 chef/cookbooks/apache2/test/features/authenticate_basic.feature delete mode 100644 chef/cookbooks/apache2/test/features/authenticate_digest.feature delete mode 100644 chef/cookbooks/apache2/test/features/authenticate_openid.feature delete mode 100644 chef/cookbooks/apache2/test/features/authorize_groupfile.feature delete mode 100644 chef/cookbooks/apache2/test/features/authorize_host.feature delete mode 100644 chef/cookbooks/apache2/test/features/authorize_ldap.feature delete mode 100644 chef/cookbooks/apache2/test/features/authorize_users.feature delete mode 100644 chef/cookbooks/apache2/test/features/basic_web_app.feature delete mode 100644 chef/cookbooks/apache2/test/features/basic_webserver.feature delete mode 100644 chef/cookbooks/apache2/test/features/compress_server_response.feature delete mode 100644 chef/cookbooks/apache2/test/features/control_caching.feature delete mode 100644 chef/cookbooks/apache2/test/features/directory_listing.feature delete mode 100644 chef/cookbooks/apache2/test/features/host_cgi_scripts.feature delete mode 100644 chef/cookbooks/apache2/test/features/host_perl_applications.feature delete mode 100644 chef/cookbooks/apache2/test/features/host_php_applications.feature delete mode 100644 chef/cookbooks/apache2/test/features/host_python_applications.feature delete mode 100644 chef/cookbooks/apache2/test/features/host_source_control_repositories.feature delete mode 100644 chef/cookbooks/apache2/test/features/proxy_java_applications.feature delete mode 100644 chef/cookbooks/apache2/test/features/secure_requests.feature delete mode 100644 chef/cookbooks/apache2/test/features/step_definitions/svn_steps.rb delete mode 100644 chef/cookbooks/apache2/test/features/step_definitions/webserver_steps.rb delete mode 100644 chef/cookbooks/apache2/test/features/support/env.rb delete mode 100644 chef/cookbooks/apache2/test/features/support/svn_helpers.rb delete mode 100644 chef/cookbooks/apache2/test/features/support/web_helpers.rb delete mode 100644 chef/cookbooks/apache2/test/features/support_older_browsers.feature delete mode 100644 chef/cookbooks/apache2/test/kitchen/Kitchenfile delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/Cheffile delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/README.md delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/attributes/default.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/ssl/ldap.example.com.pem delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/modules_test.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/support/helpers.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/metadata.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/basic_web_app.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/god_monitor.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_basic.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_digest.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authnz_ldap.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_groupfile.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_unlisted_host.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_user.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_dav_svn.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_expires.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_perl.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_php5.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_python.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_ssl.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_status_remote.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/modules.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/setup.rb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_basic.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_digest.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_openid.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authnz_ldap.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_groupfile.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_host.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_user.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/cache_test.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/entries.ldif.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/java_env.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/perl_env.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/php_env.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/python_env.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/ssl.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/status.conf.erb delete mode 100644 chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/svn_repo.conf.erb delete mode 100644 chef/cookbooks/apt/.kitchen.yml delete mode 100644 chef/cookbooks/apt/Berksfile delete mode 100644 chef/cookbooks/apt/TESTING.md create mode 100644 chef/cookbooks/apt/libraries/helpers.rb create mode 100644 chef/cookbooks/apt/libraries/matchers.rb rename chef/cookbooks/{yum/files/default/tests/minitest/default_test.rb => apt/libraries/network.rb} (64%) create mode 100644 chef/cookbooks/apt/metadata.json delete mode 100644 chef/cookbooks/apt/spec/cacher-client_spec.rb delete mode 100644 chef/cookbooks/apt/spec/cacher-ng_spec.rb delete mode 100644 chef/cookbooks/apt/spec/default_spec.rb delete mode 100644 chef/cookbooks/apt/spec/spec_helper.rb delete mode 100644 chef/cookbooks/apt/test/cookbooks/apt_test/README.md delete mode 100644 chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng-client_test.rb delete mode 100644 chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/lwrps_test.rb delete mode 100644 chef/cookbooks/apt/test/cookbooks/apt_test/metadata.rb delete mode 100644 chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng.rb delete mode 100644 chef/cookbooks/apt/test/cookbooks/apt_test/recipes/lwrps.rb delete mode 100644 chef/cookbooks/aws/CONTRIBUTING create mode 100644 chef/cookbooks/aws/metadata.json delete mode 100644 chef/cookbooks/build-essential/.kitchen.yml delete mode 100644 chef/cookbooks/build-essential/CONTRIBUTING delete mode 100644 chef/cookbooks/build-essential/LICENSE delete mode 100644 chef/cookbooks/build-essential/TESTING.md create mode 100644 chef/cookbooks/build-essential/metadata.json rename chef/cookbooks/{build-essential => ceph}/Berksfile (75%) create mode 100644 chef/cookbooks/ceph/CHANGELOG.md create mode 100644 chef/cookbooks/ceph/Gemfile create mode 100644 chef/cookbooks/ceph/LICENSE create mode 100644 chef/cookbooks/ceph/README.md create mode 100644 chef/cookbooks/ceph/Rakefile create mode 100644 chef/cookbooks/ceph/attributes/cephfs.rb create mode 100644 chef/cookbooks/ceph/attributes/conf.rb create mode 100644 chef/cookbooks/ceph/attributes/default.rb create mode 100644 chef/cookbooks/ceph/attributes/mds.rb create mode 100644 chef/cookbooks/ceph/attributes/mon.rb create mode 100644 chef/cookbooks/ceph/attributes/osd.rb create mode 100644 chef/cookbooks/ceph/attributes/radosgw.rb create mode 100644 chef/cookbooks/ceph/attributes/radosgw_apache2.rb create mode 100644 chef/cookbooks/ceph/attributes/repo.rb create mode 100644 chef/cookbooks/ceph/conf.rb create mode 100644 chef/cookbooks/ceph/infrastructure.yml create mode 100644 chef/cookbooks/ceph/libraries/default.rb create mode 100644 chef/cookbooks/ceph/libraries/utils.rb create mode 100644 chef/cookbooks/ceph/metadata.rb create mode 100644 chef/cookbooks/ceph/providers/client.rb rename chef/cookbooks/{git => ceph/recipes}/.gitignore (64%) create mode 100644 chef/cookbooks/ceph/recipes/.kitchen.yml create mode 100644 chef/cookbooks/ceph/recipes/.rubocop.yml create mode 100644 chef/cookbooks/ceph/recipes/.travis.yml create mode 100644 chef/cookbooks/ceph/recipes/_common.rb create mode 100644 chef/cookbooks/ceph/recipes/_common_install.rb create mode 100644 chef/cookbooks/ceph/recipes/all_in_one.rb create mode 100644 chef/cookbooks/ceph/recipes/apt.rb create mode 100644 chef/cookbooks/ceph/recipes/cephfs.rb create mode 100644 chef/cookbooks/ceph/recipes/cephfs_install.rb create mode 100644 chef/cookbooks/ceph/recipes/conf.rb create mode 100644 chef/cookbooks/ceph/recipes/install.rb create mode 100644 chef/cookbooks/ceph/recipes/mds.rb create mode 100644 chef/cookbooks/ceph/recipes/mds_install.rb create mode 100644 chef/cookbooks/ceph/recipes/mon.rb create mode 100644 chef/cookbooks/ceph/recipes/mon_install.rb create mode 100644 chef/cookbooks/ceph/recipes/openstack_config_mon.rb create mode 100644 chef/cookbooks/ceph/recipes/openstack_config_radosgw.rb create mode 100644 chef/cookbooks/ceph/recipes/osd.rb create mode 100644 chef/cookbooks/ceph/recipes/osd_install.rb create mode 100644 chef/cookbooks/ceph/recipes/radosgw.rb create mode 100644 chef/cookbooks/ceph/recipes/radosgw_apache2.rb create mode 100644 chef/cookbooks/ceph/recipes/radosgw_apache2_repo.rb create mode 100644 chef/cookbooks/ceph/recipes/radosgw_install.rb create mode 100644 chef/cookbooks/ceph/recipes/repo.rb create mode 100644 chef/cookbooks/ceph/recipes/rpm.rb create mode 100644 chef/cookbooks/ceph/recipes/tgt.rb create mode 100644 chef/cookbooks/ceph/resources/client.rb create mode 100644 chef/cookbooks/ceph/templates/default/ceph.client.keyring.erb create mode 100644 chef/cookbooks/ceph/templates/default/ceph.conf.erb create mode 100644 chef/cookbooks/ceph/templates/default/mods/fastcgi.conf.erb create mode 100644 chef/cookbooks/ceph/templates/default/rgw.conf.erb create mode 100644 chef/cookbooks/ceph/templates/default/s3gw.fcgi.erb create mode 100644 chef/cookbooks/ceph/test/integration/Vagrantfile.erb create mode 100644 chef/cookbooks/ceph/test/integration/aio/bats/ceph-running.bats delete mode 100644 chef/cookbooks/chef_handler/CONTRIBUTING delete mode 100644 chef/cookbooks/chef_handler/LICENSE rename chef/cookbooks/{rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cluster_test.rb => chef_handler/libraries/matchers.rb} (57%) create mode 100644 chef/cookbooks/chef_handler/metadata.json delete mode 100644 chef/cookbooks/database/CONTRIBUTING delete mode 100644 chef/cookbooks/database/LICENSE create mode 100644 chef/cookbooks/database/libraries/provider_database_postgresql_schema.rb create mode 100644 chef/cookbooks/database/libraries/resource_postgresql_database_schema.rb create mode 100644 chef/cookbooks/database/metadata.json delete mode 100644 chef/cookbooks/dmg/CONTRIBUTING delete mode 100644 chef/cookbooks/dmg/LICENSE create mode 100644 chef/cookbooks/dmg/metadata.json delete mode 100644 chef/cookbooks/erlang/.kitchen.yml delete mode 100644 chef/cookbooks/erlang/Berksfile delete mode 100644 chef/cookbooks/erlang/CONTRIBUTING.md delete mode 100644 chef/cookbooks/erlang/LICENSE delete mode 100644 chef/cookbooks/erlang/TESTING.md delete mode 100644 chef/cookbooks/erlang/chefignore delete mode 100644 chef/cookbooks/erlang/files/default/tests/minitest/default_test.rb delete mode 100644 chef/cookbooks/erlang/files/default/tests/minitest/esl_test.rb delete mode 100644 chef/cookbooks/erlang/files/default/tests/minitest/gui_tools_test.rb delete mode 100644 chef/cookbooks/erlang/files/default/tests/minitest/support/helpers.rb create mode 100644 chef/cookbooks/erlang/metadata.json delete mode 100644 chef/cookbooks/git/.kitchen.yml delete mode 100644 chef/cookbooks/git/Berksfile delete mode 100644 chef/cookbooks/git/CONTRIBUTING delete mode 100644 chef/cookbooks/git/Gemfile delete mode 100644 chef/cookbooks/git/LICENSE delete mode 100644 chef/cookbooks/git/TESTING.md create mode 100644 chef/cookbooks/git/metadata.json create mode 100644 chef/cookbooks/git/providers/config.rb create mode 100644 chef/cookbooks/git/resources/config.rb create mode 100644 chef/cookbooks/homebrew/CHANGELOG.md create mode 100644 chef/cookbooks/homebrew/README.md create mode 100644 chef/cookbooks/homebrew/attributes/default.rb create mode 100644 chef/cookbooks/homebrew/libraries/homebrew_mixin.rb create mode 100644 chef/cookbooks/homebrew/libraries/homebrew_package.rb create mode 100644 chef/cookbooks/homebrew/libraries/matchers.rb create mode 100644 chef/cookbooks/homebrew/metadata.json create mode 100644 chef/cookbooks/homebrew/metadata.rb create mode 100644 chef/cookbooks/homebrew/providers/cask.rb create mode 100644 chef/cookbooks/homebrew/providers/tap.rb create mode 100644 chef/cookbooks/homebrew/recipes/default.rb create mode 100644 chef/cookbooks/homebrew/resources/cask.rb rename chef/cookbooks/{yum/resources/key.rb => homebrew/resources/tap.rb} (56%) delete mode 100644 chef/cookbooks/iptables/CONTRIBUTING delete mode 100644 chef/cookbooks/iptables/LICENSE delete mode 100644 chef/cookbooks/iptables/files/default/rebuild-iptables create mode 100644 chef/cookbooks/iptables/templates/default/rebuild-iptables.erb create mode 100644 chef/cookbooks/keepalived/libraries/default.rb create mode 100644 chef/cookbooks/logrotate/.gitignore create mode 100644 chef/cookbooks/logrotate/.kitchen.yml create mode 100644 chef/cookbooks/logrotate/.rubocop.yml create mode 100644 chef/cookbooks/logrotate/.travis.yml create mode 100644 chef/cookbooks/logrotate/Berksfile create mode 100644 chef/cookbooks/logrotate/CHANGELOG.md create mode 100644 chef/cookbooks/logrotate/CONTRIBUTING.md create mode 100644 chef/cookbooks/logrotate/Gemfile rename chef/cookbooks/{apache2 => logrotate}/LICENSE (100%) create mode 100644 chef/cookbooks/logrotate/README.md create mode 100644 chef/cookbooks/logrotate/TESTING.md rename chef/cookbooks/{mysql/test/cookbooks/mysql_test => logrotate}/attributes/default.rb (56%) create mode 100644 chef/cookbooks/logrotate/definitions/logrotate_app.rb create mode 100644 chef/cookbooks/logrotate/libraries/logrotate_config.rb create mode 100644 chef/cookbooks/logrotate/metadata.json create mode 100644 chef/cookbooks/logrotate/metadata.rb rename chef/cookbooks/{openstack-block-storage => logrotate}/recipes/default.rb (86%) rename chef/cookbooks/{apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng_test.rb => logrotate/recipes/global.rb} (61%) create mode 100644 chef/cookbooks/logrotate/templates/default/logrotate-global.erb create mode 100644 chef/cookbooks/logrotate/templates/default/logrotate.erb delete mode 100644 chef/cookbooks/memcached/.kitchen.yml delete mode 100644 chef/cookbooks/memcached/Berksfile delete mode 100644 chef/cookbooks/memcached/CONTRIBUTING delete mode 100644 chef/cookbooks/memcached/Gemfile delete mode 100644 chef/cookbooks/memcached/LICENSE delete mode 100644 chef/cookbooks/memcached/TESTING.md create mode 100644 chef/cookbooks/memcached/metadata.json delete mode 100644 chef/cookbooks/memcached/test/cookbooks/memcached_test/README.md delete mode 100644 chef/cookbooks/memcached/test/cookbooks/memcached_test/metadata.rb delete mode 100644 chef/cookbooks/mysql/.kitchen.yml delete mode 100644 chef/cookbooks/mysql/Berksfile delete mode 100644 chef/cookbooks/mysql/CONTRIBUTING delete mode 100644 chef/cookbooks/mysql/LICENSE delete mode 100644 chef/cookbooks/mysql/TESTING.md create mode 100644 chef/cookbooks/mysql/attributes/server_debian.rb create mode 100644 chef/cookbooks/mysql/attributes/server_freebsd.rb create mode 100644 chef/cookbooks/mysql/attributes/server_mac_os_x.rb create mode 100644 chef/cookbooks/mysql/attributes/server_rhel.rb create mode 100644 chef/cookbooks/mysql/attributes/server_suse.rb create mode 100644 chef/cookbooks/mysql/attributes/server_windows.rb create mode 100644 chef/cookbooks/mysql/metadata.json create mode 100644 chef/cookbooks/mysql/recipes/_server_debian.rb create mode 100644 chef/cookbooks/mysql/recipes/_server_mac_os_x.rb create mode 100644 chef/cookbooks/mysql/recipes/_server_rhel.rb create mode 100644 chef/cookbooks/mysql/recipes/_server_windows.rb delete mode 100644 chef/cookbooks/mysql/recipes/percona_repo.rb create mode 100644 chef/cookbooks/mysql/templates/default/init-mysql.conf.erb create mode 100644 chef/cookbooks/mysql/templates/default/usr.sbin.mysqld.erb create mode 100644 chef/cookbooks/mysql/templates/ubuntu-10/init-mysql.conf.erb delete mode 100644 chef/cookbooks/mysql/templates/windows/my.cnf.erb create mode 100644 chef/cookbooks/mysql/templates/windows/my.ini.erb delete mode 100644 chef/cookbooks/mysql/test/cookbooks/mysql_test/README.md delete mode 100644 chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/server_test.rb delete mode 100644 chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/support/helpers.rb delete mode 100644 chef/cookbooks/mysql/test/cookbooks/mysql_test/metadata.rb delete mode 100644 chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/server.rb delete mode 100644 chef/cookbooks/mysql/test/features/query_database.feature delete mode 100644 chef/cookbooks/mysql/test/features/step_definitions/mysql_steps.rb delete mode 100644 chef/cookbooks/mysql/test/features/support/env.rb delete mode 100644 chef/cookbooks/mysql/test/features/support/mysql_helpers.rb create mode 100644 chef/cookbooks/openssh/.gitignore create mode 100644 chef/cookbooks/openssh/.kitchen.cloud.yml create mode 100644 chef/cookbooks/openssh/.kitchen.yml create mode 100644 chef/cookbooks/openssh/.rubocop.yml create mode 100644 chef/cookbooks/openssh/.travis.yml rename chef/cookbooks/{xfs => openssh}/Berksfile (64%) rename chef/cookbooks/{apache2 => openssh}/CONTRIBUTING.md (100%) create mode 100644 chef/cookbooks/openssh/Gemfile rename chef/cookbooks/{apt => openssh}/LICENSE (100%) create mode 100644 chef/cookbooks/openssh/TESTING.md create mode 100644 chef/cookbooks/openssh/libraries/default.rb delete mode 100644 chef/cookbooks/openssh/metadata.json create mode 100644 chef/cookbooks/openssh/providers/key.rb create mode 100644 chef/cookbooks/openssh/recipes/passwordless.rb create mode 100644 chef/cookbooks/openssh/resources/key.rb create mode 100644 chef/cookbooks/openssh/spec/spec_helper.rb create mode 100644 chef/cookbooks/openssh/spec/unit/recipes/default_spec.rb create mode 100644 chef/cookbooks/openssh/test/integration/default/bats/check_service.bats delete mode 100644 chef/cookbooks/openssl/CONTRIBUTING delete mode 100644 chef/cookbooks/openssl/LICENSE create mode 100644 chef/cookbooks/openstack-block-storage/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-block-storage/.tailor delete mode 100644 chef/cookbooks/openstack-block-storage/Berksfile.lock create mode 100644 chef/cookbooks/openstack-block-storage/TESTING.md delete mode 100644 chef/cookbooks/openstack-block-storage/files/default/cinder-volumes.sh create mode 100644 chef/cookbooks/openstack-block-storage/recipes/cinder-config-ceph.rb create mode 100644 chef/cookbooks/openstack-block-storage/recipes/client.rb delete mode 100644 chef/cookbooks/openstack-block-storage/recipes/test.rb delete mode 100644 chef/cookbooks/openstack-block-storage/spec/cinder_common-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-block-storage/spec/cinder_common-suse_spec.rb create mode 100644 chef/cookbooks/openstack-block-storage/spec/client-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-block-storage/spec/client_spec.rb delete mode 100644 chef/cookbooks/openstack-block-storage/spec/scheduler-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-block-storage/spec/scheduler-suse_spec.rb delete mode 100644 chef/cookbooks/openstack-block-storage/spec/volume-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-block-storage/spec/volume-suse_spec.rb create mode 100644 chef/cookbooks/openstack-block-storage/templates/default/cinder-group-active.erb create mode 100644 chef/cookbooks/openstack-block-storage/templates/default/cinder_emc_config.xml.erb create mode 100644 chef/cookbooks/openstack-block-storage/templates/default/nfs_shares.conf.erb delete mode 100644 chef/cookbooks/openstack-block-storage/templates/default/policy.json.erb create mode 100644 chef/cookbooks/openstack-common/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-common/.tailor mode change 100644 => 100755 chef/cookbooks/openstack-common/CHANGELOG.md mode change 100644 => 100755 chef/cookbooks/openstack-common/README.md create mode 100644 chef/cookbooks/openstack-common/TESTING.md create mode 100644 chef/cookbooks/openstack-common/attributes/database.rb create mode 100644 chef/cookbooks/openstack-common/attributes/messaging.rb create mode 100644 chef/cookbooks/openstack-common/files/default/RPM-GPG-KEY-RDO-Icehouse create mode 100755 chef/cookbooks/openstack-common/libraries/cli.rb mode change 100644 => 100755 chef/cookbooks/openstack-common/metadata.rb create mode 100644 chef/cookbooks/openstack-common/recipes/ceph_client.rb create mode 100755 chef/cookbooks/openstack-common/recipes/client.rb delete mode 100644 chef/cookbooks/openstack-common/recipes/databag.rb delete mode 100644 chef/cookbooks/openstack-common/recipes/environ.rb create mode 100644 chef/cookbooks/openstack-common/recipes/openrc.rb create mode 100644 chef/cookbooks/openstack-common/recipes/set_endpoints_by_interface.rb rename chef/cookbooks/{apt/test/cookbooks/apt_test/recipes/cacher-ng-client.rb => openstack-common/recipes/sysctl.rb} (57%) create mode 100644 chef/cookbooks/openstack-common/spec/ceph_spec.rb create mode 100755 chef/cookbooks/openstack-common/spec/cli_spec.rb create mode 100755 chef/cookbooks/openstack-common/spec/client-redhat_spec.rb create mode 100755 chef/cookbooks/openstack-common/spec/client_spec.rb create mode 100644 chef/cookbooks/openstack-common/spec/default-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-common/spec/openrc_spec.rb create mode 100644 chef/cookbooks/openstack-common/spec/sysctl_spec.rb create mode 100644 chef/cookbooks/openstack-common/templates/default/60-openstack.conf.erb create mode 100644 chef/cookbooks/openstack-common/templates/default/ceph.client.keyring.erb create mode 100644 chef/cookbooks/openstack-common/templates/default/ceph.conf.erb create mode 100644 chef/cookbooks/openstack-common/templates/default/openrc.erb create mode 100644 chef/cookbooks/openstack-compute/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-compute/.tailor delete mode 100644 chef/cookbooks/openstack-compute/Berksfile.lock create mode 100644 chef/cookbooks/openstack-compute/CHANGELOG.md create mode 100644 chef/cookbooks/openstack-compute/TESTING.md create mode 100644 chef/cookbooks/openstack-compute/recipes/client.rb create mode 100644 chef/cookbooks/openstack-compute/recipes/compute-config-ceph.rb create mode 100644 chef/cookbooks/openstack-compute/recipes/libvirt_rbd.rb create mode 100644 chef/cookbooks/openstack-compute/spec/client-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-compute/spec/client_spec.rb delete mode 100644 chef/cookbooks/openstack-compute/spec/compute-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-compute/spec/compute-suse_spec.rb delete mode 100644 chef/cookbooks/openstack-compute/spec/libvirt-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-compute/spec/libvirt-suse_spec.rb create mode 100644 chef/cookbooks/openstack-compute/spec/libvirt_rbd_spec.rb delete mode 100644 chef/cookbooks/openstack-compute/templates/default/openrc.erb delete mode 100644 chef/cookbooks/openstack-compute/templates/default/rootwrap.d/api-metadata.filters.erb delete mode 100644 chef/cookbooks/openstack-compute/templates/default/rootwrap.d/compute.filters.erb delete mode 100644 chef/cookbooks/openstack-compute/templates/default/rootwrap.d/network.filters.erb create mode 100644 chef/cookbooks/openstack-compute/templates/default/secret.xml.erb create mode 100644 chef/cookbooks/openstack-dashboard/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-dashboard/.tailor create mode 100644 chef/cookbooks/openstack-dashboard/CHANGELOG.md create mode 100644 chef/cookbooks/openstack-dashboard/TESTING.md delete mode 100644 chef/cookbooks/openstack-dashboard/spec/default_spec.rb delete mode 100644 chef/cookbooks/openstack-dashboard/spec/server-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-dashboard/spec/server-suse_spec.rb create mode 100644 chef/cookbooks/openstack-identity/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-identity/.tailor delete mode 100644 chef/cookbooks/openstack-identity/Berksfile.lock create mode 100644 chef/cookbooks/openstack-identity/CHANGELOG.md create mode 100644 chef/cookbooks/openstack-identity/TESTING.md delete mode 100644 chef/cookbooks/openstack-identity/files/default/keystone_plugin.py create mode 100644 chef/cookbooks/openstack-identity/libraries/default.rb create mode 100644 chef/cookbooks/openstack-identity/libraries/matchers.rb create mode 100644 chef/cookbooks/openstack-identity/recipes/client.rb create mode 100644 chef/cookbooks/openstack-identity/spec/client-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-identity/spec/client_spec.rb delete mode 100644 chef/cookbooks/openstack-identity/spec/default_spec.rb create mode 100644 chef/cookbooks/openstack-identity/spec/registration_spec.rb delete mode 100644 chef/cookbooks/openstack-identity/spec/server-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-identity/spec/server-suse_spec.rb create mode 100644 chef/cookbooks/openstack-image/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-image/.tailor delete mode 100644 chef/cookbooks/openstack-image/Berksfile.lock create mode 100644 chef/cookbooks/openstack-image/CHANGELOG.md create mode 100644 chef/cookbooks/openstack-image/TESTING.md create mode 100644 chef/cookbooks/openstack-image/recipes/client.rb create mode 100644 chef/cookbooks/openstack-image/recipes/glance-config-ceph.rb create mode 100644 chef/cookbooks/openstack-image/recipes/image_upload.rb create mode 100644 chef/cookbooks/openstack-image/spec/client-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-image/spec/client_spec.rb delete mode 100644 chef/cookbooks/openstack-image/spec/default_spec.rb create mode 100644 chef/cookbooks/openstack-image/spec/image_upload_spec.rb delete mode 100644 chef/cookbooks/openstack-image/templates/default/policy.json.erb delete mode 100644 chef/cookbooks/openstack-image/templates/default/tinyimage.sh.erb delete mode 100644 chef/cookbooks/openstack-metering/Berksfile.lock delete mode 100644 chef/cookbooks/openstack-metering/CHANGELOG.md delete mode 100644 chef/cookbooks/openstack-metering/Gemfile delete mode 100644 chef/cookbooks/openstack-metering/README.md delete mode 100644 chef/cookbooks/openstack-metering/attributes/default.rb delete mode 100644 chef/cookbooks/openstack-metering/files/default/policy.json delete mode 100644 chef/cookbooks/openstack-metering/metadata.rb delete mode 100644 chef/cookbooks/openstack-metering/recipes/collector.rb delete mode 100644 chef/cookbooks/openstack-metering/recipes/common.rb delete mode 100644 chef/cookbooks/openstack-metering/recipes/identity_registration.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/agent-central-opensuse_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/agent-central_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/agent-compute-opensuse_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/agent-compute_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/api-opensuse_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/api_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/collector-opensuse_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/collector_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/common-opensuse_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/common_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/identity_registration_spec.rb delete mode 100644 chef/cookbooks/openstack-metering/spec/spec_helper.rb delete mode 100644 chef/cookbooks/openstack-metering/templates/default/ceilometer.conf.erb create mode 100644 chef/cookbooks/openstack-network/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-network/Berksfile.lock create mode 100644 chef/cookbooks/openstack-network/CHANGELOG.md create mode 100644 chef/cookbooks/openstack-network/TESTING.md delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/debug.filters delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/dhcp.filters delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/iptables-firewall.filters delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/l3.filters delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/lbaas-haproxy.filters delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/linuxbridge-plugin.filters delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/nec-plugin.filters delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/openvswitch-plugin.filters delete mode 100644 chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/ryu-plugin.filters create mode 100755 chef/cookbooks/openstack-network/files/default/neutron-ha-tool.py create mode 100644 chef/cookbooks/openstack-network/files/default/ovs-dpctl-top delete mode 100755 chef/cookbooks/openstack-network/files/default/quantum-ha-tool.py create mode 100644 chef/cookbooks/openstack-network/recipes/build_openvswitch_source.rb create mode 100644 chef/cookbooks/openstack-network/recipes/client.rb create mode 100644 chef/cookbooks/openstack-network/spec/balancer-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/balancer-suse_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/build_openvswitch_source_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/client-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/client_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/common-redhat_spec.rb delete mode 100644 chef/cookbooks/openstack-network/spec/dhcp_agent-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/dhcp_agent-suse_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/hyperv_spec.rb delete mode 100644 chef/cookbooks/openstack-network/spec/linuxbridge-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/linuxbridge-suse_spec.rb delete mode 100644 chef/cookbooks/openstack-network/spec/openvswitch-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/openvswitch-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/openvswitch-suse_spec.rb delete mode 100644 chef/cookbooks/openstack-network/spec/server-opensuse_spec.rb create mode 100644 chef/cookbooks/openstack-network/spec/server-suse_spec.rb rename chef/cookbooks/openstack-network/templates/default/{quantum-server.erb => neutron-server.erb} (59%) create mode 100644 chef/cookbooks/openstack-network/templates/default/neutron.conf.erb rename chef/cookbooks/openstack-network/templates/default/{quantum.sysconfig.erb => neutron.sysconfig.erb} (51%) rename chef/cookbooks/openstack-network/templates/default/plugins/hyperv/{hyperv_quantum_plugin.ini.erb => hyperv_neutron_plugin.ini.erb} (68%) create mode 100644 chef/cookbooks/openstack-network/templates/default/plugins/ml2/ml2_conf.ini.erb delete mode 100644 chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/openvswitch.erb rename chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/{ovs_quantum_plugin.ini.erb => ovs_neutron_plugin.ini.erb} (76%) delete mode 100644 chef/cookbooks/openstack-network/templates/default/policy.json.erb delete mode 100644 chef/cookbooks/openstack-network/templates/default/quantum-server.start.erb delete mode 100644 chef/cookbooks/openstack-network/templates/default/quantum.conf.erb create mode 100644 chef/cookbooks/openstack-object-storage/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-object-storage/Berksfile.lock create mode 100644 chef/cookbooks/openstack-object-storage/CHANGELOG.md create mode 100644 chef/cookbooks/openstack-object-storage/TESTING.md create mode 100644 chef/cookbooks/openstack-object-storage/files/default/git-daemon.default create mode 100644 chef/cookbooks/openstack-object-storage/files/default/swift-container-sync.conf.upstart rename chef/cookbooks/{mysql/test/cookbooks/mysql_test => openstack-object-storage}/recipes/client.rb (64%) create mode 100644 chef/cookbooks/openstack-object-storage/recipes/swift-config-ceph.rb rename chef/cookbooks/{apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_listed_host.rb => openstack-object-storage/recipes/swiftclient-patch.rb} (57%) create mode 100644 chef/cookbooks/openstack-object-storage/spec/client_spec.rb create mode 100755 chef/cookbooks/openstack-object-storage/spec/setup_spec.rb create mode 100644 chef/cookbooks/openstack-object-storage/templates/default/swift-statsd-publish.py.erb create mode 100644 chef/cookbooks/openstack-ops-database/.rubocop.yml create mode 100644 chef/cookbooks/openstack-ops-database/Berksfile create mode 100644 chef/cookbooks/openstack-ops-database/Gemfile create mode 100644 chef/cookbooks/openstack-ops-database/Gemfile.lock rename chef/cookbooks/{openstack-metering => openstack-ops-database}/Strainerfile (80%) create mode 100644 chef/cookbooks/openstack-ops-database/TESTING.md create mode 100644 chef/cookbooks/openstack-ops-database/spec/client_spec.rb create mode 100644 chef/cookbooks/openstack-ops-database/spec/mysql-client-suse_spec.rb create mode 100644 chef/cookbooks/openstack-ops-database/spec/mysql-client_spec.rb create mode 100644 chef/cookbooks/openstack-ops-database/spec/mysql-server-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-ops-database/spec/mysql-server_spec.rb create mode 100644 chef/cookbooks/openstack-ops-database/spec/openstack-db_spec.rb create mode 100644 chef/cookbooks/openstack-ops-database/spec/postgresql-server_spec.rb create mode 100644 chef/cookbooks/openstack-ops-database/spec/server_spec.rb create mode 100644 chef/cookbooks/openstack-ops-database/spec/spec_helper.rb create mode 100644 chef/cookbooks/openstack-ops-messaging/.rubocop.yml delete mode 100644 chef/cookbooks/openstack-ops-messaging/.tailor delete mode 100644 chef/cookbooks/openstack-ops-messaging/Berksfile.lock create mode 100644 chef/cookbooks/openstack-ops-messaging/TESTING.md delete mode 100644 chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq.rb create mode 100644 chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/.rubocop.yml rename chef/cookbooks/{openstack-metering => openstack-orchestration}/Berksfile (98%) create mode 100644 chef/cookbooks/openstack-orchestration/CHANGELOG.md create mode 100644 chef/cookbooks/openstack-orchestration/Gemfile create mode 100644 chef/cookbooks/openstack-orchestration/Gemfile.lock create mode 100644 chef/cookbooks/openstack-orchestration/README.md create mode 100644 chef/cookbooks/openstack-orchestration/Strainerfile create mode 100644 chef/cookbooks/openstack-orchestration/TESTING.md create mode 100644 chef/cookbooks/openstack-orchestration/attributes/default.rb create mode 100644 chef/cookbooks/openstack-orchestration/metadata.rb create mode 100644 chef/cookbooks/openstack-orchestration/recipes/api-cfn.rb create mode 100644 chef/cookbooks/openstack-orchestration/recipes/api-cloudwatch.rb create mode 100644 chef/cookbooks/openstack-orchestration/recipes/api.rb create mode 100644 chef/cookbooks/openstack-orchestration/recipes/client.rb create mode 100644 chef/cookbooks/openstack-orchestration/recipes/common.rb rename chef/cookbooks/{apt/test/cookbooks/apt_test => openstack-orchestration}/recipes/default.rb (86%) create mode 100644 chef/cookbooks/openstack-orchestration/recipes/engine.rb create mode 100644 chef/cookbooks/openstack-orchestration/recipes/identity_registration.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/api-cfn-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/api-cloudwatch-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/api-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/client-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/client_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/common-redhat_spec.rb create mode 100755 chef/cookbooks/openstack-orchestration/spec/common_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/engine-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/identity_registration_spec.rb create mode 100644 chef/cookbooks/openstack-orchestration/spec/spec_helper.rb create mode 100644 chef/cookbooks/openstack-orchestration/templates/default/api-paste.ini.erb create mode 100644 chef/cookbooks/openstack-orchestration/templates/default/default.yaml.erb create mode 100755 chef/cookbooks/openstack-orchestration/templates/default/heat.conf.erb create mode 100644 chef/cookbooks/openstack-telemetry/.rubocop.yml create mode 100644 chef/cookbooks/openstack-telemetry/Berksfile create mode 100644 chef/cookbooks/openstack-telemetry/CHANGELOG.md create mode 100644 chef/cookbooks/openstack-telemetry/Gemfile create mode 100644 chef/cookbooks/openstack-telemetry/Gemfile.lock create mode 100644 chef/cookbooks/openstack-telemetry/README.md create mode 100644 chef/cookbooks/openstack-telemetry/Strainerfile create mode 100644 chef/cookbooks/openstack-telemetry/TESTING.md create mode 100644 chef/cookbooks/openstack-telemetry/attributes/default.rb create mode 100644 chef/cookbooks/openstack-telemetry/metadata.rb rename chef/cookbooks/{openstack-metering => openstack-telemetry}/recipes/agent-central.rb (56%) rename chef/cookbooks/{openstack-metering => openstack-telemetry}/recipes/agent-compute.rb (59%) create mode 100644 chef/cookbooks/openstack-telemetry/recipes/agent-notification.rb create mode 100644 chef/cookbooks/openstack-telemetry/recipes/alarm-evaluator.rb rename chef/cookbooks/{python/test/cookbooks/python_test/recipes/cook-3084.rb => openstack-telemetry/recipes/alarm-notifier.rb} (50%) rename chef/cookbooks/{openstack-metering => openstack-telemetry}/recipes/api.rb (52%) rename chef/cookbooks/{apt/test/cookbooks/apt_test/files/default/tests/minitest/default_test.rb => openstack-telemetry/recipes/client.rb} (61%) create mode 100644 chef/cookbooks/openstack-telemetry/recipes/collector.rb create mode 100644 chef/cookbooks/openstack-telemetry/recipes/common.rb create mode 100644 chef/cookbooks/openstack-telemetry/recipes/identity_registration.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-central-rhel_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-central-suse_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-central_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-compute-rhel_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-compute-suse_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-compute_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-notification-rhel_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-notification-suse_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/agent-notification_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/alarm-evaluator-rhel_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/alarm-evaluator-suse_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/alarm-evaluator_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/alarm-notifier-rhel_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/alarm-notifier-suse_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/alarm-notifier_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/api-rhel_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/api-suse_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/api_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/client-redhat_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/client_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/collector-rhel_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/collector-suse_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/collector_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/common-rhel_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/common-suse_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/common_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/identity_registration_spec.rb create mode 100644 chef/cookbooks/openstack-telemetry/spec/spec_helper.rb create mode 100644 chef/cookbooks/openstack-telemetry/templates/default/ceilometer.conf.erb create mode 100644 chef/cookbooks/pacman/.gitignore create mode 100644 chef/cookbooks/pacman/.travis.yml create mode 100644 chef/cookbooks/pacman/CHANGELOG.md rename chef/cookbooks/{apt => pacman}/CONTRIBUTING (100%) create mode 100644 chef/cookbooks/pacman/Gemfile create mode 100644 chef/cookbooks/pacman/Gemfile.lock rename chef/cookbooks/{aws => pacman}/LICENSE (100%) create mode 100644 chef/cookbooks/pacman/README.md create mode 100644 chef/cookbooks/pacman/metadata.rb create mode 100644 chef/cookbooks/pacman/providers/aur.rb create mode 100644 chef/cookbooks/pacman/providers/group.rb rename chef/cookbooks/{apache2/test/kitchen/cookbooks/apache2_test => pacman}/recipes/default.rb (81%) rename chef/cookbooks/{apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_openid.rb => pacman/resources/aur.rb} (53%) rename chef/cookbooks/{memcached/test/cookbooks/memcached_test/recipes/instance.rb => pacman/resources/group.rb} (67%) create mode 100644 chef/cookbooks/pacman/spec/spec_helper.rb delete mode 100644 chef/cookbooks/postgresql/.kitchen.yml delete mode 100644 chef/cookbooks/postgresql/Berksfile delete mode 100644 chef/cookbooks/postgresql/CONTRIBUTING.md delete mode 100644 chef/cookbooks/postgresql/LICENSE delete mode 100644 chef/cookbooks/postgresql/TESTING.md create mode 100644 chef/cookbooks/postgresql/metadata.json create mode 100644 chef/cookbooks/python/.travis.yml create mode 100644 chef/cookbooks/python/Berksfile.lock delete mode 100644 chef/cookbooks/python/CONTRIBUTING create mode 100644 chef/cookbooks/python/Gemfile.lock create mode 100644 chef/cookbooks/python/NOTICE.txt create mode 100644 chef/cookbooks/python/Rakefile create mode 100644 chef/cookbooks/python/ZPL.txt create mode 100644 chef/cookbooks/python/files/default/get-pip.py create mode 100644 chef/cookbooks/python/libraries/matchers.rb create mode 100644 chef/cookbooks/python/metadata.json delete mode 100644 chef/cookbooks/python/src/1/calculating_with_dictionaries/example.py delete mode 100644 chef/cookbooks/python/src/1/determine_the_top_n_items_occurring_in_a_list/example.py delete mode 100644 chef/cookbooks/python/src/1/extracting_a_subset_of_a_dictionary/example.py delete mode 100644 chef/cookbooks/python/src/1/filtering_list_elements/example.py delete mode 100644 chef/cookbooks/python/src/1/finding_out_what_two_dictionaries_have_in_common/example.py delete mode 100644 chef/cookbooks/python/src/1/finding_the_largest_or_smallest_n_items/example.py delete mode 100644 chef/cookbooks/python/src/1/grouping-records-together-based-on-a-field/grouping.py delete mode 100644 chef/cookbooks/python/src/1/implementing_a_priority_queue/example.py delete mode 100644 chef/cookbooks/python/src/1/keeping_the_last_n_items/example.py delete mode 100644 chef/cookbooks/python/src/1/keeping_the_last_n_items/somefile.txt delete mode 100644 chef/cookbooks/python/src/1/mapping_names_to_sequence_elements/example1.py delete mode 100644 chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example.py delete mode 100644 chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example2.py delete mode 100644 chef/cookbooks/python/src/1/sort_a_list_of_dictionaries_by_a_common_key/example.py delete mode 100644 chef/cookbooks/python/src/1/sort_objects_without_native_comparison_support/example.py delete mode 100644 chef/cookbooks/python/src/1/transforming_and_reducing_data_at_the_same_time/example.py delete mode 100644 chef/cookbooks/python/src/1/unpack_a_fixed_number_of_elements_from_iterables_of_arbitrary_length/example.py delete mode 100644 chef/cookbooks/python/src/1/working_with_multiple_mappings_as_a_single_mapping/example.py delete mode 100644 chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/explicit_load.py delete mode 100644 chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/metaexample.py delete mode 100644 chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/pathexample.py delete mode 100644 chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/fib.py delete mode 100644 chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/__init__.py delete mode 100644 chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/blah.py delete mode 100644 chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/spam.py delete mode 100644 chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/urlimport.py delete mode 100644 chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/bar-package/spam/grok.py delete mode 100644 chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/example.py delete mode 100644 chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/foo-package/spam/blah.py delete mode 100644 chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example1.py delete mode 100644 chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example2.py delete mode 100644 chef/cookbooks/python/src/10/monkeypatching_modules_on_import/postimport.py delete mode 100644 chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/example.py delete mode 100644 chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/__init__.py delete mode 100644 chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/a.py delete mode 100644 chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/b.py delete mode 100644 chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoclient.py delete mode 100644 chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoserv.py delete mode 100644 chef/cookbooks/python/src/11/adding_ssl_to_network_servers/makecerts.sh delete mode 100644 chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_client.py delete mode 100644 chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_server.py delete mode 100644 chef/cookbooks/python/src/11/adding_ssl_to_network_servers/sslmixin.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/client1.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/example1.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/resty.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_tcp_server/echoclient.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv1.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv2.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv3.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv4.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv5.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_tcp_server/threadedserv.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_udp_server/client.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_udp_server/timeserv1.py delete mode 100644 chef/cookbooks/python/src/11/creating_a_udp_server/timeserv2.py delete mode 100644 chef/cookbooks/python/src/11/event_driven_io_explained/eventhandler.py delete mode 100644 chef/cookbooks/python/src/11/event_driven_io_explained/tcpclient.py delete mode 100644 chef/cookbooks/python/src/11/event_driven_io_explained/tcpserver.py delete mode 100644 chef/cookbooks/python/src/11/event_driven_io_explained/threadpool.py delete mode 100644 chef/cookbooks/python/src/11/event_driven_io_explained/thrpoolclient.py delete mode 100644 chef/cookbooks/python/src/11/event_driven_io_explained/udpclient.py delete mode 100644 chef/cookbooks/python/src/11/event_driven_io_explained/udpserver.py delete mode 100644 chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpclient.py delete mode 100644 chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpcserver.py delete mode 100644 chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcclient.py delete mode 100644 chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcserver.py delete mode 100644 chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example1.py delete mode 100644 chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example2.py delete mode 100644 chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example3.py delete mode 100644 chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example4.py delete mode 100644 chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/client1.py delete mode 100644 chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server.py delete mode 100644 chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server1.py delete mode 100644 chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/servermp.py delete mode 100644 chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/worker.py delete mode 100644 chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/workermp.py delete mode 100644 chef/cookbooks/python/src/11/simple_authentication_of_clients/auth.py delete mode 100644 chef/cookbooks/python/src/11/simple_authentication_of_clients/client.py delete mode 100644 chef/cookbooks/python/src/11/simple_authentication_of_clients/server.py delete mode 100644 chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoclient.py delete mode 100644 chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoserv.py delete mode 100644 chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/client.py delete mode 100644 chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/keyserv.py delete mode 100644 chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/client.py delete mode 100644 chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/server.py delete mode 100644 chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/zerocopy.py delete mode 100644 chef/cookbooks/python/src/12/defining_an_actor_task/actor.py delete mode 100644 chef/cookbooks/python/src/12/defining_an_actor_task/tagged.py delete mode 100644 chef/cookbooks/python/src/12/defining_an_actor_task/worker.py delete mode 100644 chef/cookbooks/python/src/12/how_to_communicate_between_threads/example1.py delete mode 100644 chef/cookbooks/python/src/12/how_to_communicate_between_threads/example2.py delete mode 100644 chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example1.py delete mode 100644 chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example2.py delete mode 100644 chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example3.py delete mode 100644 chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example1.py delete mode 100644 chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example2.py delete mode 100644 chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example3.py delete mode 100644 chef/cookbooks/python/src/12/how_to_lock_critical_sections/example1.py delete mode 100644 chef/cookbooks/python/src/12/how_to_start_and_stop_threads/example.py delete mode 100644 chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange1.py delete mode 100644 chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange2.py delete mode 100644 chef/cookbooks/python/src/12/launching_a_daemon_process_on_unix/daemon.py delete mode 100644 chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/deadlock.py delete mode 100644 chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example1.py delete mode 100644 chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example2.py delete mode 100644 chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example3.py delete mode 100644 chef/cookbooks/python/src/12/polling_multiple_thread_queues/pqueue.py delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/findrobots.py delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/findrobots_par.py delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121217.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121218.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121219.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121220.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121221.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121222.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121223.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121224.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121225.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121226.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121227.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121228.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121229.log.gz delete mode 100644 chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121230.log.gz delete mode 100644 chef/cookbooks/python/src/12/storing_thread_specific_state/example1.py delete mode 100644 chef/cookbooks/python/src/12/storing_thread_specific_state/example2.py delete mode 100644 chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/actorsched.py delete mode 100644 chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/netsched.py delete mode 100644 chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/simple.py delete mode 100644 chef/cookbooks/python/src/13/adding_logging_to_libraries/somelib.py delete mode 100644 chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example1.py delete mode 100644 chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example2.py delete mode 100644 chef/cookbooks/python/src/13/finding_files/modified_within.py delete mode 100644 chef/cookbooks/python/src/13/generating_a_range_of_ip_addresses_from_a_cidr_address/example.py delete mode 100644 chef/cookbooks/python/src/13/getting_the_terminal_size/example.py delete mode 100644 chef/cookbooks/python/src/13/making_a_stopwatch/stopwatch.py delete mode 100644 chef/cookbooks/python/src/13/parsing_command_line_options/search.py delete mode 100644 chef/cookbooks/python/src/13/prompting_for_a_password_at_runtime/example.py delete mode 100644 chef/cookbooks/python/src/13/putting_limits_on_memory_and_cpu_usage/example.py delete mode 100644 chef/cookbooks/python/src/13/reading_configuration_files/config.ini delete mode 100644 chef/cookbooks/python/src/13/reading_configuration_files/example1.py delete mode 100644 chef/cookbooks/python/src/13/simple_logging_for_scripts/example1.py delete mode 100644 chef/cookbooks/python/src/13/simple_logging_for_scripts/example2.py delete mode 100644 chef/cookbooks/python/src/13/simple_logging_for_scripts/logconfig.ini delete mode 100644 chef/cookbooks/python/src/14/logging_test_output_to_a_file/test.py delete mode 100644 chef/cookbooks/python/src/14/make_your_programs_run_faster/example.py delete mode 100644 chef/cookbooks/python/src/14/profiling_and_timing_your_program/timethis.py delete mode 100644 chef/cookbooks/python/src/14/raising_an_exception_in_response_to_another_exception/example.py delete mode 100644 chef/cookbooks/python/src/14/skipping_or_anticipating_test_failures/test.py delete mode 100644 chef/cookbooks/python/src/14/testing_for_exceptional_conditions_in_unit_tests/test.py delete mode 100644 chef/cookbooks/python/src/14/testing_output_sent_to_stdout/mymodule.py delete mode 100644 chef/cookbooks/python/src/14/testing_output_sent_to_stdout/testmymodule.py delete mode 100644 chef/cookbooks/python/src/15/Makefile delete mode 100644 chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/example.py delete mode 100644 chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/sample.py delete mode 100644 chef/cookbooks/python/src/15/calling_python_from_c/Makefile delete mode 100644 chef/cookbooks/python/src/15/calling_python_from_c/embed.c delete mode 100644 chef/cookbooks/python/src/15/consuming_an_iterable_from_c/example.py delete mode 100644 chef/cookbooks/python/src/15/consuming_an_iterable_from_c/sample.c delete mode 100644 chef/cookbooks/python/src/15/consuming_an_iterable_from_c/setup.py delete mode 100644 chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/README.txt delete mode 100644 chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/example.py delete mode 100644 chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptexample.c delete mode 100644 chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptsetup.py delete mode 100644 chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.c delete mode 100644 chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.h delete mode 100644 chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/setup.py delete mode 100644 chef/cookbooks/python/src/15/diagnosing_segmentation_faults/example.py delete mode 100644 chef/cookbooks/python/src/15/diagnosing_segmentation_faults/sample.c delete mode 100644 chef/cookbooks/python/src/15/diagnosing_segmentation_faults/setup.py delete mode 100644 chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/example.py delete mode 100644 chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/pysample.c delete mode 100644 chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/setup.py delete mode 100644 chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/example.py delete mode 100644 chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/sample.c delete mode 100644 chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/setup.py delete mode 100644 chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/example.py delete mode 100644 chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/sample.c delete mode 100644 chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/setup.py delete mode 100644 chef/cookbooks/python/src/15/reading_file_like_objects_from_c/example.py delete mode 100644 chef/cookbooks/python/src/15/reading_file_like_objects_from_c/sample.c delete mode 100644 chef/cookbooks/python/src/15/reading_file_like_objects_from_c/setup.py delete mode 100644 chef/cookbooks/python/src/15/sample.c delete mode 100644 chef/cookbooks/python/src/15/sample.h delete mode 100644 chef/cookbooks/python/src/15/turning_a_function_pointer_into_a_callable/example.py delete mode 100644 chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/example.py delete mode 100644 chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/sample.pyx delete mode 100644 chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/setup.py delete mode 100644 chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/example.py delete mode 100644 chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/sample.c delete mode 100644 chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/setup.py delete mode 100644 chef/cookbooks/python/src/15/wrapping_c_code_with_swig/example.py delete mode 100644 chef/cookbooks/python/src/15/wrapping_c_code_with_swig/sample.i delete mode 100644 chef/cookbooks/python/src/15/wrapping_c_code_with_swig/setup.py delete mode 100644 chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/csample.pxd delete mode 100644 chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/example.py delete mode 100644 chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample.pyx delete mode 100644 chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample_alt.pyx delete mode 100644 chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup.py delete mode 100644 chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup_alt.py delete mode 100644 chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/example.py delete mode 100644 chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/pysample.c delete mode 100644 chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/setup.py delete mode 100644 chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/example.py delete mode 100644 chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/pysample.c delete mode 100644 chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/setup.py delete mode 100644 chef/cookbooks/python/src/2/combining_and_concatenating_strings/example.py delete mode 100644 chef/cookbooks/python/src/2/matching_and_searching_for_text_patterns_using_regular_expressions/example.py delete mode 100644 chef/cookbooks/python/src/2/matching_strings_using_shell_wildcard_patterns/example.py delete mode 100644 chef/cookbooks/python/src/2/normalizing_unicode_text_to_a_standard_representation/example.py delete mode 100644 chef/cookbooks/python/src/2/reformatting_text_to_fixed_number_of_columns/example.py delete mode 100644 chef/cookbooks/python/src/2/sanitizing_and_cleaning_up_text/example.py delete mode 100644 chef/cookbooks/python/src/2/searching_and_replacing_text/example.py delete mode 100644 chef/cookbooks/python/src/2/specifying_a_regular_expression_for_the_shortest_match/example.py delete mode 100644 chef/cookbooks/python/src/2/splitting_strings_on_any_of_multiple_delimiters/example.py delete mode 100644 chef/cookbooks/python/src/2/tokenizing_text/example.py delete mode 100644 chef/cookbooks/python/src/2/variable_interpolation_in_strings/example.py delete mode 100644 chef/cookbooks/python/src/2/writing_a_regular_expression_for_multiline_patterns/example.py delete mode 100644 chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/example.py delete mode 100644 chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/plyexample.py delete mode 100644 chef/cookbooks/python/src/3/determining_last_fridays_date/example.py delete mode 100644 chef/cookbooks/python/src/3/finding_the_date_range_for_the_current_month/example.py delete mode 100644 chef/cookbooks/python/src/4/creating_data_processing_pipelines/example.py delete mode 100644 chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log delete mode 100644 chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0108.bz2 delete mode 100644 chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0208.bz2 delete mode 100644 chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log delete mode 100644 chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log-0108.gz delete mode 100644 chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log-0208.gz delete mode 100644 chef/cookbooks/python/src/4/creating_new_iteration_patterns_with_generators/example.py delete mode 100644 chef/cookbooks/python/src/4/delegating-iteration/example.py delete mode 100644 chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/example.py delete mode 100644 chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/hardexample.py delete mode 100644 chef/cookbooks/python/src/4/generators_with_state/example.py delete mode 100644 chef/cookbooks/python/src/4/generators_with_state/somefile.txt delete mode 100644 chef/cookbooks/python/src/4/how_to_flatten_a_nested_sequence/example.py delete mode 100644 chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/example.py delete mode 100644 chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/sample.dat delete mode 100644 chef/cookbooks/python/src/4/iterating_in_reverse/example.py delete mode 100644 chef/cookbooks/python/src/4/iterating_in_sorted_order_over_merged_sorted_iterables/example.py delete mode 100644 chef/cookbooks/python/src/4/iterating_on_items_in_separate_containers/example.py delete mode 100644 chef/cookbooks/python/src/5/adding_or_changing_the_encoding_of_an_already_open_file/example.py delete mode 100644 chef/cookbooks/python/src/5/getting_a_directory_listing/example.py delete mode 100644 chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/data.bin delete mode 100644 chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/example.py delete mode 100644 chef/cookbooks/python/src/5/reading_and_writing_text_data/example.py delete mode 100644 chef/cookbooks/python/src/5/reading_and_writing_text_data/sample.txt delete mode 100644 chef/cookbooks/python/src/5/wrapping_an_existing_file_descriptor_as_a_file_object/echo.py delete mode 100644 chef/cookbooks/python/src/5/writing_bytes_to_a_text_file/example.py delete mode 100644 chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/example.py delete mode 100644 chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/potholes.xml delete mode 100644 chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/example.py delete mode 100644 chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/pred.xml delete mode 100644 chef/cookbooks/python/src/6/parsing_simple_xml_data/example.py delete mode 100644 chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/example.py delete mode 100644 chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/sample.xml delete mode 100644 chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/readrecords.py delete mode 100644 chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/unpackrecords.py delete mode 100644 chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/writerecords.py delete mode 100644 chef/cookbooks/python/src/6/reading_and_writing_csv_data/example.py delete mode 100644 chef/cookbooks/python/src/6/reading_and_writing_csv_data/stocks.csv delete mode 100644 chef/cookbooks/python/src/6/reading_and_writing_csv_data/stocks.tsv delete mode 100644 chef/cookbooks/python/src/6/reading_and_writing_json_data/example.py delete mode 100644 chef/cookbooks/python/src/6/reading_nested_and_variable_sized_binary_structures/example1.py delete mode 100644 chef/cookbooks/python/src/6/reading_nested_and_variable_sized_binary_structures/example2.py delete mode 100644 chef/cookbooks/python/src/6/reading_nested_and_variable_sized_binary_structures/example3.py delete mode 100644 chef/cookbooks/python/src/6/reading_nested_and_variable_sized_binary_structures/example4.py delete mode 100644 chef/cookbooks/python/src/6/reading_nested_and_variable_sized_binary_structures/writepolys.py delete mode 100644 chef/cookbooks/python/src/7/accessing_variables_defined_inside_a_closure/example1.py delete mode 100644 chef/cookbooks/python/src/7/accessing_variables_defined_inside_a_closure/example2.py delete mode 100644 chef/cookbooks/python/src/7/accessing_variables_defined_inside_a_closure/example3.py delete mode 100644 chef/cookbooks/python/src/7/carrying_extra_state_with_callback_functions/example.py delete mode 100644 chef/cookbooks/python/src/7/functions_that_accept_any_number_of_arguments/example.py delete mode 100644 chef/cookbooks/python/src/7/functions_that_only_accept_keyword_arguments/example.py delete mode 100644 chef/cookbooks/python/src/7/functions_with_default_arguments/example.py delete mode 100644 chef/cookbooks/python/src/7/inlining_callback_functions/example.py delete mode 100644 chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example1.py delete mode 100644 chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example2.py delete mode 100644 chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example3.py delete mode 100644 chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example1.py delete mode 100644 chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example2.py delete mode 100644 chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example3.py delete mode 100644 chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example4.py delete mode 100644 chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example5.py delete mode 100644 chef/cookbooks/python/src/8/calling_a_method_on_an_object_given_the_name_as_a_string/example.py delete mode 100644 chef/cookbooks/python/src/8/changing_the_string_representation_of_instances/example.py delete mode 100644 chef/cookbooks/python/src/8/creating_a_new_kind_of_class_or_instance_attribute/example1.py delete mode 100644 chef/cookbooks/python/src/8/creating_an_instance_without_invoking_init/example.py delete mode 100644 chef/cookbooks/python/test/cookbooks/python_test/README.md delete mode 100644 chef/cookbooks/python/test/cookbooks/python_test/files/default/tests/minitest/cook-3084_test.rb delete mode 100644 chef/cookbooks/python/test/cookbooks/python_test/metadata.rb delete mode 100644 chef/cookbooks/python/test/cookbooks/python_test/recipes/test_exert.rb delete mode 100644 chef/cookbooks/python/test/cookbooks/python_test/recipes/test_virtualenv.rb delete mode 100644 chef/cookbooks/python/test/integration/exert/bats/exert.bats delete mode 100644 chef/cookbooks/python/test/integration/source/bats/source.bats delete mode 100644 chef/cookbooks/python/test/integration/virtualenv/bats/virtualenv.bats delete mode 100644 chef/cookbooks/rabbitmq/.kitchen.yml delete mode 100644 chef/cookbooks/rabbitmq/Berksfile delete mode 100644 chef/cookbooks/rabbitmq/CONTRIBUTING delete mode 100644 chef/cookbooks/rabbitmq/LICENSE delete mode 100644 chef/cookbooks/rabbitmq/TESTING.md create mode 100644 chef/cookbooks/rabbitmq/libraries/default.rb create mode 100644 chef/cookbooks/rabbitmq/libraries/matchers.rb create mode 100644 chef/cookbooks/rabbitmq/metadata.json delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/README.md delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1684_test.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1724_test.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-2151-3489_test.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/default_test.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/lwrps_test.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/mgmt_console_test.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/ssl_test.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/support/helpers.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/metadata.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cluster.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cook-2151-3489.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/default.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/lwrps.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/mgmt_console.rb delete mode 100644 chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/ssl.rb delete mode 100644 chef/cookbooks/runit/.kitchen.yml delete mode 100644 chef/cookbooks/runit/Berksfile delete mode 100644 chef/cookbooks/runit/CONTRIBUTING.md delete mode 100644 chef/cookbooks/runit/Gemfile delete mode 100644 chef/cookbooks/runit/LICENSE delete mode 100644 chef/cookbooks/runit/Rakefile delete mode 100644 chef/cookbooks/runit/TESTING.md create mode 100644 chef/cookbooks/runit/metadata.json delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit-other_test/README.md delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit-other_test/metadata.rb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit-other_test/recipes/default.rb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/README.md delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/default_test.rb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/service_test.rb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/support/helpers.rb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/metadata.rb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/recipes/default.rb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/recipes/service.rb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-calabash-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-u.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-cook-2867-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-default-svlog-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-finish.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-no-svlog-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-finish.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-log-run.erb delete mode 100644 chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-run.erb delete mode 100644 chef/cookbooks/runit/test/spec/libraries/provider_runit_service_spec.rb delete mode 100644 chef/cookbooks/runit/test/spec/libraries/resource_runit_service_spec.rb delete mode 100644 chef/cookbooks/runit/test/spec/spec_helper.rb delete mode 100644 chef/cookbooks/selinux/CONTRIBUTING delete mode 100644 chef/cookbooks/selinux/LICENSE create mode 100644 chef/cookbooks/selinux/attributes/default.rb create mode 100644 chef/cookbooks/selinux/libraries/selinux_service_helpers.rb create mode 100644 chef/cookbooks/selinux/metadata.json create mode 100644 chef/cookbooks/selinux/providers/state.rb create mode 100644 chef/cookbooks/selinux/recipes/_common.rb rename chef/cookbooks/{yum/recipes/yum.rb => selinux/resources/state.rb} (78%) create mode 100644 chef/cookbooks/statsd/.foodcritic-rules create mode 100644 chef/cookbooks/statsd/Berksfile create mode 100644 chef/cookbooks/statsd/Berksfile.lock create mode 100644 chef/cookbooks/statsd/Gemfile rename chef/cookbooks/{openstack-metering => statsd}/Gemfile.lock (65%) create mode 100644 chef/cookbooks/statsd/README.md delete mode 100644 chef/cookbooks/statsd/README.rdoc create mode 100644 chef/cookbooks/statsd/attributes/default.rb delete mode 100644 chef/cookbooks/statsd/attributes/statsd.rb delete mode 100644 chef/cookbooks/statsd/files/default/statsd create mode 100755 chef/cookbooks/statsd/files/default/upstart.start create mode 100644 chef/cookbooks/statsd/recipes/server.rb create mode 100644 chef/cookbooks/statsd/templates/default/changelog.erb delete mode 100644 chef/cookbooks/statsd/templates/default/config.js.erb create mode 100644 chef/cookbooks/statsd/templates/default/localConfig.js.erb delete mode 100644 chef/cookbooks/windows/CONTRIBUTING delete mode 100644 chef/cookbooks/windows/LICENSE create mode 100644 chef/cookbooks/windows/libraries/matchers.rb create mode 100644 chef/cookbooks/windows/libraries/powershell_helper.rb create mode 100644 chef/cookbooks/windows/libraries/powershell_out.rb create mode 100644 chef/cookbooks/windows/libraries/windows_architecture_helper.rb rename chef/cookbooks/windows/libraries/{helper.rb => windows_helper.rb} (94%) create mode 100644 chef/cookbooks/windows/metadata.json create mode 100644 chef/cookbooks/windows/providers/feature_powershell.rb delete mode 100644 chef/cookbooks/xfs/.kitchen.yml delete mode 100644 chef/cookbooks/xfs/TESTING.md create mode 100644 chef/cookbooks/xfs/metadata.json create mode 100644 chef/cookbooks/yum-epel/CHANGELOG.md create mode 100644 chef/cookbooks/yum-epel/README.md create mode 100644 chef/cookbooks/yum-epel/attributes/epel-debuginfo.rb create mode 100644 chef/cookbooks/yum-epel/attributes/epel-source.rb create mode 100644 chef/cookbooks/yum-epel/attributes/epel-testing-debuginfo.rb create mode 100644 chef/cookbooks/yum-epel/attributes/epel-testing-source.rb create mode 100644 chef/cookbooks/yum-epel/attributes/epel-testing.rb create mode 100644 chef/cookbooks/yum-epel/attributes/epel.rb create mode 100644 chef/cookbooks/yum-epel/metadata.json create mode 100644 chef/cookbooks/yum-epel/metadata.rb create mode 100644 chef/cookbooks/yum-epel/recipes/default.rb create mode 100644 chef/cookbooks/yum-epel/templates/default/epel.repo.erb create mode 100644 chef/cookbooks/yum-erlang_solutions/CHANGELOG.md create mode 100644 chef/cookbooks/yum-erlang_solutions/README.md create mode 100644 chef/cookbooks/yum-erlang_solutions/attributes/erlang_solutions.rb create mode 100644 chef/cookbooks/yum-erlang_solutions/metadata.json create mode 100644 chef/cookbooks/yum-erlang_solutions/metadata.rb create mode 100644 chef/cookbooks/yum-erlang_solutions/recipes/default.rb delete mode 100644 chef/cookbooks/yum/.kitchen.yml delete mode 100644 chef/cookbooks/yum/Berksfile delete mode 100644 chef/cookbooks/yum/CONTRIBUTING.md delete mode 100644 chef/cookbooks/yum/LICENSE delete mode 100644 chef/cookbooks/yum/attributes/default.rb delete mode 100644 chef/cookbooks/yum/attributes/elrepo.rb delete mode 100644 chef/cookbooks/yum/attributes/epel.rb delete mode 100644 chef/cookbooks/yum/attributes/remi.rb delete mode 100644 chef/cookbooks/yum/files/default/RPM-GPG-KEY-EPEL-6 delete mode 100644 chef/cookbooks/yum/files/default/tests/minitest/support/helpers.rb delete mode 100644 chef/cookbooks/yum/files/default/tests/minitest/test_test.rb delete mode 100644 chef/cookbooks/yum/providers/key.rb delete mode 100644 chef/cookbooks/yum/recipes/elrepo.rb delete mode 100644 chef/cookbooks/yum/recipes/epel.rb delete mode 100644 chef/cookbooks/yum/recipes/ius.rb delete mode 100644 chef/cookbooks/yum/recipes/remi.rb delete mode 100644 chef/cookbooks/yum/recipes/repoforge.rb delete mode 100644 chef/cookbooks/yum/recipes/test.rb create mode 100644 chef/cookbooks/yum/templates/default/CentOS-Base.repo.erb delete mode 100644 chef/cookbooks/yum/templates/default/yum-rhel5.conf.erb delete mode 100644 chef/cookbooks/yum/templates/default/yum-rhel6.conf.erb create mode 100644 chef/roles/allinone-compute.json delete mode 100644 chef/roles/allinone-compute.rb create mode 100644 chef/roles/ceph-mds.json create mode 100644 chef/roles/ceph-mon.json create mode 100644 chef/roles/ceph-os-mon.json create mode 100644 chef/roles/ceph-os-radosgw.json create mode 100644 chef/roles/ceph-osd.json create mode 100644 chef/roles/ceph-radosgw.json create mode 100644 chef/roles/ceph-tgt.json create mode 100644 chef/roles/compass-base.json create mode 100644 chef/roles/os-base.json delete mode 100644 chef/roles/os-base.rb create mode 100644 chef/roles/os-block-storage-api.json delete mode 100644 chef/roles/os-block-storage-api.rb create mode 100644 chef/roles/os-block-storage-controller.json create mode 100644 chef/roles/os-block-storage-scheduler.json delete mode 100644 chef/roles/os-block-storage-scheduler.rb create mode 100644 chef/roles/os-block-storage-volume.json delete mode 100644 chef/roles/os-block-storage-worker.rb create mode 100644 chef/roles/os-block-storage.json delete mode 100644 chef/roles/os-block-storage.rb create mode 100644 chef/roles/os-ceph-block-storage-controller.json create mode 100644 chef/roles/os-ceph-compute-worker.json create mode 100644 chef/roles/os-ceph-controller.json create mode 100644 chef/roles/os-ceph-image.json create mode 100644 chef/roles/os-client.json create mode 100644 chef/roles/os-compute-api-ec2.json delete mode 100644 chef/roles/os-compute-api-ec2.rb create mode 100644 chef/roles/os-compute-api-metadata.json delete mode 100644 chef/roles/os-compute-api-metadata.rb create mode 100644 chef/roles/os-compute-api-os-compute.json delete mode 100644 chef/roles/os-compute-api-os-compute.rb create mode 100644 chef/roles/os-compute-api.json delete mode 100644 chef/roles/os-compute-api.rb create mode 100644 chef/roles/os-compute-cert.json delete mode 100644 chef/roles/os-compute-cert.rb create mode 100644 chef/roles/os-compute-conductor.json create mode 100644 chef/roles/os-compute-controller.json delete mode 100644 chef/roles/os-compute-controller.rb create mode 100644 chef/roles/os-compute-scheduler.json delete mode 100644 chef/roles/os-compute-scheduler.rb create mode 100644 chef/roles/os-compute-setup.json create mode 100644 chef/roles/os-compute-single-controller-no-network.json create mode 100644 chef/roles/os-compute-single-controller.json delete mode 100644 chef/roles/os-compute-single-controller.rb create mode 100644 chef/roles/os-compute-vncproxy.json delete mode 100644 chef/roles/os-compute-vncproxy.rb create mode 100644 chef/roles/os-compute-worker.json delete mode 100644 chef/roles/os-compute-worker.rb create mode 100644 chef/roles/os-controller.json delete mode 100644 chef/roles/os-controller.rb create mode 100644 chef/roles/os-dashboard.json delete mode 100644 chef/roles/os-dashboard.rb create mode 100644 chef/roles/os-ha.json delete mode 100644 chef/roles/os-ha.rb delete mode 100644 chef/roles/os-identity-api-admin.rb delete mode 100644 chef/roles/os-identity-api.rb create mode 100644 chef/roles/os-identity.json delete mode 100644 chef/roles/os-identity.rb create mode 100644 chef/roles/os-image-api.json delete mode 100644 chef/roles/os-image-api.rb create mode 100644 chef/roles/os-image-registry.json delete mode 100644 chef/roles/os-image-registry.rb create mode 100644 chef/roles/os-image-upload.json create mode 100644 chef/roles/os-image.json delete mode 100644 chef/roles/os-image.rb delete mode 100644 chef/roles/os-infra-caching.rb create mode 100644 chef/roles/os-network-dhcp-agent.json create mode 100644 chef/roles/os-network-l3-agent.json create mode 100644 chef/roles/os-network-metadata-agent.json create mode 100644 chef/roles/os-network-openvswitch.json create mode 100644 chef/roles/os-network-server.json delete mode 100644 chef/roles/os-network-server.rb create mode 100644 chef/roles/os-network-worker.json create mode 100644 chef/roles/os-network.json delete mode 100644 chef/roles/os-network.rb create mode 100644 chef/roles/os-object-storage-account.json delete mode 100644 chef/roles/os-object-storage-account.rb create mode 100644 chef/roles/os-object-storage-container.json delete mode 100644 chef/roles/os-object-storage-container.rb create mode 100644 chef/roles/os-object-storage-management.json delete mode 100644 chef/roles/os-object-storage-management.rb create mode 100644 chef/roles/os-object-storage-object.json delete mode 100644 chef/roles/os-object-storage-object.rb create mode 100644 chef/roles/os-object-storage-proxy.json delete mode 100644 chef/roles/os-object-storage-proxy.rb create mode 100644 chef/roles/os-object-storage.json delete mode 100644 chef/roles/os-object-storage.rb create mode 100644 chef/roles/os-ops-caching.json create mode 100644 chef/roles/os-ops-database.json delete mode 100644 chef/roles/os-ops-database.rb create mode 100644 chef/roles/os-ops-messaging.json delete mode 100644 chef/roles/os-ops-messaging.rb create mode 100644 chef/roles/os-orchestration-api-cfn.json create mode 100644 chef/roles/os-orchestration-api-cloudwatch.json create mode 100644 chef/roles/os-orchestration-api.json create mode 100644 chef/roles/os-orchestration-engine.json create mode 100644 chef/roles/os-orchestration.json delete mode 100644 chef/roles/os-single-controller.rb create mode 100644 chef/roles/os-telemetry-agent-central.json create mode 100644 chef/roles/os-telemetry-agent-compute.json create mode 100644 chef/roles/os-telemetry-agent-notification.json create mode 100644 chef/roles/os-telemetry-alarm-evaluator.json create mode 100644 chef/roles/os-telemetry-alarm-notifier.json create mode 100644 chef/roles/os-telemetry-api.json create mode 100644 chef/roles/os-telemetry-collector.json create mode 100644 chef/roles/os-telemetry.json delete mode 100644 chef/roles/test-synclog.rb create mode 100644 cobbler/snippets/kickstart_chef-admin.pem delete mode 100644 cobbler/snippets/kickstart_chef_firstrun.sh delete mode 100644 cobbler/snippets/kickstart_chef_rerun.sh delete mode 100644 cobbler/snippets/kickstart_chef_rsyslog.conf create mode 100644 cobbler/snippets/kickstart_chef_run.sh create mode 100644 cobbler/snippets/kickstart_knife.rb create mode 100644 cobbler/snippets/kickstart_local_repo diff --git a/chef/cookbooks/apache2/CHANGELOG.md b/chef/cookbooks/apache2/CHANGELOG.md index f574307..67329ee 100644 --- a/chef/cookbooks/apache2/CHANGELOG.md +++ b/chef/cookbooks/apache2/CHANGELOG.md @@ -1,5 +1,99 @@ -## v1.7.0: +apache2 Cookbook Changelog +========================== +This file is used to list changes made in each version of the apache2 cookbook. +v1.9.6 (2014-02-28) +------------------- +[COOK-4391] - uncommenting the PIDFILE line + + +v1.9.4 (2014-02-27) +------------------- +Bumping version for toolchain + + +v1.9.1 (2014-02-27) +------------------- +[COOK-4348] Allow arbitrary params in sysconfig + + +v1.9.0 (2014-02-21) +------------------- +### Improvement +- **[COOK-4076](https://tickets.opscode.com/browse/COOK-4076)** - foodcritic: dependencies are not defined properly +- **[COOK-2572](https://tickets.opscode.com/browse/COOK-2572)** - Add mod_pagespeed recipe to apache2 + +### Bug +- **[COOK-4043](https://tickets.opscode.com/browse/COOK-4043)** - apache2 cookbook does not depend on 'iptables' +- **[COOK-3919](https://tickets.opscode.com/browse/COOK-3919)** - Move the default pidfile for apache2 on Ubuntu 13.10 or greater +- **[COOK-3863](https://tickets.opscode.com/browse/COOK-3863)** - Add recipe for mod_jk +- **[COOK-3804](https://tickets.opscode.com/browse/COOK-3804)** - Fix incorrect datatype for apache/default_modules, use recipes option in metadata +- **[COOK-3800](https://tickets.opscode.com/browse/COOK-3800)** - Cannot load modules that use non-standard module identifiers +- **[COOK-1689](https://tickets.opscode.com/browse/COOK-1689)** - The perl package name should be configurable + + +v1.8.14 +------- +Version bump for toolchain sanity + + +v1.8.12 +------- +Fixing various style issues for travis + + +v1.8.10 +------- +fixing metadata version error. locking to 3.0" + + +v1.8.8 +------ +Version bump for toolchain sanity + + +v1.8.6 +------ +Locking yum dependency to '< 3' + + +v1.8.4 +------ +### Bug +- **[COOK-3769](https://tickets.opscode.com/browse/COOK-3769)** - Fix a critical bug where the `apache_module` could not enable modules + + +v1.8.2 +------ +### Bug +- **[COOK-3766](https://tickets.opscode.com/browse/COOK-3766)** - Fix an issue where the `mod_ssl` recipe fails due to a missing attribute + + +v1.8.0 +------ +### Bug +- **[COOK-3680](https://tickets.opscode.com/browse/COOK-3680)** - Update template paths +- **[COOK-3570](https://tickets.opscode.com/browse/COOK-3570)** - Apache cookbook breaks on RHEL / CentOS 6 +- **[COOK-2944](https://tickets.opscode.com/browse/COOK-2944)** - Fix foodcritic failures +- **[COOK-2893](https://tickets.opscode.com/browse/COOK-2893)** - Improve mod_auth_openid recipe with guards and idempotency +- **[COOK-2758](https://tickets.opscode.com/browse/COOK-2758)** - Fix use of non-existent attribute + +### New Feature +- **[COOK-3665](https://tickets.opscode.com/browse/COOK-3665)** - Add recipe for mod_userdir +- **[COOK-3646](https://tickets.opscode.com/browse/COOK-3646)** - Add recipe for mod_cloudflare +- **[COOK-3213](https://tickets.opscode.com/browse/COOK-3213)** - Add recipe for mod_info + +### Improvement +- **[COOK-3656](https://tickets.opscode.com/browse/COOK-3656)** - Parameterize apache2 binary +- **[COOK-3562](https://tickets.opscode.com/browse/COOK-3562)** - Allow mod_proxy settings to be configured as attributes +- **[COOK-3326](https://tickets.opscode.com/browse/COOK-3326)** - Fix default_test to use ServerTokens attribute +- **[COOK-2635](https://tickets.opscode.com/browse/COOK-2635)** - Add support for SVG mime types +- **[COOK-2598](https://tickets.opscode.com/browse/COOK-2598)** - FastCGI Module only works on Debian-based platforms +- **[COOK-1984](https://tickets.opscode.com/browse/COOK-1984)** - Add option to configure the address apache listens to + + +v1.7.0 +------ ### Improvement - [COOK-3073]: make access.log location configurable per-platform @@ -11,173 +105,149 @@ - [COOK-3184]: Add `mod_filter` recipe to Apache2-cookbook - [COOK-3236]: Add `mod_action` recipe to Apache2-cookbook -## v1.6.6: - +v1.6.6 +------ 1.6.4 had a missed step in the automated release, long live 1.6.6. ### Bug -- [COOK-3018]: apache2_module does duplicate delayed restart of - apache2 service when conf = true -- [COOK-3027]: Default site enable true, then false, does not disable - default site +- [COOK-3018]: apache2_module does duplicate delayed restart of apache2 service when conf = true +- [COOK-3027]: Default site enable true, then false, does not disable default site - [COOK-3109]: fix apache lib_dir arch attribute regexp -## v1.6.2 +v1.6.2 +------ +- [COOK-2535] - `mod_auth_openid` requires libtool to run autogen.sh +- [COOK-2667] - Typo in usage documentation +- [COOK-2461] - `apache2::mod_auth_openid` fails on some ubuntu systems +- [COOK-2720] - Apache2 minitest helper function `ran_recipe` is not portable -* [COOK-2535] - `mod_auth_openid` requires libtool to run autogen.sh -* [COOK-2667] - Typo in usage documentation -* [COOK-2461] - `apache2::mod_auth_openid` fails on some ubuntu systems -* [COOK-2720] - Apache2 minitest helper function `ran_recipe` is not - portable +v1.6.0 +------ +- [COOK-2372] - apache2 mpm_worker: add ServerLimit attribute (default to 16) -## v1.6.0: +v1.5.0 +------ +The `mod_auth_openid` attributes are changed. The upstream maintainer deprecated the older release versions, and the source repository has releases available at specific SHA1SUM references. The new attribute, `node['apache']['mod_auth_openid']['ref']` is used to set this. -* [COOK-2372] - apache2 mpm_worker: add ServerLimit attribute (default - to 16) +- [COOK-2198] - `apache::mod_auth_openid` compiles from source, but does not install make on debian/ubuntu +- [COOK-2224] - version conflict between cucumber and other gems +- [COOK-2248] - `apache2::mod_php5` uses `not_if` "which php" without ensuring package 'which' is installed +- [COOK-2269] - Set allow list for mod_status incase external monitor scripts need +- [COOK-2276] - cookbook apache2 documentation regarding listening ports doesn't match default attributes +- [COOK-2296] - `mod_auth_openid` doesn't have tags/releases for the version I need for features and fixes +- [COOK-2323] - Add Oracle linux support -## v1.5.0: +v1.4.2 +------ +- [COOK-1721] - fix logrotate recipe -**NOTE** The `mod_auth_openid` attributes are changed. The upstream - maintainer deprecated the older release versions, and the source - repository has releases available at specific SHA1SUM references. - The new attribute, `node['apache']['mod_auth_openid']['ref']` is - used to set this. +v1.4.0 +------ +- [COOK-1456] - iptables enhancements +- [COOK-1473] - apache2 does not disable default site when setting "`default_site_enabled`" back to false +- [COOK-1824] - the apache2 cookbook needs to specify which binary is used on rhel platform +- [COOK-1916] - Download location wrong for apache2 `mod_auth_openid` >= 0.7 +- [COOK-1917] - Improve `mod_auth_openid` recipe to handle module upgrade more gracefully +- [COOK-2029] - apache2 restarts on every run on RHEL and friends, generate-module-list on every run. +- [COOK-2036] - apache2: Cookbook style -* [COOK-2198] - `apache::mod_auth_openid` compiles from source, but - does not install make on debian/ubuntu -* [COOK-2224] - version conflict between cucumber and other gems -* [COOK-2248] - `apache2::mod_php5` uses `not_if` "which php" without - ensuring package 'which' is installed -* [COOK-2269] - Set allow list for mod_status incase external monitor scripts need -* [COOK-2276] - cookbook apache2 documentation regarding listening - ports doesn't match default attributes -* [COOK-2296] - `mod_auth_openid` doesn't have tags/releases for the - version I need for features and fixes -* [COOK-2323] - Add Oracle linux support +v1.3.2 +------ +- [COOK-1804] - fix `web_app` definition parameter so site can be disabled. -## v1.4.2: +v1.3.0 +------ +- [COOK-1738] - Better configuration for `mod_include` and some overrides in `web_app` definition +- [COOK-1470] - Change SSL Ciphers to Mitigate BEAST attack -* [COOK-1721] - fix logrotate recipe - -## v1.4.0: - -* [COOK-1456] - iptables enhancements -* [COOK-1473] - apache2 does not disable default site when setting - "`default_site_enabled`" back to false -* [COOK-1824] - the apache2 cookbook needs to specify which binary is - used on rhel platform -* [COOK-1916] - Download location wrong for apache2 `mod_auth_openid` - >= 0.7 -* [COOK-1917] - Improve `mod_auth_openid` recipe to handle module - upgrade more gracefully -* [COOK-2029] - apache2 restarts on every run on RHEL and friends, - generate-module-list on every run. -* [COOK-2036] - apache2: Cookbook style - -## v1.3.2: - -* [COOK-1804] - fix `web_app` definition parameter so site can be - disabled. - -## v1.3.0: - -* [COOK-1738] - Better configuration for `mod_include` and some - overrides in `web_app` definition -* [COOK-1470] - Change SSL Ciphers to Mitigate BEAST attack - -## v1.2.0: - -* [COOK-692] - delete package conf.d files in module recipes, for EL -* [COOK-1693] - Foodcritic finding for unnecessary string interpolation -* [COOK-1757] - platform_family and better style / usage practices - -## v1.1.16: +v1.2.0 +------ +- [COOK-692] - delete package conf.d files in module recipes, for EL +- [COOK-1693] - Foodcritic finding for unnecessary string interpolation +- [COOK-1757] - platform_family and better style / usage practices +v1.1.16 +------- re-releasing as .16 due to error on tag 1.1.14 -* [COOK-1466] - add `mod_auth_cas` recipe -* [COOK-1609] - apache2 changes ports.conf twice per run when using - apache2::mod_ssl +- [COOK-1466] - add `mod_auth_cas` recipe +- [COOK-1609] - apache2 changes ports.conf twice per run when using apache2::mod_ssl -## v1.1.12: +v1.1.12 +------- +- [COOK-1436] - restore apache2 web_app definition +- [COOK-1356] - allow ExtendedStatus via attribute +- [COOK-1403] - add mod_fastcgi recipe -* [COOK-1436] - restore apache2 web_app definition -* [COOK-1356] - allow ExtendedStatus via attribute -* [COOK-1403] - add mod_fastcgi recipe +v1.1.10 +------- +- [COOK-1315] - allow the default site to not be enabled +- [COOK-1328] - cookbook tests (minitest, cucumber) -## v1.1.10: - -* [COOK-1315] - allow the default site to not be enabled -* [COOK-1328] - cookbook tests (minitest, cucumber) - -## v1.1.8: - -* Some platforms with minimal installations that don't have perl won't - have a `node['languages']['perl']` attribute, so remove the - conditional and rely on the power of idempotence in the package - resource. -* [COOK-1214] - address foodcritic warnings -* [COOK-1180] - add `mod_logio` and fix `mod_proxy` - -## v1.1.6: +v1.1.8 +------ +- Some platforms with minimal installations that don't have perl won't have a `node['languages']['perl']` attribute, so remove the conditional and rely on the power of idempotence in the package resource. +- [COOK-1214] - address foodcritic warnings +- [COOK-1180] - add `mod_logio` and fix `mod_proxy` +v1.1.6 +------ FreeBSD users: This release requires the `freebsd` cookbook. See README.md. -* [COOK-1025] - freebsd support in mod_php5 recipe +- [COOK-1025] - freebsd support in mod_php5 recipe -## v1.1.4: +v1.1.4 +------ +- [COOK-1100] - support amazon linux -* [COOK-1100] - support amazon linux +v1.1.2 +------ +- [COOK-996] - apache2::mod_php5 can cause PHP and module API mismatches +- [COOK-1083] - return string for v_f_p and use correct value for default -## v1.1.2: +v1.1.0 +------ +- [COOK-861] - Add `mod_perl` and apreq2 +- [COOK-941] - fix `mod_auth_openid` on FreeBSD +- [COOK-1021] - add a commented-out LoadModule directive to keep apxs happy +- [COOK-1022] - consistency for icondir attribute +- [COOK-1023] - fix platform test for attributes +- [COOK-1024] - fix a2enmod script so it runs cleanly on !bash +- [COOK-1026] - fix `error_log` location on FreeBSD -* [COOK-996] - apache2::mod_php5 can cause PHP and module API mismatches -* [COOK-1083] - return string for v_f_p and use correct value for - default +v1.0.8 +------ +- COOK-548 - directory resource doesn't have backup parameter -## v1.1.0: +v1.0.6 +------ +- COOK-915 - update to `mod_auth_openid` version 0.6, see __Recipes/mod_auth_openid__ below. +- COOK-548 - Add support for FreeBSD. -* [COOK-861] - Add `mod_perl` and apreq2 -* [COOK-941] - fix `mod_auth_openid` on FreeBSD -* [COOK-1021] - add a commented-out LoadModule directive to keep apxs happy -* [COOK-1022] - consistency for icondir attribute -* [COOK-1023] - fix platform test for attributes -* [COOK-1024] - fix a2enmod script so it runs cleanly on !bash -* [COOK-1026] - fix `error_log` location on FreeBSD +v1.0.4 +------ +- COOK-859 - don't hardcode module paths -## v1.0.8: +v1.0.2 +------ +- Tickets resolved in this release: COOK-788, COOK-782, COOK-780 -* COOK-548 - directory resource doesn't have backup parameter - -## v1.0.6: - -* COOK-915 - update to `mod_auth_openid` version 0.6, see __Recipes/mod_auth_openid__ below. -* COOK-548 - Add support for FreeBSD. - -## v1.0.4: - -* COOK-859 - don't hardcode module paths - -## v1.0.2 - -* Tickets resolved in this release: COOK-788, COOK-782, COOK-780 - -## v1.0.0 - -* Red Hat family support is greatly improved, all recipes except `god_monitor` converge. -* Recipe `mod_auth_openid` now works on RHEL family distros -* Recipe `mod_php5` will now remove config from package on RHEL family so it doesn't conflict with the cookbook's. -* Added `php5.conf.erb` template for `mod_php5` recipe. -* Create the run state directory for `mod_fcgid` to prevent a startup error on RHEL version 6. -* New attribute `node['apache']['lib_dir']` to handle lib vs lib64 on RHEL family distributions. -* New attribute `node['apache']['group']`. -* Scientific Linux support added. -* Use a file resource instead of the generate-module-list executed perl script on RHEL family. -* "default" site can now be disabled. -* web_app now has an "enable" parameter. -* Support for dav_fs apache module. -* Tickets resolved in this release: COOK-754, COOK-753, COOK-665, COOK-624, COOK-579, COOK-519, COOK-518 -* Fix node references in template for a2dissite -* Use proper user and group attributes on files and templates. -* Replace the anemic README.rdoc with this new and improved superpowered README.md :). +v1.0.0 +------ +- Red Hat family support is greatly improved, all recipes except `god_monitor` converge. +- Recipe `mod_auth_openid` now works on RHEL family distros +- Recipe `mod_php5` will now remove config from package on RHEL family so it doesn't conflict with the cookbook's. +- Added `php5.conf.erb` template for `mod_php5` recipe. +- Create the run state directory for `mod_fcgid` to prevent a startup error on RHEL version 6. +- New attribute `node['apache']['lib_dir']` to handle lib vs lib64 on RHEL family distributions. +- New attribute `node['apache']['group']`. +- Scientific Linux support added. +- Use a file resource instead of the generate-module-list executed perl script on RHEL family. +- "default" site can now be disabled. +- web_app now has an "enable" parameter. +- Support for dav_fs apache module. +- Tickets resolved in this release: COOK-754, COOK-753, COOK-665, COOK-624, COOK-579, COOK-519, COOK-518 +- Fix node references in template for a2dissite +- Use proper user and group attributes on files and templates. +- Replace the anemic README.rdoc with this new and improved superpowered README.md :). diff --git a/chef/cookbooks/apache2/Gemfile b/chef/cookbooks/apache2/Gemfile deleted file mode 100644 index 9dad052..0000000 --- a/chef/cookbooks/apache2/Gemfile +++ /dev/null @@ -1,10 +0,0 @@ -source "https://rubygems.org" - -gem 'cucumber', '~> 1.2.0' -gem 'httparty', '~> 0.8.3' -gem 'minitest', '~> 3.0.0' -gem 'nokogiri', '~> 1.5.0' - -group :kitchen do - gem 'test-kitchen', '< 1.0' -end diff --git a/chef/cookbooks/apache2/README.md b/chef/cookbooks/apache2/README.md index d594bc1..817ff4f 100644 --- a/chef/cookbooks/apache2/README.md +++ b/chef/cookbooks/apache2/README.md @@ -1,5 +1,7 @@ -Description -=========== +apache2 Cookbook +================ +[![Build Status](https://secure.travis-ci.org/opscode-cookbooks/apache2.png?branch=master)](http://travis-ci.org/opscode-cookbooks/apache2) + This cookbook provides a complete Debian/Ubuntu style Apache HTTPD configuration. Non-Debian based distributions such as Red Hat/CentOS, @@ -150,6 +152,8 @@ attributes are determined based on the node's platform. See the attributes/default.rb file for default values in the case statement at the top of the file. +* `node['apache']['package']` - Package name for Apache2 +* `node['apache']['perl_pkg']` - Package name for Perl * `node['apache']['dir']` - Location for the Apache configuration * `node['apache']['log_dir']` - Location for Apache logs * `node['apache']['error_log']` - Location for the default error log @@ -157,11 +161,13 @@ the top of the file. * `node['apache']['user']` - User Apache runs as * `node['apache']['group']` - Group Apache runs as * `node['apache']['binary']` - Apache httpd server daemon +* `node['apache']['docroot_dir']` - Location for docroot +* `node['apache']['cgibin_dir']` - Location for cgi-bin * `node['apache']['icondir']` - Location for icons * `node['apache']['cache_dir']` - Location for cached files used by Apache itself or recipes * `node['apache']['pid_file']` - Location of the PID file for Apache httpd * `node['apache']['lib_dir']` - Location for shared libraries -* `node['apache']['default_site_enabled']` - Default site enabled. Defaults to true on redhat-family platforms +* `node['apache']['default_site_enabled']` - Default site enabled. Default is false. * `node['apache']['ext_status']` - if true, enables ExtendedStatus for `mod_status` General settings @@ -170,12 +176,14 @@ General settings These are general settings used in recipes and templates. Default values are noted. +* `node['apache']['listen_addresses']` - Addresses that httpd should listen on. Default is any ("*"). * `node['apache']['listen_ports']` - Ports that httpd should listen on. Default is port 80. * `node['apache']['contact']` - Value for ServerAdmin directive. Default "ops@example.com". * `node['apache']['timeout']` - Value for the Timeout directive. Default is 300. * `node['apache']['keepalive']` - Value for the KeepAlive directive. Default is On. * `node['apache']['keepaliverequests']` - Value for MaxKeepAliveRequests. Default is 100. * `node['apache']['keepalivetimeout']` - Value for the KeepAliveTimeout directive. Default is 5. +* `node['apache']['sysconfig_additional_params']` - Additionals variables set in sysconfig file. Default is empty. * `node['apache']['default_modules']` - Array of module names. Can take "mod_FOO" or "FOO" as names, where FOO is the apache module, e.g. "`mod_status`" or "`status`". The modules listed in `default_modules` will be included as recipes in `recipe[apache::default]`. @@ -413,13 +421,14 @@ the definition is used. See __Examples__. ### Parameters: * `name` - Name of the module enabled or disabled with the `a2enmod` or `a2dismod` scripts. +* `identifier` - String to identify the module for the `LoadModule` directive. Not typically needed, defaults to `#{name}_module` * `enable` - Default true, which uses `a2enmod` to enable the module. If false, the module will be disabled with `a2dismod`. * `conf` - Default false. Set to true if the module has a config file, which will use `apache_conf` for the file. * `filename` - specify the full name of the file, e.g. ### Examples: -Enable the ssl module, which also has a configuration template in `templates/default/ssl.conf.erb`. +Enable the ssl module, which also has a configuration template in `templates/default/mods/ssl.conf.erb`. apache_module "ssl" do conf true diff --git a/chef/cookbooks/apache2/attributes/default.rb b/chef/cookbooks/apache2/attributes/default.rb index 770863a..b5df554 100644 --- a/chef/cookbooks/apache2/attributes/default.rb +++ b/chef/cookbooks/apache2/attributes/default.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Attributes:: apache # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,102 +17,106 @@ # limitations under the License. # -default['apache']['root_group'] = "root" +default['apache']['root_group'] = 'root' # Where the various parts of apache are -case platform -when "redhat", "centos", "scientific", "fedora", "suse", "amazon", "oracle" - default['apache']['package'] = "httpd" - default['apache']['dir'] = "/etc/httpd" - default['apache']['log_dir'] = "/var/log/httpd" - default['apache']['error_log'] = "error.log" - default['apache']['access_log'] = "access.log" - default['apache']['user'] = "apache" - default['apache']['group'] = "apache" - default['apache']['binary'] = "/usr/sbin/httpd" - default['apache']['docroot_dir'] = "/var/www/html" - default['apache']['cgibin_dir'] = "/var/www/cgi-bin" - default['apache']['icondir'] = "/var/www/icons" - default['apache']['cache_dir'] = "/var/cache/httpd" - if node['platform_version'].to_f >= 6 then - default['apache']['pid_file'] = "/var/run/httpd/httpd.pid" - else - default['apache']['pid_file'] = "/var/run/httpd.pid" - end - default['apache']['lib_dir'] = node['kernel']['machine'] =~ /^i[36]86$/ ? "/usr/lib/httpd" : "/usr/lib64/httpd" - default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" +case node['platform'] +when 'redhat', 'centos', 'scientific', 'fedora', 'suse', 'amazon', 'oracle' + default['apache']['package'] = 'httpd' + default['apache']['perl_pkg'] = 'perl' + default['apache']['dir'] = '/etc/httpd' + default['apache']['log_dir'] = '/var/log/httpd' + default['apache']['error_log'] = 'error.log' + default['apache']['access_log'] = 'access.log' + default['apache']['user'] = 'apache' + default['apache']['group'] = 'apache' + default['apache']['binary'] = '/usr/sbin/httpd' + default['apache']['docroot_dir'] = '/var/www/html' + default['apache']['cgibin_dir'] = '/var/www/cgi-bin' + default['apache']['icondir'] = '/var/www/icons' + default['apache']['cache_dir'] = '/var/cache/httpd' + default['apache']['pid_file'] = if node['platform_version'].to_f >= 6 + '/var/run/httpd/httpd.pid' + else + '/var/run/httpd.pid' + end + default['apache']['lib_dir'] = node['kernel']['machine'] =~ /^i[36]86$/ ? '/usr/lib/httpd' : '/usr/lib64/httpd' + default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" default['apache']['default_site_enabled'] = false -when "debian", "ubuntu" - default['apache']['package'] = "apache2" - default['apache']['dir'] = "/etc/apache2" - default['apache']['log_dir'] = "/var/log/apache2" - default['apache']['error_log'] = "error.log" - default['apache']['access_log'] = "access.log" - default['apache']['user'] = "www-data" - default['apache']['group'] = "www-data" - default['apache']['binary'] = "/usr/sbin/apache2" - default['apache']['docroot_dir'] = "/var/www" - default['apache']['cgibin_dir'] = "/usr/lib/cgi-bin" - default['apache']['icondir'] = "/usr/share/apache2/icons" - default['apache']['cache_dir'] = "/var/cache/apache2" - if node['platform_version'].to_f >= 14 then - default['apache']['pid_file'] = "/var/run/apache2/apache2.pid" - else - default['apache']['pid_file'] = "/var/run/apache2.pid" - end - default['apache']['lib_dir'] = "/usr/lib/apache2" - default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" +when 'debian', 'ubuntu' + default['apache']['package'] = 'apache2' + default['apache']['perl_pkg'] = 'perl' + default['apache']['dir'] = '/etc/apache2' + default['apache']['log_dir'] = '/var/log/apache2' + default['apache']['error_log'] = 'error.log' + default['apache']['access_log'] = 'access.log' + default['apache']['user'] = 'www-data' + default['apache']['group'] = 'www-data' + default['apache']['binary'] = '/usr/sbin/apache2' + default['apache']['docroot_dir'] = '/var/www' + default['apache']['cgibin_dir'] = '/usr/lib/cgi-bin' + default['apache']['icondir'] = '/usr/share/apache2/icons' + default['apache']['cache_dir'] = '/var/cache/apache2' + default['apache']['pid_file'] = if node['platform'] == 'ubuntu' && node['platform_version'].to_f >= 13.10 + '/var/run/apache2/apache2.pid' + else + '/var/run/apache2.pid' + end + default['apache']['lib_dir'] = '/usr/lib/apache2' + default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" default['apache']['default_site_enabled'] = false -when "arch" - default['apache']['package'] = "apache" - default['apache']['dir'] = "/etc/httpd" - default['apache']['log_dir'] = "/var/log/httpd" - default['apache']['error_log'] = "error.log" - default['apache']['access_log'] = "access.log" - default['apache']['user'] = "http" - default['apache']['group'] = "http" - default['apache']['binary'] = "/usr/sbin/httpd" - default['apache']['docroot_dir'] = "/srv/http" - default['apache']['cgibin_dir'] = "/usr/share/httpd/cgi-bin" - default['apache']['icondir'] = "/usr/share/httpd/icons" - default['apache']['cache_dir'] = "/var/cache/httpd" - default['apache']['pid_file'] = "/var/run/httpd/httpd.pid" - default['apache']['lib_dir'] = "/usr/lib/httpd" - default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" +when 'arch' + default['apache']['package'] = 'apache' + default['apache']['perl_pkg'] = 'perl' + default['apache']['dir'] = '/etc/httpd' + default['apache']['log_dir'] = '/var/log/httpd' + default['apache']['error_log'] = 'error.log' + default['apache']['access_log'] = 'access.log' + default['apache']['user'] = 'http' + default['apache']['group'] = 'http' + default['apache']['binary'] = '/usr/sbin/httpd' + default['apache']['docroot_dir'] = '/srv/http' + default['apache']['cgibin_dir'] = '/usr/share/httpd/cgi-bin' + default['apache']['icondir'] = '/usr/share/httpd/icons' + default['apache']['cache_dir'] = '/var/cache/httpd' + default['apache']['pid_file'] = '/var/run/httpd/httpd.pid' + default['apache']['lib_dir'] = '/usr/lib/httpd' + default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" default['apache']['default_site_enabled'] = false -when "freebsd" - default['apache']['package'] = "apache22" - default['apache']['dir'] = "/usr/local/etc/apache22" - default['apache']['log_dir'] = "/var/log" - default['apache']['error_log'] = "httpd-error.log" - default['apache']['access_log'] = "httpd-access.log" - default['apache']['root_group'] = "wheel" - default['apache']['user'] = "www" - default['apache']['group'] = "www" - default['apache']['binary'] = "/usr/local/sbin/httpd" - default['apache']['docroot_dir'] = "/usr/local/www/apache22/data" - default['apache']['cgibin_dir'] = "/usr/local/www/apache22/cgi-bin" - default['apache']['icondir'] = "/usr/local/www/apache22/icons" - default['apache']['cache_dir'] = "/var/run/apache22" - default['apache']['pid_file'] = "/var/run/httpd.pid" - default['apache']['lib_dir'] = "/usr/local/libexec/apache22" - default['apache']['libexecdir'] = node['apache']['lib_dir'] +when 'freebsd' + default['apache']['package'] = 'apache22' + default['apache']['perl_pkg'] = 'perl5' + default['apache']['dir'] = '/usr/local/etc/apache22' + default['apache']['log_dir'] = '/var/log' + default['apache']['error_log'] = 'httpd-error.log' + default['apache']['access_log'] = 'httpd-access.log' + default['apache']['root_group'] = 'wheel' + default['apache']['user'] = 'www' + default['apache']['group'] = 'www' + default['apache']['binary'] = '/usr/local/sbin/httpd' + default['apache']['docroot_dir'] = '/usr/local/www/apache22/data' + default['apache']['cgibin_dir'] = '/usr/local/www/apache22/cgi-bin' + default['apache']['icondir'] = '/usr/local/www/apache22/icons' + default['apache']['cache_dir'] = '/var/run/apache22' + default['apache']['pid_file'] = '/var/run/httpd.pid' + default['apache']['lib_dir'] = '/usr/local/libexec/apache22' + default['apache']['libexecdir'] = node['apache']['lib_dir'] default['apache']['default_site_enabled'] = false else - default['apache']['dir'] = "/etc/apache2" - default['apache']['log_dir'] = "/var/log/apache2" - default['apache']['error_log'] = "error.log" - default['apache']['access_log'] = "access.log" - default['apache']['user'] = "www-data" - default['apache']['group'] = "www-data" - default['apache']['binary'] = "/usr/sbin/apache2" - default['apache']['docroot_dir'] = "/var/www" - default['apache']['cgibin_dir'] = "/usr/lib/cgi-bin" - default['apache']['icondir'] = "/usr/share/apache2/icons" - default['apache']['cache_dir'] = "/var/cache/apache2" - default['apache']['pid_file'] = "logs/httpd.pid" - default['apache']['lib_dir'] = "/usr/lib/apache2" - default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" + default['apache']['dir'] = '/etc/apache2' + default['apache']['log_dir'] = '/var/log/apache2' + default['apache']['error_log'] = 'error.log' + default['apache']['access_log'] = 'access.log' + default['apache']['user'] = 'www-data' + default['apache']['group'] = 'www-data' + default['apache']['binary'] = '/usr/sbin/apache2' + default['apache']['docroot_dir'] = '/var/www' + default['apache']['cgibin_dir'] = '/usr/lib/cgi-bin' + default['apache']['icondir'] = '/usr/share/apache2/icons' + default['apache']['cache_dir'] = '/var/cache/apache2' + default['apache']['pid_file'] = 'logs/httpd.pid' + default['apache']['lib_dir'] = '/usr/lib/apache2' + default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" default['apache']['default_site_enabled'] = false end @@ -122,51 +126,61 @@ end ### # General settings -default['apache']['listen_ports'] = ["80"] -default['apache']['contact'] = "ops@example.com" -default['apache']['timeout'] = 300 -default['apache']['keepalive'] = "On" +default['apache']['listen_addresses'] = %w[*] +default['apache']['listen_ports'] = %w[80] +default['apache']['contact'] = 'ops@example.com' +default['apache']['timeout'] = 300 +default['apache']['keepalive'] = 'On' default['apache']['keepaliverequests'] = 100 -default['apache']['keepalivetimeout'] = 5 +default['apache']['keepalivetimeout'] = 5 +default['apache']['sysconfig_additional_params'] = {} # Security -default['apache']['servertokens'] = "Prod" -default['apache']['serversignature'] = "On" -default['apache']['traceenable'] = "On" +default['apache']['servertokens'] = 'Prod' +default['apache']['serversignature'] = 'On' +default['apache']['traceenable'] = 'On' # mod_auth_openids -default['apache']['allowed_openids'] = Array.new +default['apache']['allowed_openids'] = [] -# mod_status Allow list, space seprated list of allowed entries. -default['apache']['status_allow_list'] = "localhost ip6-localhost" +# mod_status Allow list, space seprated list of allowed entries. +default['apache']['status_allow_list'] = 'localhost ip6-localhost' # mod_status ExtendedStatus, set to 'true' to enable default['apache']['ext_status'] = false +# mod_info Allow list, space seprated list of allowed entries. +default['apache']['info_allow_list'] = 'localhost ip6-localhost' + # Prefork Attributes -default['apache']['prefork']['startservers'] = 16 -default['apache']['prefork']['minspareservers'] = 16 -default['apache']['prefork']['maxspareservers'] = 32 -default['apache']['prefork']['serverlimit'] = 400 -default['apache']['prefork']['maxclients'] = 400 -default['apache']['prefork']['maxrequestsperchild'] = 10000 +default['apache']['prefork']['startservers'] = 16 +default['apache']['prefork']['minspareservers'] = 16 +default['apache']['prefork']['maxspareservers'] = 32 +default['apache']['prefork']['serverlimit'] = 400 +default['apache']['prefork']['maxclients'] = 400 +default['apache']['prefork']['maxrequestsperchild'] = 10_000 # Worker Attributes -default['apache']['worker']['startservers'] = 4 -default['apache']['worker']['serverlimit'] = 16 -default['apache']['worker']['maxclients'] = 1024 -default['apache']['worker']['minsparethreads'] = 64 -default['apache']['worker']['maxsparethreads'] = 192 -default['apache']['worker']['threadsperchild'] = 64 +default['apache']['worker']['startservers'] = 4 +default['apache']['worker']['serverlimit'] = 16 +default['apache']['worker']['maxclients'] = 1024 +default['apache']['worker']['minsparethreads'] = 64 +default['apache']['worker']['maxsparethreads'] = 192 +default['apache']['worker']['threadsperchild'] = 64 default['apache']['worker']['maxrequestsperchild'] = 0 +# mod_proxy settings +default['apache']['proxy']['order'] = 'deny,allow' +default['apache']['proxy']['deny_from'] = 'all' +default['apache']['proxy']['allow_from'] = 'none' + # Default modules to enable via include_recipe -default['apache']['default_modules'] = %w{ +default['apache']['default_modules'] = %w[ status alias auth_basic authn_file authz_default authz_groupfile authz_host authz_user autoindex dir env mime negotiation setenvif -} +] -%w{ log_config logio }.each do |log_mod| - default['apache']['default_modules'] << log_mod if ["rhel", "fedora", "suse", "arch", "freebsd"].include?(node['platform_family']) +%w[log_config logio].each do |log_mod| + default['apache']['default_modules'] << log_mod if %w[rhel fedora suse arch freebsd].include?(node['platform_family']) end diff --git a/chef/cookbooks/apache2/attributes/mod_auth_cas.rb b/chef/cookbooks/apache2/attributes/mod_auth_cas.rb index a5cc262..4716fe7 100644 --- a/chef/cookbooks/apache2/attributes/mod_auth_cas.rb +++ b/chef/cookbooks/apache2/attributes/mod_auth_cas.rb @@ -1,2 +1,21 @@ -default['apache']['mod_auth_cas']['from_source'] = false -default['apache']['mod_auth_cas']['source_revision'] = "v1.0.8.1" +# +# Cookbook Name:: apache2 +# Attributes:: mod_auth_cas +# +# Copyright 2013, Opscode, Inc. +# +# 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. +# + +default['apache']['mod_auth_cas']['from_source'] = false +default['apache']['mod_auth_cas']['source_revision'] = 'v1.0.8.1' diff --git a/chef/cookbooks/apache2/attributes/mod_auth_openid.rb b/chef/cookbooks/apache2/attributes/mod_auth_openid.rb index d6b8f1e..3aba0aa 100644 --- a/chef/cookbooks/apache2/attributes/mod_auth_openid.rb +++ b/chef/cookbooks/apache2/attributes/mod_auth_openid.rb @@ -1,13 +1,14 @@ # -# Author:: Joshua Timberman -# Copyright:: Copyright (c) 2011, Opscode, Inc. -# License:: Apache License, Version 2.0 +# Cookbook Name:: apache2 +# Attributes:: mod_auth_cas +# +# Copyright 2013, Opscode, Inc. # # 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 +# 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, @@ -16,16 +17,16 @@ # limitations under the License. # -default['apache']['mod_auth_openid']['ref'] = "95043901eab868400937642d9bc55d17e9dd069f" +default['apache']['mod_auth_openid']['ref'] = '95043901eab868400937642d9bc55d17e9dd069f' default['apache']['mod_auth_openid']['source_url'] = "https://github.com/bmuller/mod_auth_openid/archive/#{node['apache']['mod_auth_openid']['ref']}.tar.gz" -default['apache']['mod_auth_openid']['cache_dir'] = "/var/cache/mod_auth_openid" +default['apache']['mod_auth_openid']['cache_dir'] = '/var/cache/mod_auth_openid' default['apache']['mod_auth_openid']['dblocation'] = "#{node['apache']['mod_auth_openid']['cache_dir']}/mod_auth_openid.db" case node['platform_family'] -when "freebsd" +when 'freebsd' default['apache']['mod_auth_openid']['configure_flags'] = [ - "CPPFLAGS=-I/usr/local/include", - "LDFLAGS=-I/usr/local/lib -lsqlite3" + 'CPPFLAGS=-I/usr/local/include', + 'LDFLAGS=-I/usr/local/lib -lsqlite3' ] else default['apache']['mod_auth_openid']['configure_flags'] = [] diff --git a/chef/cookbooks/openstack-ops-messaging/recipes/default.rb b/chef/cookbooks/apache2/attributes/mod_fastcgi.rb similarity index 78% rename from chef/cookbooks/openstack-ops-messaging/recipes/default.rb rename to chef/cookbooks/apache2/attributes/mod_fastcgi.rb index 10fc737..0583c9f 100644 --- a/chef/cookbooks/openstack-ops-messaging/recipes/default.rb +++ b/chef/cookbooks/apache2/attributes/mod_fastcgi.rb @@ -1,6 +1,6 @@ # -# Cookbook Name:: openstack-ops-messaging -# Recipe:: default +# Cookbook Name:: apache2 +# Attributes:: mod_fastcgi # # Copyright 2013, Opscode, Inc. # @@ -17,4 +17,4 @@ # limitations under the License. # -include_recipe "openstack-ops-messaging::#{node['openstack']['messaging']['service']}" +default['apache']['mod_fastcgi']['download_url'] = 'http://www.fastcgi.com/dist/mod_fastcgi-current.tar.gz' diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/apache2/attributes/mod_pagespeed.rb similarity index 60% rename from chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/support/helpers.rb rename to chef/cookbooks/apache2/attributes/mod_pagespeed.rb index 33a4ac8..3c23c93 100644 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/support/helpers.rb +++ b/chef/cookbooks/apache2/attributes/mod_pagespeed.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: apt_test -# Recipe:: default +# Cookbook Name:: apache2 +# Attributes:: mod_pagespeed # -# Copyright 2012, Opscode, Inc. +# Copyright 2013, ZOZI # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,13 +17,9 @@ # limitations under the License. # -module Helpers - module AptTest - require 'chef/mixin/shell_out' - include Chef::Mixin::ShellOut - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - +default['apache2']['mod_pagespeed']['package_link'] = + if node['kernel']['machine'] =~ /^i[36']86$/ + 'https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_i386.deb' + else + 'https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_amd64.deb' end -end diff --git a/chef/cookbooks/apache2/attributes/mod_ssl.rb b/chef/cookbooks/apache2/attributes/mod_ssl.rb index c744cb4..e71d3a6 100644 --- a/chef/cookbooks/apache2/attributes/mod_ssl.rb +++ b/chef/cookbooks/apache2/attributes/mod_ssl.rb @@ -1,13 +1,14 @@ # -# Author:: Nathan L Smith -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# License:: Apache License, Version 2.0 +# Cookbook Name:: apache2 +# Attributes:: mod_ssl +# +# Copyright 2012-2013, Opscode, Inc. # # 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 +# 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, diff --git a/chef/cookbooks/apache2/definitions/apache_conf.rb b/chef/cookbooks/apache2/definitions/apache_conf.rb index 5a62158..3f61517 100644 --- a/chef/cookbooks/apache2/definitions/apache_conf.rb +++ b/chef/cookbooks/apache2/definitions/apache_conf.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Definition:: apache_conf # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-20013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,8 +19,8 @@ define :apache_conf do template "#{node['apache']['dir']}/mods-available/#{params[:name]}.conf" do - source "mods/#{params[:name]}.conf.erb" - notifies :restart, "service[apache2]" - mode 0644 + source "mods/#{params[:name]}.conf.erb" + mode '0644' + notifies :restart, 'service[apache2]' end end diff --git a/chef/cookbooks/apache2/definitions/apache_module.rb b/chef/cookbooks/apache2/definitions/apache_module.rb index 99ca29c..0129e5c 100644 --- a/chef/cookbooks/apache2/definitions/apache_module.rb +++ b/chef/cookbooks/apache2/definitions/apache_module.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Definition:: apache_module # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,36 +18,35 @@ # define :apache_module, :enable => true, :conf => false do - include_recipe "apache2" + include_recipe 'apache2::default' - params[:filename] = params[:filename] || "mod_#{params[:name]}.so" + params[:filename] = params[:filename] || "mod_#{params[:name]}.so" params[:module_path] = params[:module_path] || "#{node['apache']['libexecdir']}/#{params[:filename]}" + params[:identifier] = params[:identifier] || "#{params[:name]}_module" - if params[:conf] - apache_conf params[:name] - end + apache_conf params[:name] if params[:conf] - if platform_family?("rhel", "fedora", "arch", "suse", "freebsd") + if platform_family?('rhel', 'fedora', 'arch', 'suse', 'freebsd') file "#{node['apache']['dir']}/mods-available/#{params[:name]}.load" do - content "LoadModule #{params[:name]}_module #{params[:module_path]}\n" - mode 0644 + content "LoadModule #{params[:identifier]} #{params[:module_path]}\n" + mode '0644' end end if params[:enable] execute "a2enmod #{params[:name]}" do command "/usr/sbin/a2enmod #{params[:name]}" - notifies :restart, "service[apache2]" - not_if do (::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.load") and - ((::File.exists?("#{node['apache']['dir']}/mods-available/#{params[:name]}.conf"))? - (::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.conf")):(true))) + notifies :restart, 'service[apache2]' + not_if do + ::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.load") && + (::File.exists?("#{node['apache']['dir']}/mods-available/#{params[:name]}.conf") ? ::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.conf") : true) end end else execute "a2dismod #{params[:name]}" do command "/usr/sbin/a2dismod #{params[:name]}" - notifies :restart, "service[apache2]" - only_if do ::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.load") end + notifies :restart, 'service[apache2]' + only_if { ::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.load") } end end end diff --git a/chef/cookbooks/apache2/definitions/apache_site.rb b/chef/cookbooks/apache2/definitions/apache_site.rb index 1bc2870..1761981 100644 --- a/chef/cookbooks/apache2/definitions/apache_site.rb +++ b/chef/cookbooks/apache2/definitions/apache_site.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Definition:: apache_site # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,24 +18,24 @@ # define :apache_site, :enable => true do - include_recipe "apache2" + include_recipe 'apache2::default' if params[:enable] execute "a2ensite #{params[:name]}" do command "/usr/sbin/a2ensite #{params[:name]}" - notifies :restart, resources(:service => "apache2") + notifies :restart, 'service[apache2]' not_if do - ::File.symlink?("#{node['apache']['dir']}/sites-enabled/#{params[:name]}") or + ::File.symlink?("#{node['apache']['dir']}/sites-enabled/#{params[:name]}") || ::File.symlink?("#{node['apache']['dir']}/sites-enabled/000-#{params[:name]}") end - only_if do ::File.exists?("#{node['apache']['dir']}/sites-available/#{params[:name]}") end + only_if { ::File.exists?("#{node['apache']['dir']}/sites-available/#{params[:name]}") } end else execute "a2dissite #{params[:name]}" do command "/usr/sbin/a2dissite #{params[:name]}" - notifies :restart, resources(:service => "apache2") + notifies :restart, 'service[apache2]' only_if do - ::File.symlink?("#{node['apache']['dir']}/sites-enabled/#{params[:name]}") or + ::File.symlink?("#{node['apache']['dir']}/sites-enabled/#{params[:name]}") || ::File.symlink?("#{node['apache']['dir']}/sites-enabled/000-#{params[:name]}") end end diff --git a/chef/cookbooks/apache2/definitions/web_app.rb b/chef/cookbooks/apache2/definitions/web_app.rb index 2547e30..6844d3b 100644 --- a/chef/cookbooks/apache2/definitions/web_app.rb +++ b/chef/cookbooks/apache2/definitions/web_app.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Definition:: web_app # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,29 +17,27 @@ # limitations under the License. # -define :web_app, :template => "web_app.conf.erb", :enable => true do +define :web_app, :template => 'web_app.conf.erb', :enable => true do application_name = params[:name] - include_recipe "apache2" - include_recipe "apache2::mod_rewrite" - include_recipe "apache2::mod_deflate" - include_recipe "apache2::mod_headers" + include_recipe 'apache2::default' + include_recipe 'apache2::mod_rewrite' + include_recipe 'apache2::mod_deflate' + include_recipe 'apache2::mod_headers' template "#{node['apache']['dir']}/sites-available/#{application_name}.conf" do - source params[:template] - owner "root" - group node['apache']['root_group'] - mode 0644 - if params[:cookbook] - cookbook params[:cookbook] - end + source params[:template] + owner 'root' + group node['apache']['root_group'] + mode '0644' + cookbook params[:cookbook] if params[:cookbook] variables( :application_name => application_name, - :params => params + :params => params ) if ::File.exists?("#{node['apache']['dir']}/sites-enabled/#{application_name}.conf") - notifies :reload, resources(:service => "apache2"), :delayed + notifies :reload, 'service[apache2]' end end diff --git a/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl b/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl index 83f849e..e161fbb 100644 --- a/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl +++ b/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl @@ -3,7 +3,7 @@ =begin Generates Ubuntu style module.load files. - + ./apache2_module_conf_generate.pl /usr/lib64/httpd/modules /etc/httpd/mods-available ARGV[0] is the apache modules directory, ARGV[1] is where you want 'em. diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/default_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/default_test.rb index 0093d23..0bec9b1 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/default_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/default_test.rb @@ -16,7 +16,7 @@ describe 'apache2::default' do end it 'creates the conf.d directory' do - directory("#{node['apache']['dir']}/conf.d").must_exist.with(:mode, "755") + directory("#{node['apache']['dir']}/conf.d").must_exist.with(:mode, '755') end it 'creates the logs directory' do @@ -36,7 +36,7 @@ describe 'apache2::default' do end it 'reports server name only, not detailed version info' do - assert_match(/^ServerTokens Prod *$/, File.read("#{node['apache']['dir']}/conf.d/security")) + assert_match(/^ServerTokens #{node['apache']['servertokens']} *$/, File.read("#{node['apache']['dir']}/conf.d/security")) end it 'listens on port 80' do @@ -50,10 +50,10 @@ describe 'apache2::default' do end it 'reports server name only, not detailed version info' do - file("#{node['apache']['dir']}/conf.d/security").must_match(/^ServerTokens Prod *$/) + file("#{node['apache']['dir']}/conf.d/security").must_match(/^ServerTokens #{node['apache']['servertokens']} *$/) end - it "enables default_modules" do + it 'enables default_modules' do node['apache']['default_modules'].each do |a2mod| apache_enabled_modules.must_include "#{a2mod}_module" end @@ -73,5 +73,4 @@ describe 'apache2::default' do it { config.must_include "Include #{node['apache']['dir']}/conf.d/" } it { apache_config_parses? } end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/god_monitor_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/god_monitor_test.rb index 5c0d7a0..f2f972e 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/god_monitor_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/god_monitor_test.rb @@ -17,15 +17,15 @@ require File.expand_path('../support/helpers', __FILE__) -describe "apache2::god_monitor" do +describe 'apache2::god_monitor' do include Helpers::Apache it 'starts god service to supervise apache2' do - service("god").must_be_running + service('god').must_be_running end it 'creates the god service template for apache' do - file("/etc/god/conf.d/apache2.god").must_exist + file('/etc/god/conf.d/apache2.god').must_exist end it 'starts an apache2 service that works like a regular service' do diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_apreq2_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_apreq2_test.rb index 8679b4e..140ec16 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_apreq2_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_apreq2_test.rb @@ -4,12 +4,12 @@ describe 'apache2::mod_apreq2' do include Helpers::Apache it 'enables apreq_module' do - apache_enabled_modules.must_include "apreq_module" + apache_enabled_modules.must_include 'apreq_module' end it 'symlinks the module on EL' do - skip unless %w{rhel fedora}.include?(node['platform_family']) - libdir = node['kernel']['machine'] == 'x86_64' ? "lib64" : "lib" + skip unless %w[rhel fedora].include?(node['platform_family']) + libdir = node['kernel']['machine'] == 'x86_64' ? 'lib64' : 'lib' link( "/usr/#{libdir}/httpd/modules/mod_apreq.so" ).must_exist.with( diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_cas_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_cas_test.rb index f7e06ea..745c217 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_cas_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_cas_test.rb @@ -1,11 +1,10 @@ require File.expand_path('../support/helpers', __FILE__) -describe "apache2::mod_auth_cas" do +describe 'apache2::mod_auth_cas' do include Helpers::Apache it 'enables auth_cas_module' do - skip if %w{rhel fedora}.include?(node['platform_family']) && node['platform_version'].to_f > 6.0 - apache_enabled_modules.must_include "auth_cas_module" + skip if %w[rhel fedora].include?(node['platform_family']) && node['platform_version'].to_f > 6.0 + apache_enabled_modules.must_include 'auth_cas_module' end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_openid_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_openid_test.rb index 7793a44..84f7f54 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_openid_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_openid_test.rb @@ -4,34 +4,33 @@ require 'pathname' describe 'apache2::mod_auth_openid' do include Helpers::Apache - it "installs the opekele library" do + it 'installs the opekele library' do lib_dir = Pathname.new(node['apache']['lib_dir']).dirname.to_s file("#{lib_dir}/libopkele.so").must_exist end - it "does not add the module to httpd.conf" do + it 'does not add the module to httpd.conf' do conffile = case node['platform'] when 'debian', 'ubuntu' - "apache2.conf" - when "redhat", "centos", "scientific", "fedora", "arch", "amazon" - "conf/httpd.conf" - when "freebsd" - "httpd.conf" + 'apache2.conf' + when 'redhat', 'centos', 'scientific', 'fedora', 'arch', 'amazon' + 'conf/httpd.conf' + when 'freebsd' + 'httpd.conf' end httpd_config = File.read(File.join(node['apache']['dir'], conffile)) refute_match /^LoadModule authopenid_module /, httpd_config end - it "creates a cache directory for the module" do + it 'creates a cache directory for the module' do directory(node['apache']['mod_auth_openid']['cache_dir']).must_exist.with(:owner, node['apache']['user']) end - it "ensures the db file is writable by apache" do - file(node['apache']['mod_auth_openid']['dblocation']).must_exist.with(:owner, node['apache']['user']).and(:mode, "644") + it 'ensures the db file is writable by apache' do + file(node['apache']['mod_auth_openid']['dblocation']).must_exist.with(:owner, node['apache']['user']).and(:mode, '644') end it 'enables authopenid_module' do - apache_enabled_modules.must_include "authopenid_module" + apache_enabled_modules.must_include 'authopenid_module' end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_cgi_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_cgi_test.rb index 1391240..d14e764 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_cgi_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_cgi_test.rb @@ -9,5 +9,4 @@ describe 'apache2::mod_cgi' do apache_enabled_modules.include?('cgid_module') ) end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_dav_svn_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_dav_svn_test.rb index cfc1f61..25e0637 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_dav_svn_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_dav_svn_test.rb @@ -4,11 +4,10 @@ describe 'apache2::mod_dav_svn' do include Helpers::Apache it 'enables dav_svn_module' do - apache_enabled_modules.must_include "dav_svn_module" + apache_enabled_modules.must_include('dav_svn_module') end it 'enables dav_module' do - apache_enabled_modules.must_include "dav_module" + apache_enabled_modules.must_include('dav_module') end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_fastcgi.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_fastcgi.rb index 1c404f8..6dfb34c 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_fastcgi.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_fastcgi.rb @@ -1,11 +1,10 @@ require File.expand_path('../support/helpers', __FILE__) -describe "apache2::mod_fastcgi" do +describe 'apache2::mod_fastcgi' do include Helpers::Apache it 'enables fastcgi_module' do skip if %w{rhel fedora}.include?(node['platform_family']) - apache_enabled_modules.must_include "fastcgi_module" + apache_enabled_modules.must_include 'fastcgi_module' end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_include_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_include_test.rb index c0c4944..3cc7a26 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_include_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_include_test.rb @@ -4,12 +4,11 @@ describe 'apache2::mod_include' do include Helpers::Apache it 'enables include_module' do - apache_enabled_modules.must_include "include_module" + apache_enabled_modules.must_include 'include_module' end it 'drops off the include module configuration' do assert_match(/AddType text\/html .shtml/, File.read("#{node['apache']['dir']}/mods-enabled/include.conf")) assert_match(/AddOutputFilter INCLUDES .shtml/, File.read("#{node['apache']['dir']}/mods-enabled/include.conf")) end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_perl_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_perl_test.rb index c51341a..f403d97 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_perl_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_perl_test.rb @@ -4,7 +4,7 @@ describe 'apache2::mod_perl' do include Helpers::Apache it 'enables perl_module' do - apache_enabled_modules.must_include "perl_module" + apache_enabled_modules.must_include('perl_module') end it 'installs the apache request library' do @@ -14,5 +14,4 @@ describe 'apache2::mod_perl' do end package(req_pkg).must_be_installed end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_php5_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_php5_test.rb index 33220b9..482cdcd 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_php5_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_php5_test.rb @@ -4,10 +4,10 @@ describe 'apache2::mod_php5' do include Helpers::Apache it 'enables php5_module' do - apache_enabled_modules.must_include "php5_module" + apache_enabled_modules.must_include('php5_module') end - it "deletes the packaged php config if any" do + it 'deletes the packaged php config if any' do file("#{node['apache']['dir']}/conf.d/php.conf").wont_exist end end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_python_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_python_test.rb index 5b5f2b7..4ef119c 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_python_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_python_test.rb @@ -4,7 +4,6 @@ describe 'apache2::mod_python' do include Helpers::Apache it 'enables python_module' do - apache_enabled_modules.must_include "python_module" + apache_enabled_modules.must_include('python_module') end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_ssl_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_ssl_test.rb index 035f652..98d22a5 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/mod_ssl_test.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_ssl_test.rb @@ -4,25 +4,26 @@ describe 'apache2::mod_ssl' do include Helpers::Apache it 'installs the mod_ssl package on RHEL distributions' do - skip unless ["rhel", "fedora"].include? node['platform_family'] - package("mod_ssl").must_be_installed + skip unless %w[rhel fedora].include?(node['platform_family']) + package('mod_ssl').must_be_installed end it 'enables ssl_module' do - apache_enabled_modules.must_include "ssl_module" + apache_enabled_modules.must_include 'ssl_module' end it 'does not store SSL config in conf.d' do file("#{node['apache']['dir']}/conf.d/ssl.conf").wont_exist end - it "is configured to listen on port 443" do + it 'is configured to listen on port 443' do apache_configured_ports.must_include(443) end it 'configures SSLCiphersuit from an attribute' do - assert_match(/^SSLCipherSuite #{node['apache']['mod_ssl']['cipher_suite']}$/, - File.read("#{node['apache']['dir']}/mods-enabled/ssl.conf")) + assert_match( + /^SSLCipherSuite #{node['apache']['mod_ssl']['cipher_suite']}$/, + File.read("#{node['apache']['dir']}/mods-enabled/ssl.conf") + ) end - end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/apache2/files/default/tests/minitest/support/helpers.rb index 707fcf2..dc8a15e 100644 --- a/chef/cookbooks/apache2/files/default/tests/minitest/support/helpers.rb +++ b/chef/cookbooks/apache2/files/default/tests/minitest/support/helpers.rb @@ -1,4 +1,5 @@ module Helpers + # MiniTest helpers module Apache require 'chef/mixin/shell_out' include Chef::Mixin::ShellOut @@ -30,9 +31,9 @@ module Helpers def apache_service service( case node['platform'] - when "debian", "ubuntu" then "apache2" - when "freebsd" then "apache22" - else "httpd" + when 'debian', 'ubuntu' then 'apache2' + when 'freebsd' then 'apache22' + else 'httpd' end ) end @@ -40,26 +41,25 @@ module Helpers def config file( case node['platform'] - when "debian", "ubuntu" then "#{node['apache']['dir']}/apache2.conf" - when "freebsd" then "#{node['apache']['dir']}/httpd.conf" + when 'debian', 'ubuntu' then "#{node['apache']['dir']}/apache2.conf" + when 'freebsd' then "#{node['apache']['dir']}/httpd.conf" else "#{node['apache']['dir']}/conf/httpd.conf" end ) end def ran_recipe?(recipe) - if Chef::VERSION < "11.0" + if Chef::VERSION < '11.0' seen_recipes = node.run_state[:seen_recipes] recipes = seen_recipes.keys.each { |i| i } else recipes = run_context.loaded_recipes end - if recipes.empty? and Chef::Config[:solo] - #If you have roles listed in your run list they are NOT expanded - recipes = node.run_list.map {|item| item.name if item.type == :recipe } + if recipes.empty? && Chef::Config[:solo] + # If you have roles listed in your run list they are NOT expanded + recipes = node.run_list.map { |item| item.name if item.type == :recipe } end recipes.include?(recipe) end - end end diff --git a/chef/cookbooks/apache2/metadata.json b/chef/cookbooks/apache2/metadata.json new file mode 100644 index 0000000..3f500a2 --- /dev/null +++ b/chef/cookbooks/apache2/metadata.json @@ -0,0 +1,367 @@ +{ + "name": "apache2", + "version": "1.9.6", + "description": "Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions", + "long_description": "apache2 Cookbook\n================\n[![Build Status](https://secure.travis-ci.org/opscode-cookbooks/apache2.png?branch=master)](http://travis-ci.org/opscode-cookbooks/apache2)\n\n\nThis cookbook provides a complete Debian/Ubuntu style Apache HTTPD\nconfiguration. Non-Debian based distributions such as Red Hat/CentOS,\nArchLinux and others supported by this cookbook will have a\nconfiguration that mimics Debian/Ubuntu style as it is easier to\nmanage with Chef.\n\nDebian-style Apache configuration uses scripts to manage modules and\nsites (vhosts). The scripts are:\n\n* a2ensite\n* a2dissite\n* a2enmod\n* a2dismod\n\nThis cookbook ships with templates of these scripts for non\nDebian/Ubuntu platforms. The scripts are used in the __Definitions__\nbelow.\n\nRequirements\n============\n\n## Ohai and Chef:\n\n* Ohai: 0.6.12+\n* Chef: 0.10.10+\n\nAs of v1.2.0, this cookbook makes use of `node['platform_family']` to\nsimplify platform selection logic. This attribute was introduced in\nOhai v0.6.12. The recipe methods were introduced in Chef v0.10.10. If\nyou must run an older version of Chef or Ohai, use [version 1.1.16 of\nthis cookbook](http://community.opscode.com/cookbooks/apache2/versions/1_1_16/downloads).\n\n## Cookbooks:\n\nThis cookbook doesn't have direct dependencies on other cookbooks, as\nnone are needed for the default recipe or the general use cases.\n\nDepending on your OS configuration and security policy, you may need\nadditional recipes or cookbooks for this cookbook's recipes to\nconverge on the node. In particular, the following Operating System\nsettings may affect the behavior of this cookbook:\n\n* apt cache outdated\n* SELinux enabled\n* IPtables\n* Compile tools\n* 3rd party repositories\n\nOn Ubuntu/Debian, use Opscode's `apt` cookbook to ensure the package\ncache is updated so Chef can install packages, or consider putting\napt-get in your bootstrap process or\n[knife bootstrap template](http://wiki.opscode.com/display/chef/Knife+Bootstrap).\n\nOn RHEL, SELinux is enabled by default. The `selinux` cookbook\ncontains a `permissive` recipe that can be used to set SELinux to\n\"Permissive\" state. Otherwise, additional recipes need to be created\nby the user to address SELinux permissions.\n\nThe easiest but **certainly not ideal way** to deal with IPtables is\nto flush all rules. Opscode does provide an `iptables` cookbook but is\nmigrating from the approach used there to a more robust solution\nutilizing a general \"firewall\" LWRP that would have an \"iptables\"\nprovider. Alternately, you can use ufw, with Opscode's `ufw` and\n`firewall` cookbooks to set up rules. See those cookbooks' READMEs for\ndocumentation.\n\nBuild/compile tools may not be installed on the system by default.\nSome recipes (e.g., `apache2::mod_auth_openid`) build the module from\nsource. Use Opscode's `build-essential` cookbook to get essential\nbuild packages installed.\n\nOn ArchLinux, if you are using the `apache2::mod_auth_openid` recipe,\nyou also need the `pacman` cookbook for the `pacman_aur` LWRP. Put\n`recipe[pacman]` on the node's expanded run list (on the node or in a\nrole). This is not an explicit dependency because it is only required\nfor this single recipe and platform; the pacman default recipe\nperforms `pacman -Sy` to keep pacman's package cache updated.\n\nThe `apache2::god_monitor` recipe uses a definition from the `god`\ncookbook. Include `recipe[god]` in the node's expanded run list to\nensure that the cookbook is available to the node, and to set up `god`.\n\n## Platforms:\n\nThe following platforms and versions are tested and supported using\nOpscode's [test-kitchen](http://github.com/opscode/test-kitchen).\n\n* Ubuntu 10.04, 12.04\n* CentOS 5.8, 6.3\n\nThe following platform families are supported in the code, and are\nassumed to work based on the successful testing on Ubuntu and CentOS.\n\n* Debian\n* Red Hat (rhel)\n* Fedora\n* Amazon Linux\n\nThe following platforms are also supported in the code, have been\ntested manually but are not tested under test-kitchen.\n\n* SUSE/OpenSUSE\n* ArchLinux\n* FreeBSD\n\n### Notes for RHEL Family:\n\nOn Red Hat Enterprise Linux and derivatives, the EPEL repository may\nbe necessary to install packages used in certain recipes. The\n`apache2::default` recipe, however, does not require any additional\nrepositories. Opscode's `yum` cookbook contains a recipe to add the\nEPEL repository. See __Examples__ for more information.\n\n### Notes for FreeBSD:\n\nThe `apache2::mod_php5` recipe depends on the `freebsd` cookbook,\nwhich it uses to set the correct options for compiling the `php5` port\nfrom sources. You need to ensure the `freebsd` is in the expanded run\nlist, or this recipe will fail. We don't set an explicit dependency\nbecause we feel the `freebsd` cookbook is something users would want\non their nodes, and due to the generality of this cookbook we don't\nwant additional specific dependencies.\n\nTests\n=====\n\nThis cookbook in the\n[source repository](https://github.com/opscode-cookbooks/apache2)\ncontains minitest and cucumber tests. This is an initial proof of\nconcept that will be fleshed out with more supporting infrastructure\nat a future time.\n\nPlease see the CONTRIBUTING file for information on how to add tests\nfor your contributions.\n\nAttributes\n==========\n\nThis cookbook uses many attributes, broken up into a few different\nkinds.\n\nPlatform specific\n-----------------\n\nIn order to support the broadest number of platforms, several\nattributes are determined based on the node's platform. See the\nattributes/default.rb file for default values in the case statement at\nthe top of the file.\n\n* `node['apache']['package']` - Package name for Apache2\n* `node['apache']['perl_pkg']` - Package name for Perl\n* `node['apache']['dir']` - Location for the Apache configuration\n* `node['apache']['log_dir']` - Location for Apache logs\n* `node['apache']['error_log']` - Location for the default error log\n* `node['apache']['access_log']` - Location for the default access log\n* `node['apache']['user']` - User Apache runs as\n* `node['apache']['group']` - Group Apache runs as\n* `node['apache']['binary']` - Apache httpd server daemon\n* `node['apache']['docroot_dir']` - Location for docroot\n* `node['apache']['cgibin_dir']` - Location for cgi-bin\n* `node['apache']['icondir']` - Location for icons\n* `node['apache']['cache_dir']` - Location for cached files used by Apache itself or recipes\n* `node['apache']['pid_file']` - Location of the PID file for Apache httpd\n* `node['apache']['lib_dir']` - Location for shared libraries\n* `node['apache']['default_site_enabled']` - Default site enabled. Default is false.\n* `node['apache']['ext_status']` - if true, enables ExtendedStatus for `mod_status`\n\nGeneral settings\n----------------\n\nThese are general settings used in recipes and templates. Default\nvalues are noted.\n\n* `node['apache']['listen_addresses']` - Addresses that httpd should listen on. Default is any (\"*\").\n* `node['apache']['listen_ports']` - Ports that httpd should listen on. Default is port 80.\n* `node['apache']['contact']` - Value for ServerAdmin directive. Default \"ops@example.com\".\n* `node['apache']['timeout']` - Value for the Timeout directive. Default is 300.\n* `node['apache']['keepalive']` - Value for the KeepAlive directive. Default is On.\n* `node['apache']['keepaliverequests']` - Value for MaxKeepAliveRequests. Default is 100.\n* `node['apache']['keepalivetimeout']` - Value for the KeepAliveTimeout directive. Default is 5.\n* `node['apache']['sysconfig_additional_params']` - Additionals variables set in sysconfig file. Default is empty.\n* `node['apache']['default_modules']` - Array of module names. Can take \"mod_FOO\" or \"FOO\" as names, where FOO is the apache module, e.g. \"`mod_status`\" or \"`status`\".\n\nThe modules listed in `default_modules` will be included as recipes in `recipe[apache::default]`.\n\nPrefork attributes\n------------------\n\nPrefork attributes are used for tuning the Apache HTTPD prefork MPM\nconfiguration.\n\n* `node['apache']['prefork']['startservers']` - initial number of server processes to start. Default is 16.\n* `node['apache']['prefork']['minspareservers']` - minimum number of spare server processes. Default 16.\n* `node['apache']['prefork']['maxspareservers']` - maximum number of spare server processes. Default 32.\n* `node['apache']['prefork']['serverlimit']` - upper limit on configurable server processes. Default 400.\n* `node['apache']['prefork']['maxclients']` - Maximum number of simultaneous connections.\n* `node['apache']['prefork']['maxrequestsperchild']` - Maximum number of request a child process will handle. Default 10000.\n\nWorker attributes\n-----------------\n\nWorker attributes are used for tuning the Apache HTTPD worker MPM\nconfiguration.\n\n* `node['apache']['worker']['startservers']` - Initial number of server processes to start. Default 4\n* `node['apache']['worker']['serverlimit']` - upper limit on configurable server processes. Default 16.\n* `node['apache']['worker']['maxclients']` - Maximum number of simultaneous connections. Default 1024.\n* `node['apache']['worker']['minsparethreads']` - Minimum number of spare worker threads. Default 64\n* `node['apache']['worker']['maxsparethreads']` - Maximum number of spare worker threads. Default 192.\n* `node['apache']['worker']['maxrequestsperchild']` - Maximum number of requests a child process will handle.\n\nmod\\_auth\\_openid attributes\n----------------------------\n\nThe following attributes are in the `attributes/mod_auth_openid.rb`\nfile. Like all Chef attributes files, they are loaded as well, but\nthey're logistically unrelated to the others, being specific to the\n`mod_auth_openid` recipe.\n\n* `node['apache']['mod_auth_openid']['checksum']` - sha256sum of the tarball containing the source.\n* `node['apache']['mod_auth_openid']['ref']` - Any sha, tag, or branch found from https://github.com/bmuller/mod_auth_openid\n* `node['apache']['mod_auth_openid']['cache_dir']` - the cache directory is where the sqlite3 database is stored. It is separate so it can be managed as a directory resource.\n* `node['apache']['mod_auth_openid']['dblocation']` - filename of the sqlite3 database used for directive `AuthOpenIDDBLocation`, stored in the `cache_dir` by default.\n* `node['apache']['mod_auth_openid']['configure_flags']` - optional array of configure flags passed to the `./configure` step in the compilation of the module.\n\nmod\\_ssl attributes\n-------------------\n\n* `node['apache']['mod_ssl']['cipher_suite']` - sets the\n SSLCiphersuite value to the specified string. The default is\n considered \"sane\" but you may need to change it for your local\n security policy, e.g. if you have PCI-DSS requirements. Additional\n commentary on the\n [original pull request](https://github.com/opscode-cookbooks/apache2/pull/15#commitcomment-1605406).\n\nRecipes\n=======\n\nMost of the recipes in the cookbook are for enabling Apache modules.\nWhere additional configuration or behavior is used, it is documented\nbelow in more detail.\n\nThe following recipes merely enable the specified module: `mod_alias`,\n`mod_basic`, `mod_digest`, `mod_authn_file`, `mod_authnz_ldap`,\n`mod_authz_default`, `mod_authz_groupfile`, `mod_authz_host`,\n`mod_authz_user`, `mod_autoindex`, `mod_cgi`, `mod_dav_fs`,\n`mod_dav_svn`, `mod_deflate`, `mod_dir`, `mod_env`, `mod_expires`,\n`mod_headers`, `mod_ldap`, `mod_log_config`, `mod_mime`,\n`mod_negotiation`, `mod_proxy`, `mod_proxy_ajp`, `mod_proxy_balancer`,\n`mod_proxy_connect`, `mod_proxy_http`, `mod_python`, `mod_rewrite`,\n`mod_setenvif`, `mod_status`, `mod_wsgi`, `mod_xsendfile`.\n\nOn RHEL Family distributions, certain modules ship with a config file\nwith the package. The recipes here may delete those configuration\nfiles to ensure they don't conflict with the settings from the\ncookbook, which will use per-module configuration in\n`/etc/httpd/mods-enabled`.\n\ndefault\n-------\n\nThe default recipe does a number of things to set up Apache HTTPd. It\nalso includes a number of modules based on the attribute\n`node['apache']['default_modules']` as recipes.\n\nlogrotate\n---------\n\nLogrotate adds a logrotate entry for your apache2 logs. This recipe\nrequires the `logrotate` cookbook; ensure that `recipe[logrotate]` is\nin the node's expanded run list.\n\nmod\\_auth\\_cas\n--------------\n\nThis recipe installs the proper package and enables the `auth_cas`\nmodule. It can install from source or package. Package is the default,\nset the attribute `node['apache']['mod_auth_cas']['from_source']` to\ntrue to enable source installation. Modify the version to install by\nchanging the attribute\n`node['apache']['mod_auth_cas']['source_revision']`. It is a version\ntag by default, but could be master, or another tag, or branch.\n\nThe module configuration is written out with the `CASCookiePath` set,\notherwise an error loading the module may cause Apache to not start.\n\n**Note**: This recipe does not work on EL 6 platforms unless\nepel-testing repository is enabled (outside the scope of this\ncookbook), or the package version 1.0.8.1-3.el6 or higher is otherwise\navailable to the system due to this bug:\n\nhttps://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=708550\n\nmod\\_auth\\_openid\n-----------------\n\n**Changed via COOK-915**\n\nThis recipe compiles the module from source. In addition to\n`build-essential`, some other packages are included for installation\nlike the GNU C++ compiler and development headers.\n\nTo use the module in your own cookbooks to authenticate systems using\nOpenIDs, specify an array of OpenIDs that are allowed to authenticate\nwith the attribute `node['apache']['allowed_openids']`. Use the\nfollowing in a vhost to protect with OpenID authentication:\n\n AuthType OpenID require user <%= node['apache']['allowed_openids'].join(' ') %>\n AuthOpenIDDBLocation <%= node['apache']['mod_auth_openid']['dblocation'] %>\n\nChange the DBLocation with the attribute as required; this file is in\na different location than previous versions, see below. It should be a\nsane default for most platforms, though, see\n`attributes/mod_auth_openid.rb`.\n\n### Changes from COOK-915:\n\n* `AuthType OpenID` instead of `AuthOpenIDEnabled On`.\n* `require user` instead of `AuthOpenIDUserProgram`.\n* A bug(?) in `mod_auth_openid` causes it to segfault when attempting\n to update the database file if the containing directory is not\n writable by the HTTPD process owner (e.g., www-data), even if the\n file is writable. In order to not interfere with other settings from\n the default recipe in this cookbook, the db file is moved.\n\nmod\\_fastcgi\n------------\n\nInstall the fastcgi package and enable the module.\n\nOnly work on Debian/Ubuntu\n\nmod\\_fcgid\n----------\n\nInstalls the fcgi package and enables the module. Requires EPEL on\nRHEL family.\n\nOn RHEL family, this recipe will delete the fcgid.conf and on version\n6+, create the /var/run/httpd/mod_fcgid` directory, which prevents the\nemergency error:\n\n [emerg] (2)No such file or directory: mod_fcgid: Can't create shared memory for size XX bytes\n\nmod\\_php5\n--------\n\nSimply installs the appropriate package on Debian, Ubuntu and\nArchLinux.\n\nOn Red Hat family distributions including Fedora, the php.conf that\ncomes with the package is removed. On RHEL platforms less than v6, the\n`php53` package is used.\n\nmod\\_ssl\n--------\n\nBesides installing and enabling `mod_ssl`, this recipe will append\nport 443 to the `node['apache']['listen_ports']` attribute array and\nupdate the ports.conf.\n\ngod\\_monitor\n------------\n\nSets up a `god` monitor for Apache. External requirements are the\n`god` and `runit` cookbooks from Opscode. When using this recipe,\ninclude `recipe[god]` in the node's expanded run list to ensure the\nclient downloads it; `god` depends on runit so that will also be\ndownloaded.\n\n**Note** This recipe is not tested under test-kitchen yet and is\n pending fix in COOK-744.\n\nDefinitions\n===========\n\nThe cookbook provides a few definitions. At some point in the future\nthese definitions may be refactored into lightweight resources and\nproviders as suggested by\n[foodcritic rule FC015](http://acrmp.github.com/foodcritic/#FC015).\n\napache\\_conf\n------------\n\nSets up configuration file for an Apache module from a template. The\ntemplate should be in the same cookbook where the definition is used.\nThis is used by the `apache_module` definition and is not often used\ndirectly.\n\nThis will use a template resource to write the module's configuration\nfile in the `mods-available` under the Apache configuration directory\n(`node['apache']['dir']`). This is a platform-dependent location. See\n__apache\\_module__.\n\n### Parameters:\n\n* `name` - Name of the template. When used from the `apache_module`,\n it will use the same name as the module.\n\n### Examples:\n\nCreate `#{node['apache']['dir']}/mods-available/alias.conf`.\n\n apache_conf \"alias\"\n\napache\\_module\n--------------\n\nEnable or disable an Apache module in\n`#{node['apache']['dir']}/mods-available` by calling `a2enmod` or\n`a2dismod` to manage the symbolic link in\n`#{node['apache']['dir']}/mods-enabled`. If the module has a\nconfiguration file, a template should be created in the cookbook where\nthe definition is used. See __Examples__.\n\n### Parameters:\n\n* `name` - Name of the module enabled or disabled with the `a2enmod` or `a2dismod` scripts.\n* `identifier` - String to identify the module for the `LoadModule` directive. Not typically needed, defaults to `#{name}_module`\n* `enable` - Default true, which uses `a2enmod` to enable the module. If false, the module will be disabled with `a2dismod`.\n* `conf` - Default false. Set to true if the module has a config file, which will use `apache_conf` for the file.\n* `filename` - specify the full name of the file, e.g.\n\n### Examples:\n\nEnable the ssl module, which also has a configuration template in `templates/default/mods/ssl.conf.erb`.\n\n apache_module \"ssl\" do\n conf true\n end\n\nEnable the php5 module, which has a different filename than the module default:\n\n apache_module \"php5\" do\n filename \"libphp5.so\"\n end\n\nDisable a module:\n\n apache_module \"disabled_module\" do\n enable false\n end\n\nSee the recipes directory for many more examples of `apache_module`.\n\napache\\_site\n------------\n\nEnable or disable a VirtualHost in\n`#{node['apache']['dir']}/sites-available` by calling a2ensite or\na2dissite to manage the symbolic link in\n`#{node['apache']['dir']}/sites-enabled`.\n\nThe template for the site must be managed as a separate resource. To\ncombine the template with enabling a site, see `web_app`.\n\n### Parameters:\n\n* `name` - Name of the site.\n* `enable` - Default true, which uses `a2ensite` to enable the site. If false, the site will be disabled with `a2dissite`.\n\nweb\\_app\n--------\n\nManage a template resource for a VirtualHost site, and enable it with\n`apache_site`. This is commonly done for managing web applications\nsuch as Ruby on Rails, PHP or Django, and the default behavior\nreflects that. However it is flexible.\n\nThis definition includes some recipes to make sure the system is\nconfigured to have Apache and some sane default modules:\n\n* `apache2`\n* `apache2::mod_rewrite`\n* `apache2::mod_deflate`\n* `apache2::mod_headers`\n\nIt will then configure the template (see __Parameters__ and\n__Examples__ below), and enable or disable the site per the `enable`\nparameter.\n\n### Parameters:\n\nCurrent parameters used by the definition:\n\n* `name` - The name of the site. The template will be written to\n `#{node['apache']['dir']}/sites-available/#{params['name']}.conf`\n* `cookbook` - Optional. Cookbook where the source template is. If\n this is not defined, Chef will use the named template in the\n cookbook where the definition is used.\n* `template` - Default `web_app.conf.erb`, source template file.\n* `enable` - Default true. Passed to the `apache_site` definition.\n\nAdditional parameters can be defined when the definition is called in\na recipe, see __Examples__.\n\n### Examples:\n\nAll parameters are passed into the template. You can use whatever you\nlike. The apache2 cookbook comes with a `web_app.conf.erb` template as\nan example. The following parameters are used in the template:\n\n* `server_name` - ServerName directive.\n* `server_aliases` - ServerAlias directive. Must be an array of aliases.\n* `docroot` - DocumentRoot directive.\n* `application_name` - Used in RewriteLog directive. Will be set to the `name` parameter.\n* `directory_index` - Allow overriding the default DirectoryIndex setting, optional\n* `directory_options` - Override Options on the docroot, for example to add parameters like Includes or Indexes, optional.\n* `allow_override` - Modify the AllowOverride directive on the docroot to support apps that need .htaccess to modify configuration or require authentication.\n\nTo use the default web_app, for example:\n\n web_app \"my_site\" do\n server_name node['hostname']\n server_aliases [node['fqdn'], \"my-site.example.com\"]\n docroot \"/srv/www/my_site\"\n end\n\nThe parameters specified will be used as:\n\n* `@params[:server_name]`\n* `@params[:server_aliases]`\n* `@params[:docroot]`\n\nIn the template. When you write your own, the `@` is significant.\n\nFor more information about Definitions and parameters, see the\n[Chef Wiki](http://wiki.opscode.com/display/chef/Definitions)\n\nUsage\n=====\n\nUsing this cookbook is relatively straightforward. Add the desired\nrecipes to the run list of a node, or create a role. Depending on your\nenvironment, you may have multiple roles that use different recipes\nfrom this cookbook. Adjust any attributes as desired. For example, to\ncreate a basic role for web servers that provide both HTTP and HTTPS:\n\n % cat roles/webserver.rb\n name \"webserver\"\n description \"Systems that serve HTTP and HTTPS\"\n run_list(\n \"recipe[apache2]\",\n \"recipe[apache2::mod_ssl]\"\n )\n default_attributes(\n \"apache\" => {\n \"listen_ports\" => [\"80\", \"443\"]\n }\n )\n\nFor examples of using the definitions in your own recipes, see their\nrespective sections above.\n\nLicense and Authors\n===================\n\n* Author:: Adam Jacob \n* Author:: Joshua Timberman \n* Author:: Bryan McLellan \n* Author:: Dave Esposito \n* Author:: David Abdemoulaie \n* Author:: Edmund Haselwanter \n* Author:: Eric Rochester \n* Author:: Jim Browne \n* Author:: Matthew Kent \n* Author:: Nathen Harvey \n* Author:: Ringo De Smet \n* Author:: Sean OMeara \n* Author:: Seth Chisamore \n* Author:: Gilles Devaux \n\n* Copyright:: 2009-2012, Opscode, Inc\n* Copyright:: 2011, Atriso\n* Copyright:: 2011, CustomInk, LLC.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "amazon": ">= 0.0.0", + "arch": ">= 0.0.0", + "centos": ">= 0.0.0", + "debian": ">= 0.0.0", + "fedora": ">= 0.0.0", + "freebsd": ">= 0.0.0", + "redhat": ">= 0.0.0", + "scientific": ">= 0.0.0", + "ubuntu": ">= 0.0.0" + }, + "dependencies": { + "iptables": ">= 0.0.0", + "logrotate": ">= 0.0.0", + "pacman": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + "apache": { + "display_name": "Apache Hash", + "description": "Hash of Apache attributes", + "type": "hash" + }, + "apache/dir": { + "display_name": "Apache Directory", + "description": "Location for Apache configuration", + "default": "/etc/apache2", + "recipes": [ + "apache2::default" + ] + }, + "apache/log_dir": { + "display_name": "Apache Log Directory", + "description": "Location for Apache logs", + "default": "/etc/apache2", + "recipes": [ + "apache2::default\", \"apache2::logrotate" + ] + }, + "apache/user": { + "display_name": "Apache User", + "description": "User Apache runs as", + "default": "www-data", + "recipes": [ + "apache2::default" + ] + }, + "apache/binary": { + "display_name": "Apache Binary", + "description": "Apache server daemon program", + "default": "/usr/sbin/apache2", + "recipes": [ + "apache2::default" + ] + }, + "apache/icondir": { + "display_name": "Apache Icondir", + "description": "Directory location for icons", + "default": "/usr/share/apache2/icons", + "recipes": [ + "apache2::default" + ] + }, + "apache/listen_addresses": { + "display_name": "Apache Listen Addresses", + "description": "Addresses that Apache should listen on", + "type": "array", + "default": [ + "*" + ], + "recipes": [ + "apache2::default" + ] + }, + "apache/listen_ports": { + "display_name": "Apache Listen Ports", + "description": "Ports that Apache should listen on", + "type": "array", + "default": [ + "80", + "443" + ], + "recipes": [ + "apache2::default" + ] + }, + "apache/contact": { + "display_name": "Apache Contact", + "description": "Email address of webmaster", + "default": "ops@example.com", + "recipes": [ + "apache2::default" + ] + }, + "apache/timeout": { + "display_name": "Apache Timeout", + "description": "Connection timeout value", + "default": "300", + "recipes": [ + "apache2::default" + ] + }, + "apache/keepalive": { + "display_name": "Apache Keepalive", + "description": "HTTP persistent connections", + "default": "On", + "recipes": [ + "apache2::default" + ] + }, + "apache/keepaliverequests": { + "display_name": "Apache Keepalive Requests", + "description": "Number of requests allowed on a persistent connection", + "default": "100", + "recipes": [ + "apache2::default" + ] + }, + "apache/keepalivetimeout": { + "display_name": "Apache Keepalive Timeout", + "description": "Time to wait for requests on persistent connection", + "default": "5", + "recipes": [ + "apache2::default" + ] + }, + "apache/servertokens": { + "display_name": "Apache Server Tokens", + "description": "Server response header", + "default": "Prod", + "recipes": [ + "apache2::default" + ] + }, + "apache/serversignature": { + "display_name": "Apache Server Signature", + "description": "Configure footer on server-generated documents", + "default": "On", + "recipes": [ + "apache2::default" + ] + }, + "apache/traceenable": { + "display_name": "Apache Trace Enable", + "description": "Determine behavior of TRACE requests", + "default": "On", + "recipes": [ + "apache2::default" + ] + }, + "apache/allowed_openids": { + "display_name": "Apache Allowed OpenIDs", + "description": "Array of OpenIDs allowed to authenticate", + "default": "", + "recipes": [ + "apache2::default" + ] + }, + "apache/prefork": { + "display_name": "Apache Prefork", + "description": "Hash of Apache prefork tuning attributes.", + "type": "hash", + "recipes": [ + "apache2::default" + ] + }, + "apache/prefork/startservers": { + "display_name": "Apache Prefork MPM StartServers", + "description": "Number of MPM servers to start", + "default": "16", + "recipes": [ + "apache2::default" + ] + }, + "apache/prefork/minspareservers": { + "display_name": "Apache Prefork MPM MinSpareServers", + "description": "Minimum number of spare server processes", + "default": "16", + "recipes": [ + "apache2::default" + ] + }, + "apache/prefork/maxspareservers": { + "display_name": "Apache Prefork MPM MaxSpareServers", + "description": "Maximum number of spare server processes", + "default": "32", + "recipes": [ + "apache2::default" + ] + }, + "apache/prefork/serverlimit": { + "display_name": "Apache Prefork MPM ServerLimit", + "description": "Upper limit on configurable server processes", + "default": "400", + "recipes": [ + "apache2::default" + ] + }, + "apache/prefork/maxclients": { + "display_name": "Apache Prefork MPM MaxClients", + "description": "Maximum number of simultaneous connections", + "default": "400", + "recipes": [ + "apache2::default" + ] + }, + "apache/prefork/maxrequestsperchild": { + "display_name": "Apache Prefork MPM MaxRequestsPerChild", + "description": "Maximum number of request a child process will handle", + "default": "10000", + "recipes": [ + "apache2::default" + ] + }, + "apache/worker": { + "display_name": "Apache Worker", + "description": "Hash of Apache prefork tuning attributes.", + "type": "hash", + "recipes": [ + "apache2::default" + ] + }, + "apache/worker/startservers": { + "display_name": "Apache Worker MPM StartServers", + "description": "Initial number of server processes to start", + "default": "4", + "recipes": [ + "apache2::default" + ] + }, + "apache/worker/maxclients": { + "display_name": "Apache Worker MPM MaxClients", + "description": "Maximum number of simultaneous connections", + "default": "1024", + "recipes": [ + "apache2::default" + ] + }, + "apache/worker/minsparethreads": { + "display_name": "Apache Worker MPM MinSpareThreads", + "description": "Minimum number of spare worker threads", + "default": "64", + "recipes": [ + "apache2::default" + ] + }, + "apache/worker/maxsparethreads": { + "display_name": "Apache Worker MPM MaxSpareThreads", + "description": "Maximum number of spare worker threads", + "default": "192", + "recipes": [ + "apache2::default" + ] + }, + "apache/worker/threadsperchild": { + "display_name": "Apache Worker MPM ThreadsPerChild", + "description": "Constant number of worker threads in each server process", + "default": "64", + "recipes": [ + "apache2::default" + ] + }, + "apache/worker/maxrequestsperchild": { + "display_name": "Apache Worker MPM MaxRequestsPerChild", + "description": "Maximum number of request a child process will handle", + "default": "0", + "recipes": [ + "apache2::default" + ] + }, + "apache/default_modules": { + "display_name": "Apache Default Modules", + "description": "Default modules to enable via recipes", + "type": "array", + "default": [ + "status", + "alias", + "auth_basic", + "authn_file", + "authz_default", + "authz_groupfile", + "authz_host", + "authz_user", + "autoindex", + "dir", + "env", + "mime", + "negotiation", + "setenvif" + ], + "recipes": [ + "apache2::default" + ] + }, + "apache/mod_ssl/cipher_suite": { + "display_name": "Apache mod_ssl Cipher Suite", + "description": "String of SSL ciphers to use for SSLCipherSuite", + "default": "RC4-SHA:HIGH:!ADH", + "recipes": [ + "apache2::default" + ] + } + }, + "groupings": { + }, + "recipes": { + "apache2": "Main Apache configuration", + "apache2::logrotate": "Rotate apache2 logs. Requires logrotate cookbook", + "apache2::mod_alias": "Apache module \"alias\" with config file", + "apache2::mod_apreq2": "Apache module \"apreq\"", + "apache2::mod_auth_basic": "Apache module \"auth_basic\"", + "apache2::mod_auth_digest": "Apache module \"auth_digest\"", + "apache2::mod_auth_openid": "Apache module \"authopenid\"", + "apache2::mod_authn_file": "Apache module \"authn_file\"", + "apache2::mod_authnz_ldap": "Apache module \"authnz_ldap\"", + "apache2::mod_authz_default": "Apache module \"authz_default\"", + "apache2::mod_authz_groupfile": "Apache module \"authz_groupfile\"", + "apache2::mod_authz_host": "Apache module \"authz_host\"", + "apache2::mod_authz_user": "Apache module \"authz_user\"", + "apache2::mod_autoindex": "Apache module \"autoindex\" with config file", + "apache2::mod_cgi": "Apache module \"cgi\"", + "apache2::mod_dav": "Apache module \"dav\"", + "apache2::mod_dav_svn": "Apache module \"dav_svn\"", + "apache2::mod_deflate": "Apache module \"deflate\" with config file", + "apache2::mod_dir": "Apache module \"dir\" with config file", + "apache2::mod_env": "Apache module \"env\"", + "apache2::mod_expires": "Apache module \"expires\"", + "apache2::mod_fcgid": "Apache module \"fcgid\", package on ubuntu/debian, rhel/centos, compile source on suse; with config file", + "apache2::mod_headers": "Apache module \"headers\"", + "apache2::mod_include": "Apache module \"include\"", + "apache2::mod_ldap": "Apache module \"ldap\"", + "apache2::mod_log_config": "Apache module \"log_config\"", + "apache2::mod_mime": "Apache module \"mime\" with config file", + "apache2::mod_negotiation": "Apache module \"negotiation\" with config file", + "apache2::mod_pagespeed": "Apache module \"pagespeed\" with config file", + "apache2::mod_perl": "Apache module \"perl\"", + "apache2::mod_php5": "Apache module \"php5\"", + "apache2::mod_proxy": "Apache module \"proxy\" with config file", + "apache2::mod_proxy_ajp": "Apache module \"proxy_ajp\"", + "apache2::mod_proxy_balancer": "Apache module \"proxy_balancer\"", + "apache2::mod_proxy_connect": "Apache module \"proxy_connect\"", + "apache2::mod_proxy_http": "Apache module \"proxy_http\"", + "apache2::mod_python": "Apache module \"python\"", + "apache2::mod_rewrite": "Apache module \"rewrite\"", + "apache2::mod_setenvif": "Apache module \"setenvif\" with config file", + "apache2::mod_ssl": "Apache module \"ssl\" with config file, adds port 443 to listen_ports", + "apache2::mod_status": "Apache module \"status\" with config file", + "apache2::mod_xsendfile": "Apache module \"xsendfile\"" + } +} \ No newline at end of file diff --git a/chef/cookbooks/apache2/metadata.rb b/chef/cookbooks/apache2/metadata.rb index a5aa510..052f5e2 100644 --- a/chef/cookbooks/apache2/metadata.rb +++ b/chef/cookbooks/apache2/metadata.rb @@ -1,213 +1,264 @@ -name "apache2" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions" +name 'apache2' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "1.7.0" -recipe "apache2", "Main Apache configuration" -recipe "apache2::logrotate", "Rotate apache2 logs. Requires logrotate cookbook" -recipe "apache2::mod_alias", "Apache module 'alias' with config file" -recipe "apache2::mod_apreq2", "Apache module 'apreq'" -recipe "apache2::mod_auth_basic", "Apache module 'auth_basic'" -recipe "apache2::mod_auth_digest", "Apache module 'auth_digest'" -recipe "apache2::mod_auth_openid", "Apache module 'authopenid'" -recipe "apache2::mod_authn_file", "Apache module 'authn_file'" -recipe "apache2::mod_authnz_ldap", "Apache module 'authnz_ldap'" -recipe "apache2::mod_authz_default", "Apache module 'authz_default'" -recipe "apache2::mod_authz_groupfile", "Apache module 'authz_groupfile'" -recipe "apache2::mod_authz_host", "Apache module 'authz_host'" -recipe "apache2::mod_authz_user", "Apache module 'authz_user'" -recipe "apache2::mod_autoindex", "Apache module 'autoindex' with config file" -recipe "apache2::mod_cgi", "Apache module 'cgi'" -recipe "apache2::mod_dav", "Apache module 'dav'" -recipe "apache2::mod_dav_svn", "Apache module 'dav_svn'" -recipe "apache2::mod_deflate", "Apache module 'deflate' with config file" -recipe "apache2::mod_dir", "Apache module 'dir' with config file" -recipe "apache2::mod_env", "Apache module 'env'" -recipe "apache2::mod_expires", "Apache module 'expires'" -recipe "apache2::mod_fcgid", "Apache module 'fcgid', package on ubuntu/debian, rhel/centos, compile source on suse; with config file" -recipe "apache2::mod_headers", "Apache module 'headers'" -recipe "apache2::mod_include", "Apache module 'include'" -recipe "apache2::mod_ldap", "Apache module 'ldap'" -recipe "apache2::mod_log_config", "Apache module 'log_config'" -recipe "apache2::mod_mime", "Apache module 'mime' with config file" -recipe "apache2::mod_negotiation", "Apache module 'negotiation' with config file" -recipe "apache2::mod_perl", "Apache module 'perl'" -recipe "apache2::mod_php5", "Apache module 'php5'" -recipe "apache2::mod_proxy", "Apache module 'proxy' with config file" -recipe "apache2::mod_proxy_ajp", "Apache module 'proxy_ajp'" -recipe "apache2::mod_proxy_balancer", "Apache module 'proxy_balancer'" -recipe "apache2::mod_proxy_connect", "Apache module 'proxy_connect'" -recipe "apache2::mod_proxy_http", "Apache module 'proxy_http'" -recipe "apache2::mod_python", "Apache module 'python'" -recipe "apache2::mod_rewrite", "Apache module 'rewrite'" -recipe "apache2::mod_setenvif", "Apache module 'setenvif' with config file" -recipe "apache2::mod_ssl", "Apache module 'ssl' with config file, adds port 443 to listen_ports" -recipe "apache2::mod_status", "Apache module 'status' with config file" -recipe "apache2::mod_xsendfile", "Apache module 'xsendfile'" +version '1.9.6' +recipe 'apache2', 'Main Apache configuration' +recipe 'apache2::logrotate', 'Rotate apache2 logs. Requires logrotate cookbook' +recipe 'apache2::mod_alias', 'Apache module "alias" with config file' +recipe 'apache2::mod_apreq2', 'Apache module "apreq"' +recipe 'apache2::mod_auth_basic', 'Apache module "auth_basic"' +recipe 'apache2::mod_auth_digest', 'Apache module "auth_digest"' +recipe 'apache2::mod_auth_openid', 'Apache module "authopenid"' +recipe 'apache2::mod_authn_file', 'Apache module "authn_file"' +recipe 'apache2::mod_authnz_ldap', 'Apache module "authnz_ldap"' +recipe 'apache2::mod_authz_default', 'Apache module "authz_default"' +recipe 'apache2::mod_authz_groupfile', 'Apache module "authz_groupfile"' +recipe 'apache2::mod_authz_host', 'Apache module "authz_host"' +recipe 'apache2::mod_authz_user', 'Apache module "authz_user"' +recipe 'apache2::mod_autoindex', 'Apache module "autoindex" with config file' +recipe 'apache2::mod_cgi', 'Apache module "cgi"' +recipe 'apache2::mod_dav', 'Apache module "dav"' +recipe 'apache2::mod_dav_svn', 'Apache module "dav_svn"' +recipe 'apache2::mod_deflate', 'Apache module "deflate" with config file' +recipe 'apache2::mod_dir', 'Apache module "dir" with config file' +recipe 'apache2::mod_env', 'Apache module "env"' +recipe 'apache2::mod_expires', 'Apache module "expires"' +recipe 'apache2::mod_fcgid', 'Apache module "fcgid", package on ubuntu/debian, rhel/centos, compile source on suse; with config file' +recipe 'apache2::mod_headers', 'Apache module "headers"' +recipe 'apache2::mod_include', 'Apache module "include"' +recipe 'apache2::mod_ldap', 'Apache module "ldap"' +recipe 'apache2::mod_log_config', 'Apache module "log_config"' +recipe 'apache2::mod_mime', 'Apache module "mime" with config file' +recipe 'apache2::mod_negotiation', 'Apache module "negotiation" with config file' +recipe 'apache2::mod_pagespeed', 'Apache module "pagespeed" with config file' +recipe 'apache2::mod_perl', 'Apache module "perl"' +recipe 'apache2::mod_php5', 'Apache module "php5"' +recipe 'apache2::mod_proxy', 'Apache module "proxy" with config file' +recipe 'apache2::mod_proxy_ajp', 'Apache module "proxy_ajp"' +recipe 'apache2::mod_proxy_balancer', 'Apache module "proxy_balancer"' +recipe 'apache2::mod_proxy_connect', 'Apache module "proxy_connect"' +recipe 'apache2::mod_proxy_http', 'Apache module "proxy_http"' +recipe 'apache2::mod_python', 'Apache module "python"' +recipe 'apache2::mod_rewrite', 'Apache module "rewrite"' +recipe 'apache2::mod_setenvif', 'Apache module "setenvif" with config file' +recipe 'apache2::mod_ssl', 'Apache module "ssl" with config file, adds port 443 to listen_ports' +recipe 'apache2::mod_status', 'Apache module "status" with config file' +recipe 'apache2::mod_xsendfile', 'Apache module "xsendfile"' -%w{redhat centos scientific fedora debian ubuntu arch freebsd amazon}.each do |os| - supports os -end +depends 'iptables' -attribute "apache", - :display_name => "Apache Hash", - :description => "Hash of Apache attributes", - :type => "hash" +supports 'amazon' +supports 'arch' +supports 'centos' +supports 'debian' +supports 'fedora' +supports 'freebsd' +supports 'redhat' +supports 'scientific' +supports 'ubuntu' -attribute "apache/dir", - :display_name => "Apache Directory", - :description => "Location for Apache configuration", - :default => "/etc/apache2" +depends 'logrotate' +depends 'pacman' -attribute "apache/log_dir", - :display_name => "Apache Log Directory", - :description => "Location for Apache logs", - :default => "/etc/apache2" +attribute 'apache', + :display_name => 'Apache Hash', + :description => 'Hash of Apache attributes', + :type => 'hash' -attribute "apache/user", - :display_name => "Apache User", - :description => "User Apache runs as", - :default => "www-data" +attribute 'apache/dir', + :display_name => 'Apache Directory', + :description => 'Location for Apache configuration', + :default => '/etc/apache2', + :recipes => ['apache2::default'] -attribute "apache/binary", - :display_name => "Apache Binary", - :description => "Apache server daemon program", - :default => "/usr/sbin/apache2" +attribute 'apache/log_dir', + :display_name => 'Apache Log Directory', + :description => 'Location for Apache logs', + :default => '/etc/apache2', + :recipes => ['apache2::default", "apache2::logrotate'] -attribute "apache/icondir", - :display_name => "Apache Icondir", - :description => "Directory location for icons", - :default => "/usr/share/apache2/icons" +attribute 'apache/user', + :display_name => 'Apache User', + :description => 'User Apache runs as', + :default => 'www-data', + :recipes => ['apache2::default'] -attribute "apache/listen_ports", - :display_name => "Apache Listen Ports", - :description => "Ports that Apache should listen on", - :type => "array", - :default => ["80", "443"] +attribute 'apache/binary', + :display_name => 'Apache Binary', + :description => 'Apache server daemon program', + :default => '/usr/sbin/apache2', + :recipes => ['apache2::default'] -attribute "apache/contact", - :display_name => "Apache Contact", - :description => "Email address of webmaster", - :default => "ops@example.com" +attribute 'apache/icondir', + :display_name => 'Apache Icondir', + :description => 'Directory location for icons', + :default => '/usr/share/apache2/icons', + :recipes => ['apache2::default'] -attribute "apache/timeout", - :display_name => "Apache Timeout", - :description => "Connection timeout value", - :default => "300" +attribute 'apache/listen_addresses', + :display_name => 'Apache Listen Addresses', + :description => 'Addresses that Apache should listen on', + :type => 'array', + :default => %w[*], + :recipes => ['apache2::default'] -attribute "apache/keepalive", - :display_name => "Apache Keepalive", - :description => "HTTP persistent connections", - :default => "On" +attribute 'apache/listen_ports', + :display_name => 'Apache Listen Ports', + :description => 'Ports that Apache should listen on', + :type => 'array', + :default => %w[80 443], + :recipes => ['apache2::default'] -attribute "apache/keepaliverequests", - :display_name => "Apache Keepalive Requests", - :description => "Number of requests allowed on a persistent connection", - :default => "100" +attribute 'apache/contact', + :display_name => 'Apache Contact', + :description => 'Email address of webmaster', + :default => 'ops@example.com', + :recipes => ['apache2::default'] -attribute "apache/keepalivetimeout", - :display_name => "Apache Keepalive Timeout", - :description => "Time to wait for requests on persistent connection", - :default => "5" +attribute 'apache/timeout', + :display_name => 'Apache Timeout', + :description => 'Connection timeout value', + :default => '300', + :recipes => ['apache2::default'] -attribute "apache/servertokens", - :display_name => "Apache Server Tokens", - :description => "Server response header", - :default => "Prod" +attribute 'apache/keepalive', + :display_name => 'Apache Keepalive', + :description => 'HTTP persistent connections', + :default => 'On', + :recipes => ['apache2::default'] -attribute "apache/serversignature", - :display_name => "Apache Server Signature", - :description => "Configure footer on server-generated documents", - :default => "On" +attribute 'apache/keepaliverequests', + :display_name => 'Apache Keepalive Requests', + :description => 'Number of requests allowed on a persistent connection', + :default => '100', + :recipes => ['apache2::default'] -attribute "apache/traceenable", - :display_name => "Apache Trace Enable", - :description => "Determine behavior of TRACE requests", - :default => "On" +attribute 'apache/keepalivetimeout', + :display_name => 'Apache Keepalive Timeout', + :description => 'Time to wait for requests on persistent connection', + :default => '5', + :recipes => ['apache2::default'] -attribute "apache/allowed_openids", - :display_name => "Apache Allowed OpenIDs", - :description => "Array of OpenIDs allowed to authenticate", - :default => "" +attribute 'apache/servertokens', + :display_name => 'Apache Server Tokens', + :description => 'Server response header', + :default => 'Prod', + :recipes => ['apache2::default'] -attribute "apache/prefork", - :display_name => "Apache Prefork", - :description => "Hash of Apache prefork tuning attributes.", - :type => "hash" +attribute 'apache/serversignature', + :display_name => 'Apache Server Signature', + :description => 'Configure footer on server-generated documents', + :default => 'On', + :recipes => ['apache2::default'] -attribute "apache/prefork/startservers", - :display_name => "Apache Prefork MPM StartServers", - :description => "Number of MPM servers to start", - :default => "16" +attribute 'apache/traceenable', + :display_name => 'Apache Trace Enable', + :description => 'Determine behavior of TRACE requests', + :default => 'On', + :recipes => ['apache2::default'] -attribute "apache/prefork/minspareservers", - :display_name => "Apache Prefork MPM MinSpareServers", - :description => "Minimum number of spare server processes", - :default => "16" +attribute 'apache/allowed_openids', + :display_name => 'Apache Allowed OpenIDs', + :description => 'Array of OpenIDs allowed to authenticate', + :default => '', + :recipes => ['apache2::default'] -attribute "apache/prefork/maxspareservers", - :display_name => "Apache Prefork MPM MaxSpareServers", - :description => "Maximum number of spare server processes", - :default => "32" +attribute 'apache/prefork', + :display_name => 'Apache Prefork', + :description => 'Hash of Apache prefork tuning attributes.', + :type => 'hash', + :recipes => ['apache2::default'] -attribute "apache/prefork/serverlimit", - :display_name => "Apache Prefork MPM ServerLimit", - :description => "Upper limit on configurable server processes", - :default => "400" +attribute 'apache/prefork/startservers', + :display_name => 'Apache Prefork MPM StartServers', + :description => 'Number of MPM servers to start', + :default => '16', + :recipes => ['apache2::default'] -attribute "apache/prefork/maxclients", - :display_name => "Apache Prefork MPM MaxClients", - :description => "Maximum number of simultaneous connections", - :default => "400" +attribute 'apache/prefork/minspareservers', + :display_name => 'Apache Prefork MPM MinSpareServers', + :description => 'Minimum number of spare server processes', + :default => '16', + :recipes => ['apache2::default'] -attribute "apache/prefork/maxrequestsperchild", - :display_name => "Apache Prefork MPM MaxRequestsPerChild", - :description => "Maximum number of request a child process will handle", - :default => "10000" +attribute 'apache/prefork/maxspareservers', + :display_name => 'Apache Prefork MPM MaxSpareServers', + :description => 'Maximum number of spare server processes', + :default => '32', + :recipes => ['apache2::default'] -attribute "apache/worker", - :display_name => "Apache Worker", - :description => "Hash of Apache prefork tuning attributes.", - :type => "hash" +attribute 'apache/prefork/serverlimit', + :display_name => 'Apache Prefork MPM ServerLimit', + :description => 'Upper limit on configurable server processes', + :default => '400', + :recipes => ['apache2::default'] -attribute "apache/worker/startservers", - :display_name => "Apache Worker MPM StartServers", - :description => "Initial number of server processes to start", - :default => "4" +attribute 'apache/prefork/maxclients', + :display_name => 'Apache Prefork MPM MaxClients', + :description => 'Maximum number of simultaneous connections', + :default => '400', + :recipes => ['apache2::default'] -attribute "apache/worker/maxclients", - :display_name => "Apache Worker MPM MaxClients", - :description => "Maximum number of simultaneous connections", - :default => "1024" +attribute 'apache/prefork/maxrequestsperchild', + :display_name => 'Apache Prefork MPM MaxRequestsPerChild', + :description => 'Maximum number of request a child process will handle', + :default => '10000', + :recipes => ['apache2::default'] -attribute "apache/worker/minsparethreads", - :display_name => "Apache Worker MPM MinSpareThreads", - :description => "Minimum number of spare worker threads", - :default => "64" +attribute 'apache/worker', + :display_name => 'Apache Worker', + :description => 'Hash of Apache prefork tuning attributes.', + :type => 'hash', + :recipes => ['apache2::default'] -attribute "apache/worker/maxsparethreads", - :display_name => "Apache Worker MPM MaxSpareThreads", - :description => "Maximum number of spare worker threads", - :default => "192" +attribute 'apache/worker/startservers', + :display_name => 'Apache Worker MPM StartServers', + :description => 'Initial number of server processes to start', + :default => '4', + :recipes => ['apache2::default'] -attribute "apache/worker/threadsperchild", - :display_name => "Apache Worker MPM ThreadsPerChild", - :description => "Constant number of worker threads in each server process", - :default => "64" +attribute 'apache/worker/maxclients', + :display_name => 'Apache Worker MPM MaxClients', + :description => 'Maximum number of simultaneous connections', + :default => '1024', + :recipes => ['apache2::default'] -attribute "apache/worker/maxrequestsperchild", - :display_name => "Apache Worker MPM MaxRequestsPerChild", - :description => "Maximum number of request a child process will handle", - :default => "0" +attribute 'apache/worker/minsparethreads', + :display_name => 'Apache Worker MPM MinSpareThreads', + :description => 'Minimum number of spare worker threads', + :default => '64', + :recipes => ['apache2::default'] -attribute "apache/default_modules", - :display_name => "Apache Default Modules", - :description => "Default modules to enable via recipes", - :default => "status alias auth_basic authn_file authz_default authz_groupfile authz_host authz_user autoindex dir env mime negotiation setenvif" +attribute 'apache/worker/maxsparethreads', + :display_name => 'Apache Worker MPM MaxSpareThreads', + :description => 'Maximum number of spare worker threads', + :default => '192', + :recipes => ['apache2::default'] -attribute "apache/mod_ssl/cipher_suite", - :display_name => "Apache mod_ssl Cipher Suite", - :description => "String of SSL ciphers to use for SSLCipherSuite", - :default => "RC4-SHA:HIGH:!ADH" +attribute 'apache/worker/threadsperchild', + :display_name => 'Apache Worker MPM ThreadsPerChild', + :description => 'Constant number of worker threads in each server process', + :default => '64', + :recipes => ['apache2::default'] + +attribute 'apache/worker/maxrequestsperchild', + :display_name => 'Apache Worker MPM MaxRequestsPerChild', + :description => 'Maximum number of request a child process will handle', + :default => '0', + :recipes => ['apache2::default'] + +attribute 'apache/default_modules', + :display_name => 'Apache Default Modules', + :description => 'Default modules to enable via recipes', + :type => 'array', + :default => %w[status alias auth_basic authn_file authz_default authz_groupfile authz_host authz_user autoindex dir env mime negotiation setenvif], + :recipes => ['apache2::default'] + +attribute 'apache/mod_ssl/cipher_suite', + :display_name => 'Apache mod_ssl Cipher Suite', + :description => 'String of SSL ciphers to use for SSLCipherSuite', + :default => 'RC4-SHA:HIGH:!ADH', + :recipes => ['apache2::default'] diff --git a/chef/cookbooks/apache2/recipes/default.rb b/chef/cookbooks/apache2/recipes/default.rb index f5cb6e6..694199b 100644 --- a/chef/cookbooks/apache2/recipes/default.rb +++ b/chef/cookbooks/apache2/recipes/default.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: default # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,70 +17,70 @@ # limitations under the License. # -package "apache2" do +package 'apache2' do package_name node['apache']['package'] end -service "apache2" do +service 'apache2' do case node['platform_family'] - when "rhel", "fedora", "suse" - service_name "httpd" + when 'rhel', 'fedora', 'suse' + service_name 'httpd' # If restarted/reloaded too quickly httpd has a habit of failing. # This may happen with multiple recipes notifying apache to restart - like # during the initial bootstrap. - restart_command "/sbin/service httpd restart && sleep 1" - reload_command "/sbin/service httpd reload && sleep 1" - when "debian" - service_name "apache2" - restart_command "/usr/sbin/invoke-rc.d apache2 restart && sleep 1" - reload_command "/usr/sbin/invoke-rc.d apache2 reload && sleep 1" - when "arch" - service_name "httpd" - when "freebsd" - service_name "apache22" + restart_command '/sbin/service httpd restart && sleep 1' + reload_command '/sbin/service httpd reload && sleep 1' + when 'debian' + service_name 'apache2' + restart_command '/usr/sbin/invoke-rc.d apache2 restart && sleep 1' + reload_command '/usr/sbin/invoke-rc.d apache2 reload && sleep 1' + when 'arch' + service_name 'httpd' + when 'freebsd' + service_name 'apache22' end supports [:restart, :reload, :status] action :enable end -if platform_family?("rhel", "fedora", "arch", "suse", "freebsd") +if platform_family?('rhel', 'fedora', 'arch', 'suse', 'freebsd') directory node['apache']['log_dir'] do - mode 00755 + mode '0755' end - package "perl" + package node['apache']['perl_pkg'] - cookbook_file "/usr/local/bin/apache2_module_conf_generate.pl" do - source "apache2_module_conf_generate.pl" - mode 00755 - owner "root" - group node['apache']['root_group'] + cookbook_file '/usr/local/bin/apache2_module_conf_generate.pl' do + source 'apache2_module_conf_generate.pl' + mode '0755' + owner 'root' + group node['apache']['root_group'] end - %w{sites-available sites-enabled mods-available mods-enabled}.each do |dir| + %w[sites-available sites-enabled mods-available mods-enabled].each do |dir| directory "#{node['apache']['dir']}/#{dir}" do - mode 00755 - owner "root" + mode '0755' + owner 'root' group node['apache']['root_group'] end end - execute "generate-module-list" do + execute 'generate-module-list' do command "/usr/local/bin/apache2_module_conf_generate.pl #{node['apache']['lib_dir']} #{node['apache']['dir']}/mods-available" - action :nothing + action :nothing end - %w{a2ensite a2dissite a2enmod a2dismod}.each do |modscript| + %w[a2ensite a2dissite a2enmod a2dismod].each do |modscript| template "/usr/sbin/#{modscript}" do source "#{modscript}.erb" - mode 00700 - owner "root" + mode '0700' + owner 'root' group node['apache']['root_group'] end end # installed by default on centos/rhel, remove in favour of mods-enabled - %w{ proxy_ajp auth_pam authz_ldap webalizer ssl welcome }.each do |f| + %w[proxy_ajp auth_pam authz_ldap webalizer ssl welcome].each do |f| file "#{node['apache']['dir']}/conf.d/#{f}.conf" do action :delete backup false @@ -94,11 +94,10 @@ if platform_family?("rhel", "fedora", "arch", "suse", "freebsd") end # enable mod_deflate for consistency across distributions - include_recipe "apache2::mod_deflate" + include_recipe 'apache2::mod_deflate' end -if platform_family?("freebsd") - +if platform_family?('freebsd') file "#{node['apache']['dir']}/Includes/no-accf.conf" do action :delete backup false @@ -108,105 +107,95 @@ if platform_family?("freebsd") action :delete end - %w{ + %w[ httpd-autoindex.conf httpd-dav.conf httpd-default.conf httpd-info.conf httpd-languages.conf httpd-manual.conf httpd-mpm.conf httpd-multilang-errordoc.conf httpd-ssl.conf httpd-userdir.conf httpd-vhosts.conf - }.each do |f| - + ].each do |f| file "#{node['apache']['dir']}/extra/#{f}" do action :delete backup false end - end directory "#{node['apache']['dir']}/extra" do action :delete end - end -directory "#{node['apache']['dir']}/ssl" do - mode 00755 - owner "root" - group node['apache']['root_group'] -end - -directory "#{node['apache']['dir']}/conf.d" do - mode 00755 - owner "root" - group node['apache']['root_group'] -end - -directory node['apache']['cache_dir'] do - mode 00755 - owner "root" - group node['apache']['root_group'] +%W[ + #{node['apache']['dir']}/ssl + #{node['apache']['dir']}/conf.d + #{node['apache']['cache_dir']} +].each do |path| + directory path do + mode '0755' + owner 'root' + group node['apache']['root_group'] + end end # Set the preferred execution binary - prefork or worker -template "/etc/sysconfig/httpd" do - source "etc-sysconfig-httpd.erb" - owner "root" - group node['apache']['root_group'] - mode 00644 - notifies :restart, "service[apache2]" - only_if { platform_family?("rhel", "fedora") } +template '/etc/sysconfig/httpd' do + source 'etc-sysconfig-httpd.erb' + owner 'root' + group node['apache']['root_group'] + mode '0644' + notifies :restart, 'service[apache2]' + only_if { platform_family?('rhel', 'fedora') } end -template "apache2.conf" do +template 'apache2.conf' do case node['platform_family'] - when "rhel", "fedora", "arch" + when 'rhel', 'fedora', 'arch' path "#{node['apache']['dir']}/conf/httpd.conf" - when "debian" + when 'debian' path "#{node['apache']['dir']}/apache2.conf" - when "freebsd" + when 'freebsd' path "#{node['apache']['dir']}/httpd.conf" end - source "apache2.conf.erb" - owner "root" - group node['apache']['root_group'] - mode 00644 - notifies :restart, "service[apache2]" + source 'apache2.conf.erb' + owner 'root' + group node['apache']['root_group'] + mode '0644' + notifies :restart, 'service[apache2]' end -template "apache2-conf-security" do - path "#{node['apache']['dir']}/conf.d/security" - source "security.erb" - owner "root" - group node['apache']['root_group'] - mode 00644 - backup false - notifies :restart, "service[apache2]" +template 'apache2-conf-security' do + path "#{node['apache']['dir']}/conf.d/security.conf" + source 'security.erb' + owner 'root' + group node['apache']['root_group'] + mode '0644' + backup false + notifies :restart, 'service[apache2]' end -template "apache2-conf-charset" do - path "#{node['apache']['dir']}/conf.d/charset" - source "charset.erb" - owner "root" - group node['apache']['root_group'] - mode 00644 - backup false - notifies :restart, "service[apache2]" +template 'apache2-conf-charset' do + path "#{node['apache']['dir']}/conf.d/charset.conf" + source 'charset.erb' + owner 'root' + group node['apache']['root_group'] + mode '0644' + backup false + notifies :restart, 'service[apache2]' end template "#{node['apache']['dir']}/ports.conf" do - source "ports.conf.erb" - owner "root" - group node['apache']['root_group'] - variables :apache_listen_ports => node['apache']['listen_ports'].map { |p| p.to_i }.uniq - mode 00644 - notifies :restart, "service[apache2]" + source 'ports.conf.erb' + owner 'root' + group node['apache']['root_group'] + mode '0644' + notifies :restart, 'service[apache2]' end template "#{node['apache']['dir']}/sites-available/default" do - source "default-site.erb" - owner "root" - group node['apache']['root_group'] - mode 00644 - notifies :restart, "service[apache2]" + source 'default-site.erb' + owner 'root' + group node['apache']['root_group'] + mode '0644' + notifies :restart, 'service[apache2]' end node['apache']['default_modules'].each do |mod| @@ -214,10 +203,10 @@ node['apache']['default_modules'].each do |mod| include_recipe "apache2::#{module_recipe_name}" end -apache_site "default" do +apache_site 'default' do enable node['apache']['default_site_enabled'] end -service "apache2" do +service 'apache2' do action :start end diff --git a/chef/cookbooks/apache2/recipes/god_monitor.rb b/chef/cookbooks/apache2/recipes/god_monitor.rb index b3c49eb..f597b9c 100644 --- a/chef/cookbooks/apache2/recipes/god_monitor.rb +++ b/chef/cookbooks/apache2/recipes/god_monitor.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: god_monitor # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,17 +17,17 @@ # limitations under the License. # -apache_service = service "apache2" do +apache_service = service 'apache2' do action :nothing end -start_command = apache_service.start_command -stop_command = apache_service.stop_command +start_command = apache_service.start_command +stop_command = apache_service.stop_command restart_command = apache_service.restart_command -god_monitor "apache2" do - config "apache2.god.erb" - start (start_command)?start_command : "/etc/init.d/#{apache_service.service_name} start" - restart (restart_command)?restart_command : "/etc/init.d/#{apache_service.service_name} restart" - stop (stop_command)?stop_command : "/etc/init.d/#{apache_service.service_name} stop" +god_monitor 'apache2' do + config 'apache2.god.erb' + start start_command || "/etc/init.d/#{apache_service.service_name} start" + restart restart_command || "/etc/init.d/#{apache_service.service_name} restart" + stop stop_command || "/etc/init.d/#{apache_service.service_name} stop" end diff --git a/chef/cookbooks/apache2/recipes/iptables.rb b/chef/cookbooks/apache2/recipes/iptables.rb index fbf1eba..ae0bbb2 100644 --- a/chef/cookbooks/apache2/recipes/iptables.rb +++ b/chef/cookbooks/apache2/recipes/iptables.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: iptables # -# Copyright 2012, Opscode, Inc. +# Copyright 2012-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -iptables_rule "port_apache" +iptables_rule 'port_apache' diff --git a/chef/cookbooks/apache2/recipes/logrotate.rb b/chef/cookbooks/apache2/recipes/logrotate.rb index d90b0fb..723241b 100644 --- a/chef/cookbooks/apache2/recipes/logrotate.rb +++ b/chef/cookbooks/apache2/recipes/logrotate.rb @@ -17,14 +17,14 @@ # limitations under the License. # -apache_service = service "apache2" do +apache_service = service 'apache2' do action :nothing end begin include_recipe 'logrotate' rescue - Chef::Log.warn("The apache::logrotate recipe requires the logrotate cookbook. Install the cookbook with `knife cookbook site install logrotate`.") + Chef::Log.warn('The apache::logrotate recipe requires the logrotate cookbook. Install the cookbook with `knife cookbook site install logrotate`.') end logrotate_app apache_service.service_name do path node['apache']['log_dir'] diff --git a/chef/cookbooks/apache2/recipes/mod_actions.rb b/chef/cookbooks/apache2/recipes/mod_actions.rb index da13b77..2d9336e 100644 --- a/chef/cookbooks/apache2/recipes/mod_actions.rb +++ b/chef/cookbooks/apache2/recipes/mod_actions.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: actions # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "actions" +apache_module 'actions' diff --git a/chef/cookbooks/apache2/recipes/mod_alias.rb b/chef/cookbooks/apache2/recipes/mod_alias.rb index a4618ed..4825f7f 100644 --- a/chef/cookbooks/apache2/recipes/mod_alias.rb +++ b/chef/cookbooks/apache2/recipes/mod_alias.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: alias +# Recipe:: alias # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "alias" do +apache_module 'alias' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_apreq2.rb b/chef/cookbooks/apache2/recipes/mod_apreq2.rb index c13ca10..c08f7e2 100644 --- a/chef/cookbooks/apache2/recipes/mod_apreq2.rb +++ b/chef/cookbooks/apache2/recipes/mod_apreq2.rb @@ -4,7 +4,7 @@ # # modified from the python recipe by Jeremy Bingham # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,30 +19,27 @@ # limitations under the License. # -include_recipe "apache2" +include_recipe 'apache2::default' case node['platform_family'] -when "debian" - - package "libapache2-mod-apreq2" - -when "rhel", "fedora" - - package "libapreq2" do - notifies :run, "execute[generate-module-list]", :immediately +when 'debian' + package 'libapache2-mod-apreq2' +when 'rhel', 'fedora' + package 'libapreq2' do + notifies :run, 'execute[generate-module-list]', :immediately end # seems that the apreq lib is weirdly broken or something - it needs to be - # loaded as "apreq", but on RHEL & derivitatives the file needs a symbolic + # loaded as 'apreq', but on RHEL & derivitatives the file needs a symbolic # link to mod_apreq.so. - link "/usr/lib64/httpd/modules/mod_apreq.so" do - to "/usr/lib64/httpd/modules/mod_apreq2.so" - only_if "test -f /usr/lib64/httpd/modules/mod_apreq2.so" + link '/usr/lib64/httpd/modules/mod_apreq.so' do + to '/usr/lib64/httpd/modules/mod_apreq2.so' + only_if 'test -f /usr/lib64/httpd/modules/mod_apreq2.so' end - link "/usr/lib/httpd/modules/mod_apreq.so" do - to "/usr/lib/httpd/modules/mod_apreq2.so" - only_if "test -f /usr/lib/httpd/modules/mod_apreq2.so" + link '/usr/lib/httpd/modules/mod_apreq.so' do + to '/usr/lib/httpd/modules/mod_apreq2.so' + only_if 'test -f /usr/lib/httpd/modules/mod_apreq2.so' end end @@ -51,4 +48,4 @@ file "#{node['apache']['dir']}/conf.d/apreq.conf" do backup false end -apache_module "apreq" +apache_module 'apreq' diff --git a/chef/cookbooks/apache2/recipes/mod_auth_basic.rb b/chef/cookbooks/apache2/recipes/mod_auth_basic.rb index d30264f..65a2cc4 100644 --- a/chef/cookbooks/apache2/recipes/mod_auth_basic.rb +++ b/chef/cookbooks/apache2/recipes/mod_auth_basic.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: auth_basic +# Recipe:: auth_basic # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "auth_basic" +apache_module 'auth_basic' diff --git a/chef/cookbooks/apache2/recipes/mod_auth_cas.rb b/chef/cookbooks/apache2/recipes/mod_auth_cas.rb index 87096d7..7743d0e 100644 --- a/chef/cookbooks/apache2/recipes/mod_auth_cas.rb +++ b/chef/cookbooks/apache2/recipes/mod_auth_cas.rb @@ -1,50 +1,64 @@ -include_recipe "apache2" +# +# Cookbook Name:: apache2 +# Recipe:: auth_basic +# +# Copyright 2013, Opscode, Inc. +# +# 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. +# + +include_recipe 'apache2::default' if node['apache']['mod_auth_cas']['from_source'] - - package "httpd-devel" do + package 'httpd-devel' do package_name value_for_platform_family( - ["rhel", "fedora", "suse"] => "httpd-devel", - "debian" => "apache2-dev" + %w[rhel fedora suse] => 'httpd-devel', + 'debian' => 'apache2-dev' ) end git '/tmp/mod_auth_cas' do repository 'git://github.com/Jasig/mod_auth_cas.git' - revision node['apache']['mod_auth_cas']['source_revision'] - notifies :run, 'execute[compile mod_auth_cas]', :immediately + revision node['apache']['mod_auth_cas']['source_revision'] + notifies :run, 'execute[compile mod_auth_cas]', :immediately end execute 'compile mod_auth_cas' do command './configure && make && make install' - cwd '/tmp/mod_auth_cas' - not_if "test -f #{node['apache']['libexecdir']}/mod_auth_cas.so" + cwd '/tmp/mod_auth_cas' + not_if "test -f #{node['apache']['libexecdir']}/mod_auth_cas.so" end template "#{node['apache']['dir']}/mods-available/auth_cas.load" do source 'mods/auth_cas.load.erb' - owner 'root' - group node['apache']['root_group'] - mode 00644 + owner 'root' + group node['apache']['root_group'] + mode '0644' end - else case node['platform_family'] - when "debian" + when 'debian' + package 'libapache2-mod-auth-cas' - package "libapache2-mod-auth-cas" - - when "rhel", "fedora" - - yum_package "mod_auth_cas" do - notifies :run, "execute[generate-module-list]", :immediately + when 'rhel', 'fedora' + yum_package 'mod_auth_cas' do + notifies :run, 'execute[generate-module-list]', :immediately end file "#{node['apache']['dir']}/conf.d/auth_cas.conf" do action :delete backup false end - end end @@ -55,5 +69,5 @@ end directory "#{node['apache']['cache_dir']}/mod_auth_cas" do owner node['apache']['user'] group node['apache']['group'] - mode 00700 + mode '0700' end diff --git a/chef/cookbooks/apache2/recipes/mod_auth_digest.rb b/chef/cookbooks/apache2/recipes/mod_auth_digest.rb index 5aef926..ba91def 100644 --- a/chef/cookbooks/apache2/recipes/mod_auth_digest.rb +++ b/chef/cookbooks/apache2/recipes/mod_auth_digest.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: auth_digest +# Recipe:: auth_digest # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "auth_digest" +apache_module 'auth_digest' diff --git a/chef/cookbooks/apache2/recipes/mod_auth_openid.rb b/chef/cookbooks/apache2/recipes/mod_auth_openid.rb index 2aa6614..0faecfd 100644 --- a/chef/cookbooks/apache2/recipes/mod_auth_openid.rb +++ b/chef/cookbooks/apache2/recipes/mod_auth_openid.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: mod_auth_openid # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,46 +18,44 @@ # openid_dev_pkgs = value_for_platform_family( - ["debian"] => %w{automake make g++ apache2-prefork-dev libopkele-dev libopkele3 libtool}, - ["rhel", "fedora"] => %w{gcc-c++ httpd-devel curl-devel libtidy libtidy-devel sqlite-devel pcre-devel openssl-devel make libtool}, - "arch" => ["libopkele"], - "freebsd" => %w{libopkele pcre sqlite3} + 'debian' => %w[automake make g++ apache2-prefork-dev libopkele-dev libopkele3 libtool], + %w[rhel fedora] => %w[gcc-c++ httpd-devel curl-devel libtidy libtidy-devel sqlite-devel pcre-devel openssl-devel make libtool], + 'arch' => %w[libopkele], + 'freebsd' => %w[libopkele pcre sqlite3] ) make_cmd = value_for_platform_family( - "freebsd" => { "default" => "gmake" }, - "default" => "make" + 'freebsd' => { 'default' => 'gmake' }, + 'default' => 'make' ) case node['platform_family'] -when "arch" +when 'arch' + include_recipe 'pacman::default' + + package 'tidyhtml' - include_recipe "pacman" - package "tidyhtml" pacman_aur openid_dev_pkgs.first do action [:build, :install] end - else openid_dev_pkgs.each do |pkg| - package pkg - end end case node['platform_family'] -when "rhel", "fedora" +when 'rhel', 'fedora' remote_file "#{Chef::Config['file_cache_path']}/libopkele-2.0.4.tar.gz" do - source "http://kin.klever.net/dist/libopkele-2.0.4.tar.gz" - mode 00644 - checksum "57a5bc753b7e80c5ece1e5968b2051b0ce7ed9ce4329d17122c61575a9ea7648" + source 'http://kin.klever.net/dist/libopkele-2.0.4.tar.gz' + mode '0644' + checksum '57a5bc753b7e80c5ece1e5968b2051b0ce7ed9ce4329d17122c61575a9ea7648' end - bash "install libopkele" do + bash 'install libopkele' do cwd Chef::Config['file_cache_path'] # Ruby 1.8.6 does not have rpartition, unfortunately - syslibdir = node['apache']['lib_dir'][0..node['apache']['lib_dir'].rindex("/")] + syslibdir = node['apache']['lib_dir'][0..node['apache']['lib_dir'].rindex('/')] code <<-EOH tar zxvf libopkele-2.0.4.tar.gz cd libopkele-2.0.4 && ./configure --prefix=/usr --libdir=#{syslibdir} @@ -72,54 +70,54 @@ configure_flags = node['apache']['mod_auth_openid']['configure_flags'] remote_file "#{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}.tar.gz" do source node['apache']['mod_auth_openid']['source_url'] - mode 00644 + mode '0644' action :create_if_missing end -file "mod_auth_openid_dblocation" do - path node['apache']['mod_auth_openid']['dblocation'] - action :nothing -end - -bash "untar mod_auth_openid" do - cwd Chef::Config['file_cache_path'] - code <<-EOH - tar zxvf mod_auth_openid-#{version}.tar.gz - EOH -end - -bash "compile mod_auth_openid" do - cwd "#{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}" - code <<-EOH - ./autogen.sh - ./configure #{configure_flags.join(' ')} - perl -pi -e "s/-i -a -n 'authopenid'/-i -n 'authopenid'/g" Makefile - #{make_cmd} && #{make_cmd} install - EOH - creates "#{node['apache']['libexecdir']}/mod_auth_openid.so" - notifies :delete, "file[mod_auth_openid_dblocation]", :immediately - notifies :restart, "service[apache2]" -end - directory node['apache']['mod_auth_openid']['cache_dir'] do owner node['apache']['user'] group node['apache']['group'] - mode 00700 + mode '0700' end -file node['apache']['mod_auth_openid']['dblocation'] do - owner node['apache']['user'] - group node['apache']['group'] - mode 00644 +bash 'untar mod_auth_openid' do + cwd Chef::Config['file_cache_path'] + code <<-EOH + tar zxvf mod_auth_openid-#{version}.tar.gz + EOH + creates "#{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}/src/types.h" +end + +bash 'compile mod_auth_openid' do + cwd "#{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}" + code <<-EOH + ./autogen.sh + ./configure #{configure_flags.join(' ')} + perl -pi -e "s/-i -a -n 'authopenid'/-i -n 'authopenid'/g" Makefile + #{make_cmd} + EOH + creates "#{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}/src/.libs/mod_auth_openid.so" + notifies :run, 'bash[install-mod_auth_openid]', :immediately + not_if "test -f #{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}/src/.libs/mod_auth_openid.so" +end + +bash 'install-mod_auth_openid' do + cwd "#{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}" + code <<-EOH + #{make_cmd} install + EOH + creates "#{node['apache']['libexecdir']}/mod_auth_openid.so" + notifies :restart, 'service[apache2]' + not_if "test -f #{node['apache']['libexecdir']}/mod_auth_openid.so" end template "#{node['apache']['dir']}/mods-available/authopenid.load" do - source "mods/authopenid.load.erb" - owner "root" - group node['apache']['root_group'] - mode 00644 + source 'mods/authopenid.load.erb' + owner 'root' + group node['apache']['root_group'] + mode '0644' end -apache_module "authopenid" do - filename "mod_auth_openid.so" +apache_module 'authopenid' do + filename 'mod_auth_openid.so' end diff --git a/chef/cookbooks/apache2/recipes/mod_authn_file.rb b/chef/cookbooks/apache2/recipes/mod_authn_file.rb index 872caa7..88dbed6 100644 --- a/chef/cookbooks/apache2/recipes/mod_authn_file.rb +++ b/chef/cookbooks/apache2/recipes/mod_authn_file.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: authn_file +# Recipe:: authn_file # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "authn_file" +apache_module 'authn_file' diff --git a/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb b/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb index 0310d24..2e93fe2 100644 --- a/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb +++ b/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: authnz_ldap +# Recipe:: authnz_ldap # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "authnz_ldap" +apache_module 'authnz_ldap' diff --git a/chef/cookbooks/apache2/recipes/mod_authz_default.rb b/chef/cookbooks/apache2/recipes/mod_authz_default.rb index 849861d..2fe45f4 100644 --- a/chef/cookbooks/apache2/recipes/mod_authz_default.rb +++ b/chef/cookbooks/apache2/recipes/mod_authz_default.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: authz_default +# Recipe:: authz_default # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,9 +17,4 @@ # limitations under the License. # -unless %w{debian}.include?(node['platform_family']) && node['platform_version'].to_f >= 14 then - print "load authz_default module" - apache_module "authz_default" -else - print "do not load authz_default module" -end +apache_module 'authz_default' diff --git a/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb b/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb index b2833b2..a2cb7bb 100644 --- a/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb +++ b/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: authz_groupfile +# Recipe:: authz_groupfile # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "authz_groupfile" +apache_module 'authz_groupfile' diff --git a/chef/cookbooks/apache2/recipes/mod_authz_host.rb b/chef/cookbooks/apache2/recipes/mod_authz_host.rb index 87c1a4b..08e0eff 100644 --- a/chef/cookbooks/apache2/recipes/mod_authz_host.rb +++ b/chef/cookbooks/apache2/recipes/mod_authz_host.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: authz_host +# Recipe:: authz_host # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "authz_host" +apache_module 'authz_host' diff --git a/chef/cookbooks/apache2/recipes/mod_authz_user.rb b/chef/cookbooks/apache2/recipes/mod_authz_user.rb index 8dd46df..a54b798 100644 --- a/chef/cookbooks/apache2/recipes/mod_authz_user.rb +++ b/chef/cookbooks/apache2/recipes/mod_authz_user.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: authz_user +# Recipe:: authz_user # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "authz_user" +apache_module 'authz_user' diff --git a/chef/cookbooks/apache2/recipes/mod_autoindex.rb b/chef/cookbooks/apache2/recipes/mod_autoindex.rb index 622a66e..1ec58a6 100644 --- a/chef/cookbooks/apache2/recipes/mod_autoindex.rb +++ b/chef/cookbooks/apache2/recipes/mod_autoindex.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: autoindex +# Recipe:: autoindex # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "autoindex" do +apache_module 'autoindex' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_cgi.rb b/chef/cookbooks/apache2/recipes/mod_cgi.rb index a151d07..c67aa74 100644 --- a/chef/cookbooks/apache2/recipes/mod_cgi.rb +++ b/chef/cookbooks/apache2/recipes/mod_cgi.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: cgi # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "cgi" +apache_module 'cgi' diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_cgi.rb b/chef/cookbooks/apache2/recipes/mod_cloudflare.rb similarity index 59% rename from chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_cgi.rb rename to chef/cookbooks/apache2/recipes/mod_cloudflare.rb index cb407e9..87a23ef 100644 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_cgi.rb +++ b/chef/cookbooks/apache2/recipes/mod_cloudflare.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: apache2_test -# Recipe:: mod_cgi +# Cookbook Name:: apache2 +# Recipe:: cloudflare # -# Copyright 2012, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,19 +17,14 @@ # limitations under the License. # -include_recipe "apache2::default" -include_recipe "apache2::mod_cgi" - -directory node['apache_test']['cgi_dir'] do - action :create +apt_repository 'cloudflare' do + uri 'http://pkg.cloudflare.com' + distribution node['lsb']['codename'] + components ['main'] + key 'http://pkg.cloudflare.com/pubkey.gpg' + action :add end -file "#{node['apache_test']['cgi_dir']}/env" do - content %q{ -#!/bin/bash -echo -e "Content-type: text/plain\n" -/usr/bin/env -}.strip - mode "0755" - action :create +package 'libapache2-mod-cloudflare' do + notifies :restart, 'service[apache2]' end diff --git a/chef/cookbooks/apache2/recipes/mod_dav.rb b/chef/cookbooks/apache2/recipes/mod_dav.rb index 94f7c1a..504c255 100644 --- a/chef/cookbooks/apache2/recipes/mod_dav.rb +++ b/chef/cookbooks/apache2/recipes/mod_dav.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: dav # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "dav" +apache_module 'dav' diff --git a/chef/cookbooks/apache2/recipes/mod_dav_fs.rb b/chef/cookbooks/apache2/recipes/mod_dav_fs.rb index bd3e74b..939594b 100644 --- a/chef/cookbooks/apache2/recipes/mod_dav_fs.rb +++ b/chef/cookbooks/apache2/recipes/mod_dav_fs.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: dav_fs # -# Copyright 2011, Atriso +# Copyright 2011-2013, Atriso # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,5 +17,5 @@ # limitations under the License. # -include_recipe "apache2::mod_dav" -apache_module "dav_fs" +include_recipe 'apache2::mod_dav' +apache_module 'dav_fs' diff --git a/chef/cookbooks/apache2/recipes/mod_dav_svn.rb b/chef/cookbooks/apache2/recipes/mod_dav_svn.rb index 3a1d0fc..bcd8b00 100644 --- a/chef/cookbooks/apache2/recipes/mod_dav_svn.rb +++ b/chef/cookbooks/apache2/recipes/mod_dav_svn.rb @@ -17,25 +17,23 @@ # limitations under the License. # -include_recipe "apache2::mod_dav" +include_recipe 'apache2::mod_dav' -package "libapache2-svn" do +package 'libapache2-svn' do case node['platform_family'] - when "rhel", "fedora", "suse" - package_name "mod_dav_svn" + when 'rhel', 'fedora', 'suse' + package_name 'mod_dav_svn' else - package_name "libapache2-svn" + package_name 'libapache2-svn' end end case node['platform_family'] -when "rhel", "fedora", "suse" - - file "#{node['apache']['conf']}/conf.d/subversion.conf" do +when 'rhel', 'fedora', 'suse' + file "#{node['apache']['dir']}/conf.d/subversion.conf" do action :delete backup false end - end -apache_module "dav_svn" +apache_module 'dav_svn' diff --git a/chef/cookbooks/apache2/recipes/mod_deflate.rb b/chef/cookbooks/apache2/recipes/mod_deflate.rb index b568f30..c876086 100644 --- a/chef/cookbooks/apache2/recipes/mod_deflate.rb +++ b/chef/cookbooks/apache2/recipes/mod_deflate.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: deflate +# Recipe:: deflate # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "deflate" do +apache_module 'deflate' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_dir.rb b/chef/cookbooks/apache2/recipes/mod_dir.rb index e59b36b..f2a33c8 100644 --- a/chef/cookbooks/apache2/recipes/mod_dir.rb +++ b/chef/cookbooks/apache2/recipes/mod_dir.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: dir +# Recipe:: dir # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "dir" do +apache_module 'dir' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_env.rb b/chef/cookbooks/apache2/recipes/mod_env.rb index d345503..10a8029 100644 --- a/chef/cookbooks/apache2/recipes/mod_env.rb +++ b/chef/cookbooks/apache2/recipes/mod_env.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: env +# Recipe:: env # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "env" +apache_module 'env' diff --git a/chef/cookbooks/apache2/recipes/mod_expires.rb b/chef/cookbooks/apache2/recipes/mod_expires.rb index 9e5042e..52a637e 100644 --- a/chef/cookbooks/apache2/recipes/mod_expires.rb +++ b/chef/cookbooks/apache2/recipes/mod_expires.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: expires +# Recipe:: expires # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "expires" +apache_module 'expires' diff --git a/chef/cookbooks/apache2/recipes/mod_fastcgi.rb b/chef/cookbooks/apache2/recipes/mod_fastcgi.rb index a0a4d95..d53cbf0 100644 --- a/chef/cookbooks/apache2/recipes/mod_fastcgi.rb +++ b/chef/cookbooks/apache2/recipes/mod_fastcgi.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: fastcgi # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,10 +17,36 @@ # limitations under the License. # -if platform_family?("debian") - package "libapache2-mod-fastcgi" +if platform_family?('debian') + package 'libapache2-mod-fastcgi' +elsif platform_family?('rhel') + %w[gcc make libtool httpd-devel apr-devel apr].each do |package| + yum_package package do + action :upgrade + end + end - apache_module "fastcgi" do - conf true + src_filepath = "#{Chef::Config['file_cache_path']}/fastcgi.tar.gz" + remote_file 'download fastcgi source' do + source node['apache']['mod_fastcgi']['download_url'] + path src_filepath + backup false + end + + top_dir = node['apache']['lib_dir'] + bash 'compile fastcgi source' do + notifies :run, 'execute[generate-module-list]', :immediately + not_if "test -f #{node['apache']['dir']}/mods-available/fastcgi.conf" + cwd ::File.dirname(src_filepath) + code <<-EOH + tar zxf #{::File.basename(src_filepath)} && + cd mod_fastcgi-* && + cp Makefile.AP2 Makefile && + make top_dir=#{top_dir} && make install top_dir=#{top_dir} + EOH end end + +apache_module 'fastcgi' do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_fcgid.rb b/chef/cookbooks/apache2/recipes/mod_fcgid.rb index b82dc95..dcb81a4 100644 --- a/chef/cookbooks/apache2/recipes/mod_fcgid.rb +++ b/chef/cookbooks/apache2/recipes/mod_fcgid.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: fcgid # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,12 +17,11 @@ # limitations under the License. # -if platform_family?("debian") - package "libapache2-mod-fcgid" -elsif platform_family?("rhel", "fedora") - - package "mod_fcgid" do - notifies :run, resources(:execute => "generate-module-list"), :immediately +if platform_family?('debian') + package 'libapache2-mod-fcgid' +elsif platform_family?('rhel', 'fedora') + package 'mod_fcgid' do + notifies :run, 'execute[generate-module-list]', :immediately end file "#{node['apache']['dir']}/conf.d/fcgid.conf" do @@ -30,17 +29,16 @@ elsif platform_family?("rhel", "fedora") backup false end - directory "/var/run/httpd/mod_fcgid" do + directory '/var/run/httpd/mod_fcgid' do recursive true only_if { node['platform_version'].to_i >= 6 } end - -elsif platform_family?("suse") +elsif platform_family?('suse') apache_lib_path = node['apache']['lib_dir'] - package "httpd-devel" + package 'httpd-devel' - bash "install-fcgid" do + bash 'install-fcgid' do code <<-EOH (cd #{Chef::Config['file_cache_path']}; wget http://superb-east.dl.sourceforge.net/sourceforge/mod-fcgid/mod_fcgid.2.2.tgz) (cd #{Chef::Config['file_cache_path']}; tar zxvf mod_fcgid.2.2.tgz) @@ -50,6 +48,6 @@ EOH end end -apache_module "fcgid" do +apache_module 'fcgid' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_filter.rb b/chef/cookbooks/apache2/recipes/mod_filter.rb index 148ef7d..cf1f00e 100644 --- a/chef/cookbooks/apache2/recipes/mod_filter.rb +++ b/chef/cookbooks/apache2/recipes/mod_filter.rb @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "filter" +apache_module 'filter' diff --git a/chef/cookbooks/apache2/recipes/mod_headers.rb b/chef/cookbooks/apache2/recipes/mod_headers.rb index 5e6b94d..8aa9850 100644 --- a/chef/cookbooks/apache2/recipes/mod_headers.rb +++ b/chef/cookbooks/apache2/recipes/mod_headers.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: headers +# Recipe:: headers # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "headers" +apache_module 'headers' diff --git a/chef/cookbooks/apache2/recipes/mod_include.rb b/chef/cookbooks/apache2/recipes/mod_include.rb index e46d81f..237270c 100644 --- a/chef/cookbooks/apache2/recipes/mod_include.rb +++ b/chef/cookbooks/apache2/recipes/mod_include.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: include # -# Copyright 2012, Opscode, Inc. +# Copyright 2012-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "include" do +apache_module 'include' do conf true end diff --git a/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/default.rb b/chef/cookbooks/apache2/recipes/mod_info.rb similarity index 86% rename from chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/default.rb rename to chef/cookbooks/apache2/recipes/mod_info.rb index 22024f7..093e59f 100644 --- a/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/default.rb +++ b/chef/cookbooks/apache2/recipes/mod_info.rb @@ -1,6 +1,6 @@ # -# Cookbook Name:: memcached_test -# Recipe:: default +# Cookbook Name:: apache2 +# Recipe:: info # # Copyright 2013, Opscode, Inc. # @@ -17,4 +17,6 @@ # limitations under the License. # -include_recipe "memcached::default" +apache_module 'info' do + conf true +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-client_test.rb b/chef/cookbooks/apache2/recipes/mod_jk.rb similarity index 67% rename from chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-client_test.rb rename to chef/cookbooks/apache2/recipes/mod_jk.rb index db617a2..39f383b 100644 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-client_test.rb +++ b/chef/cookbooks/apache2/recipes/mod_jk.rb @@ -1,7 +1,8 @@ # -# Cookbook Name:: apt_test -# Recipe:: cacher-client_test +# Cookbook Name:: apache2 +# Recipe:: jk # +# Copyright 2013, Mike Babineau # Copyright 2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,13 +18,13 @@ # limitations under the License. # -require File.expand_path('../support/helpers', __FILE__) - -describe "apt_test::cacher-client" do - include Helpers::AptTest - - it 'does not create 01proxy' do - file('/etc/apt/apt.conf.d/01proxy').wont_exist +package 'libapache2-mod-jk' do + case node['platform_family'] + when 'rhel', 'fedora', 'suse' + package_name 'mod_jk' + else + package_name 'libapache2-mod-jk' end - end + +apache_module 'jk' diff --git a/chef/cookbooks/apache2/recipes/mod_ldap.rb b/chef/cookbooks/apache2/recipes/mod_ldap.rb index 0877694..ec62466 100644 --- a/chef/cookbooks/apache2/recipes/mod_ldap.rb +++ b/chef/cookbooks/apache2/recipes/mod_ldap.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: ldap +# Recipe:: ldap # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "ldap" +apache_module 'ldap' diff --git a/chef/cookbooks/apache2/recipes/mod_log_config.rb b/chef/cookbooks/apache2/recipes/mod_log_config.rb index 4ab653f..529c5f5 100644 --- a/chef/cookbooks/apache2/recipes/mod_log_config.rb +++ b/chef/cookbooks/apache2/recipes/mod_log_config.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: log_config # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ # limitations under the License. # -if platform_family?("rhel", "fedora", "suse", "arch", "freebsd") - apache_module "log_config" +if platform_family?('rhel', 'fedora', 'suse', 'arch', 'freebsd') + apache_module 'log_config' else - include_recipe "apache2" + include_recipe 'apache2::default' end diff --git a/chef/cookbooks/apache2/recipes/mod_logio.rb b/chef/cookbooks/apache2/recipes/mod_logio.rb index 74f1350..efdf512 100644 --- a/chef/cookbooks/apache2/recipes/mod_logio.rb +++ b/chef/cookbooks/apache2/recipes/mod_logio.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: logio # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ # limitations under the License. # -if platform_family?("rhel", "fedora", "suse", "arch", "freebsd") - apache_module "logio" +if platform_family?('rhel', 'fedora', 'suse', 'arch', 'freebsd') + apache_module 'logio' else - include_recipe "apache2" + include_recipe 'apache2::default' end diff --git a/chef/cookbooks/apache2/recipes/mod_mime.rb b/chef/cookbooks/apache2/recipes/mod_mime.rb index 16aee1a..1727277 100644 --- a/chef/cookbooks/apache2/recipes/mod_mime.rb +++ b/chef/cookbooks/apache2/recipes/mod_mime.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: mime +# Recipe:: mime # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "mime" do +apache_module 'mime' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_negotiation.rb b/chef/cookbooks/apache2/recipes/mod_negotiation.rb index 348e11f..68a856c 100644 --- a/chef/cookbooks/apache2/recipes/mod_negotiation.rb +++ b/chef/cookbooks/apache2/recipes/mod_negotiation.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: negotiation +# Recipe:: negotiation # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "negotiation" do +apache_module 'negotiation' do conf true end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_proxy_ajp.rb b/chef/cookbooks/apache2/recipes/mod_pagespeed.rb similarity index 52% rename from chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_proxy_ajp.rb rename to chef/cookbooks/apache2/recipes/mod_pagespeed.rb index c006af2..23b05bd 100644 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_proxy_ajp.rb +++ b/chef/cookbooks/apache2/recipes/mod_pagespeed.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: apache2_test -# Recipe:: mod_ajp +# Cookbook Name:: apache2 +# Recipe:: default # -# Copyright 2012, Opscode, Inc. +# Copyright 2013, ZOZI # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,28 +17,21 @@ # limitations under the License. # -include_recipe "apache2::default" -include_recipe "apache2::mod_proxy" -include_recipe "apache2::mod_proxy_ajp" +if platform_family?('debian') + remote_file "#{Chef::Config[:file_cache_path]}/mod-pagespeed.deb" do + source node['apache2']['mod_pagespeed']['package_link'] + mode '0644' + action :create_if_missing + end -if platform_family?("rhel") && node['platform_version'].to_f < 6.0 - include_recipe "jpackage::default" -end - -include_recipe "tomcat::default" - -if platform?("debian","ubuntu") - package "tomcat6-examples" do + package 'mod_pagespeed' do + source "#{Chef::Config[:file_cache_path]}/mod-pagespeed.deb" action :install end + + apache_module 'pagespeed' do + conf true + end else - package "tomcat6-webapps" do - action :install - end -end - -web_app "java_env" do - template "java_env.conf.erb" - ajp_host 'localhost' - ajp_port 8009 + Chef::Log.warm "apache::mod_pagespeed does not support #{node["platform_family"]} yet, and is not being installed" end diff --git a/chef/cookbooks/apache2/recipes/mod_perl.rb b/chef/cookbooks/apache2/recipes/mod_perl.rb index b4d2a74..23bad4a 100644 --- a/chef/cookbooks/apache2/recipes/mod_perl.rb +++ b/chef/cookbooks/apache2/recipes/mod_perl.rb @@ -4,7 +4,7 @@ # # adapted from the mod_python recipe by Jeremy Bingham # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,20 +20,16 @@ # case node['platform_family'] -when "debian" - %w{libapache2-mod-perl2 libapache2-request-perl apache2-mpm-prefork}.each do |pkg| - +when 'debian' + %w[libapache2-mod-perl2 libapache2-request-perl apache2-mpm-prefork].each do |pkg| package pkg - end -when "rhel", "fedora" - - package "mod_perl" do - notifies :run, "execute[generate-module-list]", :immediately +when 'rhel', 'fedora' + package 'mod_perl' do + notifies :run, 'execute[generate-module-list]', :immediately end - package "perl-libapreq2" - + package 'perl-libapreq2' end file "#{node['apache']['dir']}/conf.d/perl.conf" do @@ -41,4 +37,4 @@ file "#{node['apache']['dir']}/conf.d/perl.conf" do backup false end -apache_module "perl" +apache_module 'perl' diff --git a/chef/cookbooks/apache2/recipes/mod_php5.rb b/chef/cookbooks/apache2/recipes/mod_php5.rb index 63cc0ce..d5dee6d 100644 --- a/chef/cookbooks/apache2/recipes/mod_php5.rb +++ b/chef/cookbooks/apache2/recipes/mod_php5.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: php5 # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,50 +18,41 @@ # case node['platform_family'] -when "debian" - - package "libapache2-mod-php5" - -when "arch" - - package "php-apache" do - notifies :run, "execute[generate-module-list]", :immediately +when 'debian' + package 'libapache2-mod-php5' +when 'arch' + package 'php-apache' do + notifies :run, 'execute[generate-module-list]', :immediately end +when 'rhel' + package 'which' -when "rhel" - - package "which" - package "php package" do + package 'php package' do if node['platform_version'].to_f < 6.0 - package_name "php53" + package_name 'php53' else - package_name "php" + package_name 'php' end - notifies :run, "execute[generate-module-list]", :immediately - not_if "which php" + notifies :run, 'execute[generate-module-list]', :immediately + not_if 'which php' end - -when "fedora" - - package "php package" do - package_name "php" - notifies :run, "execute[generate-module-list]", :immediately - not_if "which php" +when 'fedora' + package 'php package' do + package_name 'php' + notifies :run, 'execute[generate-module-list]', :immediately + not_if 'which php' end - -when "freebsd" - - freebsd_port_options "php5" do - options "APACHE" => true +when 'freebsd' + freebsd_port_options 'php5' do + options 'APACHE' => true action :create end - package "php package" do - package_name "php5" - source "ports" - notifies :run, "execute[generate-module-list]", :immediately + package 'php package' do + package_name 'php5' + source 'ports' + notifies :run, 'execute[generate-module-list]', :immediately end - end file "#{node['apache']['dir']}/conf.d/php.conf" do @@ -69,10 +60,10 @@ file "#{node['apache']['dir']}/conf.d/php.conf" do backup false end -apache_module "php5" do +apache_module 'php5' do case node['platform_family'] - when "rhel", "fedora", "freebsd" + when 'rhel', 'fedora', 'freebsd' conf true - filename "libphp5.so" + filename 'libphp5.so' end end diff --git a/chef/cookbooks/apache2/recipes/mod_proxy.rb b/chef/cookbooks/apache2/recipes/mod_proxy.rb index fff7627..8cb5554 100644 --- a/chef/cookbooks/apache2/recipes/mod_proxy.rb +++ b/chef/cookbooks/apache2/recipes/mod_proxy.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: proxy +# Recipe:: proxy # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "proxy" do +apache_module 'proxy' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb b/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb index 61bc078..0d80bbe 100644 --- a/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb +++ b/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: proxy +# Recipe:: proxy # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,5 +17,5 @@ # limitations under the License. # -include_recipe "apache2::mod_proxy" -apache_module "proxy_ajp" +include_recipe 'apache2::mod_proxy' +apache_module 'proxy_ajp' diff --git a/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb b/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb index dc62a71..85646e7 100644 --- a/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb +++ b/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: proxy +# Recipe:: proxy # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "proxy_balancer" +apache_module 'proxy_balancer' diff --git a/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb b/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb index f41954f..bece099 100644 --- a/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb +++ b/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: proxy +# Recipe:: proxy # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "proxy_connect" +apache_module 'proxy_connect' diff --git a/chef/cookbooks/apache2/recipes/mod_proxy_http.rb b/chef/cookbooks/apache2/recipes/mod_proxy_http.rb index ddff3ea..2929e21 100644 --- a/chef/cookbooks/apache2/recipes/mod_proxy_http.rb +++ b/chef/cookbooks/apache2/recipes/mod_proxy_http.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: proxy_http +# Recipe:: proxy_http # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "proxy_http" +apache_module 'proxy_http' diff --git a/chef/cookbooks/apache2/recipes/mod_python.rb b/chef/cookbooks/apache2/recipes/mod_python.rb index bfa684a..5f50485 100644 --- a/chef/cookbooks/apache2/recipes/mod_python.rb +++ b/chef/cookbooks/apache2/recipes/mod_python.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: python # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,14 +18,11 @@ # case node['platform_family'] -when "debian" - - package "libapache2-mod-python" - -when "rhel", "fedora" - - package "mod_python" do - notifies :run, "execute[generate-module-list]", :immediately +when 'debian' + package 'libapache2-mod-python' +when 'rhel', 'fedora' + package 'mod_python' do + notifies :run, 'execute[generate-module-list]', :immediately end end @@ -34,4 +31,4 @@ file "#{node['apache']['dir']}/conf.d/python.conf" do backup false end -apache_module "python" +apache_module 'python' diff --git a/chef/cookbooks/apache2/recipes/mod_rewrite.rb b/chef/cookbooks/apache2/recipes/mod_rewrite.rb index df388a6..651fadf 100644 --- a/chef/cookbooks/apache2/recipes/mod_rewrite.rb +++ b/chef/cookbooks/apache2/recipes/mod_rewrite.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: rewrite +# Recipe:: rewrite # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ # limitations under the License. # -apache_module "rewrite" +apache_module 'rewrite' diff --git a/chef/cookbooks/apache2/recipes/mod_setenvif.rb b/chef/cookbooks/apache2/recipes/mod_setenvif.rb index 4048a5f..fab9819 100644 --- a/chef/cookbooks/apache2/recipes/mod_setenvif.rb +++ b/chef/cookbooks/apache2/recipes/mod_setenvif.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: setenvif +# Recipe:: setenvif # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "setenvif" do +apache_module 'setenvif' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_ssl.rb b/chef/cookbooks/apache2/recipes/mod_ssl.rb index d5095ec..e21cb6d 100644 --- a/chef/cookbooks/apache2/recipes/mod_ssl.rb +++ b/chef/cookbooks/apache2/recipes/mod_ssl.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: ssl # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,16 +16,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # -unless node['apache']['listen_ports'].include?("443") - node.set['apache']['listen_ports'] = node['apache']['listen_ports'] + ["443"] +unless node['apache']['listen_ports'].include?('443') + node.set['apache']['listen_ports'] = node['apache']['listen_ports'] + ['443'] end -ports = node['apache']['listen_ports'] - -if platform_family?("rhel", "fedora", "suse") - - package "mod_ssl" do - notifies :run, "execute[generate-module-list]", :immediately +if platform_family?('rhel', 'fedora', 'suse') + package 'mod_ssl' do + notifies :run, 'execute[generate-module-list]', :immediately end file "#{node['apache']['dir']}/conf.d/ssl.conf" do @@ -35,12 +32,11 @@ if platform_family?("rhel", "fedora", "suse") end template "#{node['apache']['dir']}/ports.conf" do - source "ports.conf.erb" - variables :apache_listen_ports => ports.map { |p| p.to_i }.uniq - notifies :restart, "service[apache2]" - mode 00644 + source 'ports.conf.erb' + mode '0644' + notifies :restart, 'service[apache2]' end -apache_module "ssl" do +apache_module 'ssl' do conf true end diff --git a/chef/cookbooks/apache2/recipes/mod_status.rb b/chef/cookbooks/apache2/recipes/mod_status.rb index 3e71727..2f1cc2a 100644 --- a/chef/cookbooks/apache2/recipes/mod_status.rb +++ b/chef/cookbooks/apache2/recipes/mod_status.rb @@ -1,8 +1,8 @@ # # Cookbook Name:: apache2 -# Recipe:: status +# Recipe:: status # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2012, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ # limitations under the License. # -apache_module "status" do +apache_module 'status' do conf true end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-client.rb b/chef/cookbooks/apache2/recipes/mod_userdir.rb similarity index 86% rename from chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-client.rb rename to chef/cookbooks/apache2/recipes/mod_userdir.rb index 432db27..8ad4f82 100644 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-client.rb +++ b/chef/cookbooks/apache2/recipes/mod_userdir.rb @@ -1,6 +1,6 @@ # -# Cookbook Name:: apt_test -# Recipe:: cacher-client +# Cookbook Name:: apache2 +# Recipe:: mod_userdir # # Copyright 2013, Opscode, Inc. # @@ -17,4 +17,4 @@ # limitations under the License. # -include_recipe "apt::cacher-client" +apache_module 'userdir' diff --git a/chef/cookbooks/apache2/recipes/mod_wsgi.rb b/chef/cookbooks/apache2/recipes/mod_wsgi.rb index fef91bb..6f9fc46 100644 --- a/chef/cookbooks/apache2/recipes/mod_wsgi.rb +++ b/chef/cookbooks/apache2/recipes/mod_wsgi.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: mod_wsgi # -# Copyright 2008-2012, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,16 +18,12 @@ # case node['platform_family'] -when "debian" - - package "libapache2-mod-wsgi" - -when "rhel", "fedora", "arch" - - package "mod_wsgi" do - notifies :run, "execute[generate-module-list]", :immediately +when 'debian' + package 'libapache2-mod-wsgi' +when 'rhel', 'fedora', 'arch' + package 'mod_wsgi' do + notifies :run, 'execute[generate-module-list]', :immediately end - end file "#{node['apache']['dir']}/conf.d/wsgi.conf" do @@ -35,4 +31,4 @@ file "#{node['apache']['dir']}/conf.d/wsgi.conf" do backup false end -apache_module "wsgi" +apache_module 'wsgi' diff --git a/chef/cookbooks/apache2/recipes/mod_xsendfile.rb b/chef/cookbooks/apache2/recipes/mod_xsendfile.rb index 9d4c60f..1ed99e8 100644 --- a/chef/cookbooks/apache2/recipes/mod_xsendfile.rb +++ b/chef/cookbooks/apache2/recipes/mod_xsendfile.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apache2 # Recipe:: mod_xsendfile # -# Copyright 2011, CustomInk, LLC. +# Copyright 2011-2013, CustomInk, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,16 +18,12 @@ # case node['platform_family'] -when "debian" - - package "libapache2-mod-xsendfile" - -when "rhel", "fedora" - - package "mod_xsendfile" do - notifies :run, "execute[generate-module-list]", :immediately +when 'debian' + package 'libapache2-mod-xsendfile' +when 'rhel', 'fedora' + package 'mod_xsendfile' do + notifies :run, 'execute[generate-module-list]', :immediately end - end file "#{node['apache']['dir']}/conf.d/xsendfile.conf" do @@ -35,4 +31,4 @@ file "#{node['apache']['dir']}/conf.d/xsendfile.conf" do backup false end -apache_module "xsendfile" +apache_module 'xsendfile' diff --git a/chef/cookbooks/apache2/templates/default/a2enmod.erb b/chef/cookbooks/apache2/templates/default/a2enmod.erb index fe641df..9766e80 100644 --- a/chef/cookbooks/apache2/templates/default/a2enmod.erb +++ b/chef/cookbooks/apache2/templates/default/a2enmod.erb @@ -14,8 +14,8 @@ else fi #figure out if we're on a prefork or threaded mpm -if [ -x /usr/sbin/apache2 ]; then - PREFORK=`/usr/sbin/apache2 -l | grep prefork || true` +if [ -x <%= node['apache']['binary'] %> ]; then + PREFORK=`<%= node['apache']['binary'] %> -l | grep prefork || true` fi if [ -e $SYSCONFDIR/mods-enabled/$MODNAME.load -a -e $SYSCONFDIR/mods-enabled/$MODNAME.conf ]; then diff --git a/chef/cookbooks/apache2/templates/default/apache2.conf.erb b/chef/cookbooks/apache2/templates/default/apache2.conf.erb index 668ee02..5bc6d3c 100644 --- a/chef/cookbooks/apache2/templates/default/apache2.conf.erb +++ b/chef/cookbooks/apache2/templates/default/apache2.conf.erb @@ -8,13 +8,9 @@ ServerRoot "<%= node['apache']['dir'] %>" # # The accept serialization lock file MUST BE STORED ON A LOCAL DISK. # -<% if %w{debian}.include?(node['platform_family']) -%> - <% if node['platform_version'].to_f >= 14 -%> -Mutex file:/var/lock/apache2 default - <% else %> +<% if %w[debian].include?(node['platform_family']) -%> LockFile /var/lock/apache2/accept.lock - <% end -%> -<% elsif %w{freebsd}.include?(node['platform_family']) -%> +<% elsif %w[freebsd].include?(node['platform_family']) -%> LockFile /var/log/accept.lock <% else %> LockFile logs/accept.lock @@ -24,7 +20,6 @@ LockFile logs/accept.lock # PidFile: The file in which the server should record its process # identification number when it starts. # - PidFile <%= node['apache']['pid_file'] %> # @@ -53,7 +48,7 @@ KeepAliveTimeout <%= node['apache']['keepalivetimeout'] %> ## ## Server-Pool Size Regulation (MPM specific) -## +## # prefork MPM # StartServers: number of server processes to start @@ -99,8 +94,8 @@ Group <%= node['apache']['group'] %> AccessFileName .htaccess # -# The following lines prevent .htaccess and .htpasswd files from being -# viewed by Web clients. +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. # Order allow,deny @@ -151,7 +146,7 @@ LogLevel warn Include <%= node['apache']['dir'] %>/mods-enabled/*.load Include <%= node['apache']['dir'] %>/mods-enabled/*.conf -<% if %w{freebsd}.include?(node['platform_family']) -%> +<% if %w[freebsd].include?(node['platform_family']) -%> AcceptFilter http none AcceptFilter https none @@ -186,7 +181,7 @@ LogFormat "%{User-agent}i" agent # Putting this all together, we can internationalize error responses. # # We use Alias to redirect any /error/HTTP_.html.var response to -# our collection of by-error message multi-language collections. We use +# our collection of by-error message multi-language collections. We use # includes to substitute the appropriate text. # # You can modify the messages' appearance without changing any of the @@ -195,7 +190,7 @@ LogFormat "%{User-agent}i" agent # Alias /error/include/ "/your/include/path/" # # which allows you to create your own set of files by starting with the -# /usr/share/apache2/error/include/ files and copying them to /your/include/path/, +# /usr/share/apache2/error/include/ files and copying them to /your/include/path/, # even on a per-VirtualHost basis. The default include files will display # your Apache version number and your ServerAdmin email address regardless # of the setting of ServerSignature. @@ -237,7 +232,7 @@ LogFormat "%{User-agent}i" agent # Include generic snippets of statements -Include <%= node['apache']['dir'] %>/conf.d/ +Include <%= node['apache']['dir'] %>/conf.d/*.conf # Include the virtual host configurations: Include <%= node['apache']['dir'] %>/sites-enabled/ diff --git a/chef/cookbooks/apache2/templates/default/default-site.erb b/chef/cookbooks/apache2/templates/default/default-site.erb index a65ab53..b134437 100644 --- a/chef/cookbooks/apache2/templates/default/default-site.erb +++ b/chef/cookbooks/apache2/templates/default/default-site.erb @@ -1,57 +1,55 @@ - ServerAdmin <%= node['apache']['contact'] %> + ServerAdmin <%= node['apache']['contact'] %> - DocumentRoot <%= node['apache']['docroot_dir'] %>/ - - Options FollowSymLinks - AllowOverride None - - /> - Options Indexes FollowSymLinks MultiViews - AllowOverride None - Order allow,deny - allow from all - # This directive allows us to have apache2's default start page - # in /apache2-default/, but still have / go to the right place - #RedirectMatch ^/$ /apache2-default/ - + DocumentRoot <%= node['apache']['docroot_dir'] %>/ + + Options FollowSymLinks + AllowOverride None + - ScriptAlias /cgi-bin/ <%= node['apache']['cgibin_dir'] %>/ - "> - AllowOverride None - Options ExecCGI -MultiViews +SymLinksIfOwnerMatch - Order allow,deny - Allow from all - + /> + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + Allow from all + - ErrorLog <%= node['apache']['log_dir'] %>/<%= node['apache']['error_log'] %> + ScriptAlias /cgi-bin/ <%= node['apache']['cgibin_dir'] %>/ + "> + AllowOverride None + Options ExecCGI -MultiViews +SymLinksIfOwnerMatch + Order allow,deny + Allow from all + - # Possible values include: debug, info, notice, warn, error, crit, - # alert, emerg. - LogLevel warn + ErrorLog <%= node['apache']['log_dir'] %>/<%= node['apache']['error_log'] %> - CustomLog <%= node['apache']['log_dir'] %>/<%= node['apache']['access_log'] %> combined - ServerSignature On + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn - Alias /doc/ "/usr/share/doc/" - - Options Indexes MultiViews FollowSymLinks - AllowOverride None - Order deny,allow - Deny from all - Allow from 127.0.0.0/255.0.0.0 ::1/128 - + CustomLog <%= node['apache']['log_dir'] %>/<%= node['apache']['access_log'] %> combined + ServerSignature On - <% if %w{ rhel fedora }.include?(node['platform_family']) -%> - # - # This configuration file enables the default "Welcome" - # page if there is no default index page present for - # the root URL. To disable the Welcome page, comment - # out all the lines below. - # - - Options -Indexes - ErrorDocument 403 /error/noindex.html - - <% end -%> + Alias /doc/ "/usr/share/doc/" + + Options Indexes MultiViews FollowSymLinks + AllowOverride None + Order deny,allow + Deny from all + Allow from 127.0.0.0/255.0.0.0 ::1/128 + + + <% if %w[rhel fedora].include?(node['platform_family']) -%> + # + # This configuration file enables the default "Welcome" + # page if there is no default index page present for + # the root URL. To disable the Welcome page, comment + # out all the lines below. + # + + Options -Indexes + ErrorDocument 403 /error/noindex.html + + <% end -%> diff --git a/chef/cookbooks/apache2/templates/default/etc-sysconfig-httpd.erb b/chef/cookbooks/apache2/templates/default/etc-sysconfig-httpd.erb index dd1c2a7..faa27a8 100644 --- a/chef/cookbooks/apache2/templates/default/etc-sysconfig-httpd.erb +++ b/chef/cookbooks/apache2/templates/default/etc-sysconfig-httpd.erb @@ -28,4 +28,8 @@ HTTPD=<%= node['apache']['binary'] %> # specified in httpd.conf (via the PidFile directive), the new # location needs to be reported in the PIDFILE. # -#PIDFILE=<%= node['apache']['pid_file'] %> +PIDFILE=<%= node['apache']['pid_file'] %> + +<% node['apache']['sysconfig_additional_params'].each do |k,v| %> +<%= "#{k}=#{v}" %> +<% end %> diff --git a/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb b/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb index 5ab139e..8d8c2ec 100644 --- a/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb @@ -1,24 +1,23 @@ -# -# Aliases: Add here as many aliases as you need (with no limit). The format is -# Alias fakename realname -# -# Note that if you include a trailing / on fakename then the server will -# require it to be present in the URL. So "/icons" isn't aliased in this -# example, only "/icons/". If the fakename is slash-terminated, then the -# realname must also be slash terminated, and if the fakename omits the -# trailing slash, the realname must also omit it. -# -# We include the /icons/ alias for FancyIndexed directory listings. If -# you do not use FancyIndexing, you may comment this out. -# -Alias /icons/ "<%= node['apache']['icondir'] %>/" + # + # Aliases: Add here as many aliases as you need (with no limit). The format is + # Alias fakename realname + # + # Note that if you include a trailing / on fakename then the server will + # require it to be present in the URL. So "/icons" isn't aliased in this + # example, only "/icons/". If the fakename is slash-terminated, then the + # realname must also be slash terminated, and if the fakename omits the + # trailing slash, the realname must also omit it. + # + # We include the /icons/ alias for FancyIndexed directory listings. If + # you do not use FancyIndexing, you may comment this out. + # + Alias /icons/ "<%= node['apache']['icondir'] %>/" -"> + "> Options Indexes MultiViews AllowOverride None Order allow,deny Allow from all - - + diff --git a/chef/cookbooks/apache2/templates/default/mods/auth_cas.conf.erb b/chef/cookbooks/apache2/templates/default/mods/auth_cas.conf.erb index 80efafe..1f23f8e 100644 --- a/chef/cookbooks/apache2/templates/default/mods/auth_cas.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/auth_cas.conf.erb @@ -1 +1 @@ -CASCookiePath <%= node['apache']['cache_dir'] %>/mod_auth_cas/ \ No newline at end of file +CASCookiePath <%= node['apache']['cache_dir'] %>/mod_auth_cas/ diff --git a/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb b/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb index 3839093..5f86614 100644 --- a/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb @@ -1,101 +1,100 @@ -# -# Directives controlling the display of server-generated directory listings. -# + # + # Directives controlling the display of server-generated directory listings. + # -# -# IndexOptions: Controls the appearance of server-generated directory -# listings. -# Remove/replace the "Charset=UTF-8" if you don't use UTF-8 for your filenames. -# -IndexOptions FancyIndexing VersionSort HTMLTable NameWidth=* DescriptionWidth=* Charset=UTF-8 + # + # IndexOptions: Controls the appearance of server-generated directory + # listings. + # Remove/replace the "Charset=UTF-8" if you don't use UTF-8 for your filenames. + # + IndexOptions FancyIndexing VersionSort HTMLTable NameWidth=* DescriptionWidth=* Charset=UTF-8 -# -# AddIcon* directives tell the server which icon to show for different -# files or filename extensions. These are only displayed for -# FancyIndexed directories. -# -AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip x-bzip2 + # + # AddIcon* directives tell the server which icon to show for different + # files or filename extensions. These are only displayed for + # FancyIndexed directories. + # + AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip x-bzip2 -AddIconByType (TXT,/icons/text.gif) text/* -AddIconByType (IMG,/icons/image2.gif) image/* -AddIconByType (SND,/icons/sound2.gif) audio/* -AddIconByType (VID,/icons/movie.gif) video/* + AddIconByType (TXT,/icons/text.gif) text/* + AddIconByType (IMG,/icons/image2.gif) image/* + AddIconByType (SND,/icons/sound2.gif) audio/* + AddIconByType (VID,/icons/movie.gif) video/* -AddIcon /icons/binary.gif .bin .exe -AddIcon /icons/binhex.gif .hqx -AddIcon /icons/tar.gif .tar -AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv -AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip -AddIcon /icons/a.gif .ps .ai .eps -AddIcon /icons/layout.gif .html .shtml .htm .pdf -AddIcon /icons/text.gif .txt -AddIcon /icons/c.gif .c -AddIcon /icons/p.gif .pl .py -AddIcon /icons/f.gif .for -AddIcon /icons/dvi.gif .dvi -AddIcon /icons/uuencoded.gif .uu -AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl -AddIcon /icons/tex.gif .tex -# It's a suffix rule, so simply matching "core" matches "score" as well ! -AddIcon /icons/bomb.gif /core -AddIcon (SND,/icons/sound2.gif) .ogg -AddIcon (VID,/icons/movie.gif) .ogm + AddIcon /icons/binary.gif .bin .exe + AddIcon /icons/binhex.gif .hqx + AddIcon /icons/tar.gif .tar + AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv + AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip + AddIcon /icons/a.gif .ps .ai .eps + AddIcon /icons/layout.gif .html .shtml .htm .pdf + AddIcon /icons/text.gif .txt + AddIcon /icons/c.gif .c + AddIcon /icons/p.gif .pl .py + AddIcon /icons/f.gif .for + AddIcon /icons/dvi.gif .dvi + AddIcon /icons/uuencoded.gif .uu + AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl + AddIcon /icons/tex.gif .tex + # It's a suffix rule, so simply matching "core" matches "score" as well ! + AddIcon /icons/bomb.gif /core + AddIcon (SND,/icons/sound2.gif) .ogg + AddIcon (VID,/icons/movie.gif) .ogm -AddIcon /icons/back.gif .. -AddIcon /icons/hand.right.gif README -AddIcon /icons/folder.gif ^^DIRECTORY^^ -AddIcon /icons/blank.gif ^^BLANKICON^^ + AddIcon /icons/back.gif .. + AddIcon /icons/hand.right.gif README + AddIcon /icons/folder.gif ^^DIRECTORY^^ + AddIcon /icons/blank.gif ^^BLANKICON^^ -# Default icons for OpenDocument format -AddIcon /icons/odf6odt-20x22.png .odt -AddIcon /icons/odf6ods-20x22.png .ods -AddIcon /icons/odf6odp-20x22.png .odp -AddIcon /icons/odf6odg-20x22.png .odg -AddIcon /icons/odf6odc-20x22.png .odc -AddIcon /icons/odf6odf-20x22.png .odf -AddIcon /icons/odf6odb-20x22.png .odb -AddIcon /icons/odf6odi-20x22.png .odi -AddIcon /icons/odf6odm-20x22.png .odm + # Default icons for OpenDocument format + AddIcon /icons/odf6odt-20x22.png .odt + AddIcon /icons/odf6ods-20x22.png .ods + AddIcon /icons/odf6odp-20x22.png .odp + AddIcon /icons/odf6odg-20x22.png .odg + AddIcon /icons/odf6odc-20x22.png .odc + AddIcon /icons/odf6odf-20x22.png .odf + AddIcon /icons/odf6odb-20x22.png .odb + AddIcon /icons/odf6odi-20x22.png .odi + AddIcon /icons/odf6odm-20x22.png .odm -AddIcon /icons/odf6ott-20x22.png .ott -AddIcon /icons/odf6ots-20x22.png .ots -AddIcon /icons/odf6otp-20x22.png .otp -AddIcon /icons/odf6otg-20x22.png .otg -AddIcon /icons/odf6otc-20x22.png .otc -AddIcon /icons/odf6otf-20x22.png .otf -AddIcon /icons/odf6oti-20x22.png .oti -AddIcon /icons/odf6oth-20x22.png .oth + AddIcon /icons/odf6ott-20x22.png .ott + AddIcon /icons/odf6ots-20x22.png .ots + AddIcon /icons/odf6otp-20x22.png .otp + AddIcon /icons/odf6otg-20x22.png .otg + AddIcon /icons/odf6otc-20x22.png .otc + AddIcon /icons/odf6otf-20x22.png .otf + AddIcon /icons/odf6oti-20x22.png .oti + AddIcon /icons/odf6oth-20x22.png .oth -# -# DefaultIcon is which icon to show for files which do not have an icon -# explicitly set. -# -DefaultIcon /icons/unknown.gif + # + # DefaultIcon is which icon to show for files which do not have an icon + # explicitly set. + # + DefaultIcon /icons/unknown.gif -# -# AddDescription allows you to place a short description after a file in -# server-generated indexes. These are only displayed for FancyIndexed -# directories. -# Format: AddDescription "description" filename -# -#AddDescription "GZIP compressed document" .gz -#AddDescription "tar archive" .tar -#AddDescription "GZIP compressed tar archive" .tgz + # + # AddDescription allows you to place a short description after a file in + # server-generated indexes. These are only displayed for FancyIndexed + # directories. + # Format: AddDescription "description" filename + # + #AddDescription "GZIP compressed document" .gz + #AddDescription "tar archive" .tar + #AddDescription "GZIP compressed tar archive" .tgz -# -# ReadmeName is the name of the README file the server will look for by -# default, and append to directory listings. -# -# HeaderName is the name of a file which should be prepended to -# directory indexes. -ReadmeName README.html -HeaderName HEADER.html - -# -# IndexIgnore is a set of filenames which directory indexing should ignore -# and not include in the listing. Shell-style wildcarding is permitted. -# -IndexIgnore .??* *~ *# RCS CVS *,v *,t + # + # ReadmeName is the name of the README file the server will look for by + # default, and append to directory listings. + # + # HeaderName is the name of a file which should be prepended to + # directory indexes. + ReadmeName README.html + HeaderName HEADER.html + # + # IndexIgnore is a set of filenames which directory indexing should ignore + # and not include in the listing. Shell-style wildcarding is permitted. + # + IndexIgnore .??* *~ *# RCS CVS *,v *,t diff --git a/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb b/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb index 2e41975..4a312b2 100644 --- a/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb @@ -1,16 +1,16 @@ - AddOutputFilterByType DEFLATE text/html - AddOutputFilterByType DEFLATE text/css - AddOutputFilterByType DEFLATE text/plain - AddOutputFilterByType DEFLATE text/xml - AddOutputFilterByType DEFLATE application/xhtml+xml - AddOutputFilterByType DEFLATE application/xml - AddOutputFilterByType DEFLATE image/svg+xml - AddOutputFilterByType DEFLATE application/rss+xml - AddOutputFilterByType DEFLATE application/atom_xml - AddOutputFilterByType DEFLATE application/javascript - AddOutputFilterByType DEFLATE application/x-javascript - AddOutputFilterByType DEFLATE application/x-httpd-php - AddOutputFilterByType DEFLATE application/x-httpd-fastphp - AddOutputFilterByType DEFLATE application/x-httpd-eruby + AddOutputFilterByType DEFLATE text/html + AddOutputFilterByType DEFLATE text/css + AddOutputFilterByType DEFLATE text/plain + AddOutputFilterByType DEFLATE text/xml + AddOutputFilterByType DEFLATE application/xhtml+xml + AddOutputFilterByType DEFLATE application/xml + AddOutputFilterByType DEFLATE image/svg+xml + AddOutputFilterByType DEFLATE application/rss+xml + AddOutputFilterByType DEFLATE application/atom_xml + AddOutputFilterByType DEFLATE application/javascript + AddOutputFilterByType DEFLATE application/x-javascript + AddOutputFilterByType DEFLATE application/x-httpd-php + AddOutputFilterByType DEFLATE application/x-httpd-fastphp + AddOutputFilterByType DEFLATE application/x-httpd-eruby diff --git a/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb b/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb index e16fcb3..dd6cea1 100644 --- a/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb @@ -1,5 +1,3 @@ - - DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm - + DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm diff --git a/chef/cookbooks/apache2/templates/default/mods/fastcgi.conf.erb b/chef/cookbooks/apache2/templates/default/mods/fastcgi.conf.erb index a252609..add16d5 100644 --- a/chef/cookbooks/apache2/templates/default/mods/fastcgi.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/fastcgi.conf.erb @@ -1,5 +1,5 @@ AddHandler fastcgi-script .fcgi #FastCgiWrapper /usr/lib/apache2/suexec - FastCgiIpcDir /var/lib/apache2/fastcgi + FastCgiIpcDir <%= "#{node['apache']['lib_dir']}/fastcgi" %> diff --git a/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb b/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb index b314292..d13e8a6 100644 --- a/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb @@ -3,7 +3,7 @@ IPCConnectTimeout 20 -<% if %w{ rhel fedora }.include?(node['platform_family']) -%> +<% if %w[rhel fedora].include?(node['platform_family']) -%> # Sane place to put sockets and shared memory file SocketPath run/mod_fcgid SharememPath run/mod_fcgid/fcgid_shm diff --git a/chef/cookbooks/apache2/templates/default/mods/info.conf.erb b/chef/cookbooks/apache2/templates/default/mods/info.conf.erb new file mode 100644 index 0000000..1d0e7ea --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/info.conf.erb @@ -0,0 +1,14 @@ + + # + # Allow server info reports generated by mod_info, + # with the URL of http://servername/server-info + # Uncomment and change the ".example.com" to allow + # access from other hosts. + # + + SetHandler server-info + Order deny,allow + Deny from all + Allow from <%= node['apache']['info_allow_list'] %> + + diff --git a/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb b/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb index 3f21225..56d1fca 100644 --- a/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb @@ -1,198 +1,199 @@ + # + # TypesConfig points to the file containing the list of mappings from + # filename extension to MIME-type. + # + <% case node['platform_family'] -%> + <% when 'arch' -%> + TypesConfig <%= node['apache']['dir'] %>/conf/mime.types + <% when 'freebsd' -%> + TypesConfig <%= node['apache']['dir'] %>/mime.types + <% else -%> + TypesConfig /etc/mime.types + <% end -%> -# -# TypesConfig points to the file containing the list of mappings from -# filename extension to MIME-type. -# -<% case node['platform_family'] -%> -<% when "arch" -%> -TypesConfig <%= node['apache']['dir'] %>/conf/mime.types -<% when "freebsd" -%> -TypesConfig <%= node['apache']['dir'] %>/mime.types -<% else -%> -TypesConfig /etc/mime.types -<% end -%> + # + # AddType allows you to add to or override the MIME configuration + # file mime.types for specific file types. + # + #AddType application/x-gzip .tgz + # + # AddEncoding allows you to have certain browsers uncompress + # information on the fly. Note: Not all browsers support this. + # Despite the name similarity, the following Add* directives have + # nothing to do with the FancyIndexing customization directives above. + # + #AddEncoding x-compress .Z + #AddEncoding x-gzip .gz .tgz + #AddEncoding x-bzip2 .bz2 + # + # If the AddEncoding directives above are commented-out, then you + # probably should define those extensions to indicate media types: + # + AddType application/x-compress .Z + AddType application/x-gzip .gz .tgz + AddType application/x-bzip2 .bz2 -# -# AddType allows you to add to or override the MIME configuration -# file mime.types for specific file types. -# -#AddType application/x-gzip .tgz -# -# AddEncoding allows you to have certain browsers uncompress -# information on the fly. Note: Not all browsers support this. -# Despite the name similarity, the following Add* directives have -# nothing to do with the FancyIndexing customization directives above. -# -#AddEncoding x-compress .Z -#AddEncoding x-gzip .gz .tgz -#AddEncoding x-bzip2 .bz2 -# -# If the AddEncoding directives above are commented-out, then you -# probably should define those extensions to indicate media types: -# -AddType application/x-compress .Z -AddType application/x-gzip .gz .tgz -AddType application/x-bzip2 .bz2 + AddType image/svg+xml svg svgz + AddEncoding gzip svgz -# -# DefaultLanguage and AddLanguage allows you to specify the language of -# a document. You can then use content negotiation to give a browser a -# file in a language the user can understand. -# -# Specify a default language. This means that all data -# going out without a specific language tag (see below) will -# be marked with this one. You probably do NOT want to set -# this unless you are sure it is correct for all cases. -# -# * It is generally better to not mark a page as -# * being a certain language than marking it with the wrong -# * language! -# -# DefaultLanguage nl -# -# Note 1: The suffix does not have to be the same as the language -# keyword --- those with documents in Polish (whose net-standard -# language code is pl) may wish to use "AddLanguage pl .po" to -# avoid the ambiguity with the common suffix for perl scripts. -# -# Note 2: The example entries below illustrate that in some cases -# the two character 'Language' abbreviation is not identical to -# the two character 'Country' code for its country, -# E.g. 'Danmark/dk' versus 'Danish/da'. -# -# Note 3: In the case of 'ltz' we violate the RFC by using a three char -# specifier. There is 'work in progress' to fix this and get -# the reference data for rfc1766 cleaned up. -# -# Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) -# English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de) -# Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja) -# Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn) -# Norwegian (no) - Polish (pl) - Portugese (pt) -# Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv) -# Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW) -# -AddLanguage ca .ca -AddLanguage cs .cz .cs -AddLanguage da .dk -AddLanguage de .de -AddLanguage el .el -AddLanguage en .en -AddLanguage eo .eo -# See README.Debian for Spanish -AddLanguage es .es -AddLanguage et .et -AddLanguage fr .fr -AddLanguage he .he -AddLanguage hr .hr -AddLanguage it .it -AddLanguage ja .ja -AddLanguage ko .ko -AddLanguage ltz .ltz -AddLanguage nl .nl -AddLanguage nn .nn -AddLanguage no .no -AddLanguage pl .po -AddLanguage pt .pt -AddLanguage pt-BR .pt-br -AddLanguage ru .ru -AddLanguage sv .sv -# See README.Debian for Turkish -AddLanguage tr .tr -AddLanguage zh-CN .zh-cn -AddLanguage zh-TW .zh-tw + # + # DefaultLanguage and AddLanguage allows you to specify the language of + # a document. You can then use content negotiation to give a browser a + # file in a language the user can understand. + # + # Specify a default language. This means that all data + # going out without a specific language tag (see below) will + # be marked with this one. You probably do NOT want to set + # this unless you are sure it is correct for all cases. + # + # * It is generally better to not mark a page as + # * being a certain language than marking it with the wrong + # * language! + # + # DefaultLanguage nl + # + # Note 1: The suffix does not have to be the same as the language + # keyword --- those with documents in Polish (whose net-standard + # language code is pl) may wish to use "AddLanguage pl .po" to + # avoid the ambiguity with the common suffix for perl scripts. + # + # Note 2: The example entries below illustrate that in some cases + # the two character 'Language' abbreviation is not identical to + # the two character 'Country' code for its country, + # E.g. 'Danmark/dk' versus 'Danish/da'. + # + # Note 3: In the case of 'ltz' we violate the RFC by using a three char + # specifier. There is 'work in progress' to fix this and get + # the reference data for rfc1766 cleaned up. + # + # Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) + # English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de) + # Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja) + # Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn) + # Norwegian (no) - Polish (pl) - Portugese (pt) + # Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv) + # Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW) + # + AddLanguage ca .ca + AddLanguage cs .cz .cs + AddLanguage da .dk + AddLanguage de .de + AddLanguage el .el + AddLanguage en .en + AddLanguage eo .eo + # See README.Debian for Spanish + AddLanguage es .es + AddLanguage et .et + AddLanguage fr .fr + AddLanguage he .he + AddLanguage hr .hr + AddLanguage it .it + AddLanguage ja .ja + AddLanguage ko .ko + AddLanguage ltz .ltz + AddLanguage nl .nl + AddLanguage nn .nn + AddLanguage no .no + AddLanguage pl .po + AddLanguage pt .pt + AddLanguage pt-BR .pt-br + AddLanguage ru .ru + AddLanguage sv .sv + # See README.Debian for Turkish + AddLanguage tr .tr + AddLanguage zh-CN .zh-cn + AddLanguage zh-TW .zh-tw -# -# Commonly used filename extensions to character sets. You probably -# want to avoid clashes with the language extensions, unless you -# are good at carefully testing your setup after each change. -# See http://www.iana.org/assignments/character-sets for the -# official list of charset names and their respective RFCs. -# -AddCharset us-ascii .ascii .us-ascii -AddCharset ISO-8859-1 .iso8859-1 .latin1 -AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen -AddCharset ISO-8859-3 .iso8859-3 .latin3 -AddCharset ISO-8859-4 .iso8859-4 .latin4 -AddCharset ISO-8859-5 .iso8859-5 .cyr .iso-ru -AddCharset ISO-8859-6 .iso8859-6 .arb .arabic -AddCharset ISO-8859-7 .iso8859-7 .grk .greek -AddCharset ISO-8859-8 .iso8859-8 .heb .hebrew -AddCharset ISO-8859-9 .iso8859-9 .latin5 .trk -AddCharset ISO-8859-10 .iso8859-10 .latin6 -AddCharset ISO-8859-13 .iso8859-13 -AddCharset ISO-8859-14 .iso8859-14 .latin8 -AddCharset ISO-8859-15 .iso8859-15 .latin9 -AddCharset ISO-8859-16 .iso8859-16 .latin10 -AddCharset ISO-2022-JP .iso2022-jp .jis -AddCharset ISO-2022-KR .iso2022-kr .kis -AddCharset ISO-2022-CN .iso2022-cn .cis -AddCharset Big5 .Big5 .big5 .b5 -AddCharset cn-Big5 .cn-big5 -# For russian, more than one charset is used (depends on client, mostly): -AddCharset WINDOWS-1251 .cp-1251 .win-1251 -AddCharset CP866 .cp866 -AddCharset KOI8 .koi8 -AddCharset KOI8-E .koi8-e -AddCharset KOI8-r .koi8-r .koi8-ru -AddCharset KOI8-U .koi8-u -AddCharset KOI8-ru .koi8-uk .ua -AddCharset ISO-10646-UCS-2 .ucs2 -AddCharset ISO-10646-UCS-4 .ucs4 -AddCharset UTF-7 .utf7 -AddCharset UTF-8 .utf8 -AddCharset UTF-16 .utf16 -AddCharset UTF-16BE .utf16be -AddCharset UTF-16LE .utf16le -AddCharset UTF-32 .utf32 -AddCharset UTF-32BE .utf32be -AddCharset UTF-32LE .utf32le -AddCharset euc-cn .euc-cn -AddCharset euc-gb .euc-gb -AddCharset euc-jp .euc-jp -AddCharset euc-kr .euc-kr -#Not sure how euc-tw got in - IANA doesn't list it??? -AddCharset EUC-TW .euc-tw -AddCharset gb2312 .gb2312 .gb -AddCharset iso-10646-ucs-2 .ucs-2 .iso-10646-ucs-2 -AddCharset iso-10646-ucs-4 .ucs-4 .iso-10646-ucs-4 -AddCharset shift_jis .shift_jis .sjis + # + # Commonly used filename extensions to character sets. You probably + # want to avoid clashes with the language extensions, unless you + # are good at carefully testing your setup after each change. + # See http://www.iana.org/assignments/character-sets for the + # official list of charset names and their respective RFCs. + # + AddCharset us-ascii .ascii .us-ascii + AddCharset ISO-8859-1 .iso8859-1 .latin1 + AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen + AddCharset ISO-8859-3 .iso8859-3 .latin3 + AddCharset ISO-8859-4 .iso8859-4 .latin4 + AddCharset ISO-8859-5 .iso8859-5 .cyr .iso-ru + AddCharset ISO-8859-6 .iso8859-6 .arb .arabic + AddCharset ISO-8859-7 .iso8859-7 .grk .greek + AddCharset ISO-8859-8 .iso8859-8 .heb .hebrew + AddCharset ISO-8859-9 .iso8859-9 .latin5 .trk + AddCharset ISO-8859-10 .iso8859-10 .latin6 + AddCharset ISO-8859-13 .iso8859-13 + AddCharset ISO-8859-14 .iso8859-14 .latin8 + AddCharset ISO-8859-15 .iso8859-15 .latin9 + AddCharset ISO-8859-16 .iso8859-16 .latin10 + AddCharset ISO-2022-JP .iso2022-jp .jis + AddCharset ISO-2022-KR .iso2022-kr .kis + AddCharset ISO-2022-CN .iso2022-cn .cis + AddCharset Big5 .Big5 .big5 .b5 + AddCharset cn-Big5 .cn-big5 + # For russian, more than one charset is used (depends on client, mostly): + AddCharset WINDOWS-1251 .cp-1251 .win-1251 + AddCharset CP866 .cp866 + AddCharset KOI8 .koi8 + AddCharset KOI8-E .koi8-e + AddCharset KOI8-r .koi8-r .koi8-ru + AddCharset KOI8-U .koi8-u + AddCharset KOI8-ru .koi8-uk .ua + AddCharset ISO-10646-UCS-2 .ucs2 + AddCharset ISO-10646-UCS-4 .ucs4 + AddCharset UTF-7 .utf7 + AddCharset UTF-8 .utf8 + AddCharset UTF-16 .utf16 + AddCharset UTF-16BE .utf16be + AddCharset UTF-16LE .utf16le + AddCharset UTF-32 .utf32 + AddCharset UTF-32BE .utf32be + AddCharset UTF-32LE .utf32le + AddCharset euc-cn .euc-cn + AddCharset euc-gb .euc-gb + AddCharset euc-jp .euc-jp + AddCharset euc-kr .euc-kr + #Not sure how euc-tw got in - IANA doesn't list it??? + AddCharset EUC-TW .euc-tw + AddCharset gb2312 .gb2312 .gb + AddCharset iso-10646-ucs-2 .ucs-2 .iso-10646-ucs-2 + AddCharset iso-10646-ucs-4 .ucs-4 .iso-10646-ucs-4 + AddCharset shift_jis .shift_jis .sjis -# -# AddHandler allows you to map certain file extensions to "handlers": -# actions unrelated to filetype. These can be either built into the server -# or added with the Action directive (see below) -# -# To use CGI scripts outside of ScriptAliased directories: -# (You will also need to add "ExecCGI" to the "Options" directive.) -# -#AddHandler cgi-script .cgi + # + # AddHandler allows you to map certain file extensions to "handlers": + # actions unrelated to filetype. These can be either built into the server + # or added with the Action directive (see below) + # + # To use CGI scripts outside of ScriptAliased directories: + # (You will also need to add "ExecCGI" to the "Options" directive.) + # + #AddHandler cgi-script .cgi -# -# For files that include their own HTTP headers: -# -#AddHandler send-as-is asis + # + # For files that include their own HTTP headers: + # + #AddHandler send-as-is asis -# -# For server-parsed imagemap files: -# -#AddHandler imap-file map + # + # For server-parsed imagemap files: + # + #AddHandler imap-file map -# -# For type maps (negotiated resources): -# (This is enabled by default to allow the Apache "It Worked" page -# to be distributed in multiple languages.) -# -AddHandler type-map var - -# -# Filters allow you to process content before it is sent to the client. -# -# To parse .shtml files for server-side includes (SSI): -# (You will also need to add "Includes" to the "Options" directive.) -# -AddType text/html .shtml -AddOutputFilter INCLUDES .shtml + # + # For type maps (negotiated resources): + # (This is enabled by default to allow the Apache "It Worked" page + # to be distributed in multiple languages.) + # + AddHandler type-map var + # + # Filters allow you to process content before it is sent to the client. + # + # To parse .shtml files for server-side includes (SSI): + # (You will also need to add "Includes" to the "Options" directive.) + # + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml diff --git a/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb b/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb index 0e3455b..6bea05f 100644 --- a/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb @@ -1,18 +1,17 @@ -# -# LanguagePriority allows you to give precedence to some languages -# in case of a tie during content negotiation. -# -# Just list the languages in decreasing order of preference. We have -# more or less alphabetized them here. You probably want to change this. -# -LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv tr zh-CN zh-TW - -# -# ForceLanguagePriority allows you to serve a result page rather than -# MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback) -# [in case no accepted languages matched the available variants] -# -ForceLanguagePriority Prefer Fallback + # + # LanguagePriority allows you to give precedence to some languages + # in case of a tie during content negotiation. + # + # Just list the languages in decreasing order of preference. We have + # more or less alphabetized them here. You probably want to change this. + # + LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv tr zh-CN zh-TW + # + # ForceLanguagePriority allows you to serve a result page rather than + # MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback) + # [in case no accepted languages matched the available variants] + # + ForceLanguagePriority Prefer Fallback diff --git a/chef/cookbooks/apache2/templates/default/mods/pagespeed.conf.erb b/chef/cookbooks/apache2/templates/default/mods/pagespeed.conf.erb new file mode 100644 index 0000000..e744b07 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/pagespeed.conf.erb @@ -0,0 +1,293 @@ + + # Turn on mod_pagespeed. To completely disable mod_pagespeed, you + # can set this to "off". + ModPagespeed on + + # We want VHosts to inherit global configuration. + # If this is not included, they'll be independent (except for inherently + # global options), at least for backwards compatibility. + ModPagespeedInheritVHostConfig on + + # Direct Apache to send all HTML output to the mod_pagespeed + # output handler. + AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER text/html + + # If you want mod_pagespeed process XHTML as well, please uncomment this + # line. + # AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER application/xhtml+xml + + # The ModPagespeedFileCachePath directory must exist and be writable + # by the apache user (as specified by the User directive). + ModPagespeedFileCachePath "/var/cache/mod_pagespeed/" + + # Override the mod_pagespeed 'rewrite level'. The default level + # "CoreFilters" uses a set of rewrite filters that are generally + # safe for most web pages. Most sites should not need to change + # this value and can instead fine-tune the configuration using the + # ModPagespeedDisableFilters and ModPagespeedEnableFilters + # directives, below. Valid values for ModPagespeedRewriteLevel are + # PassThrough, CoreFilters and TestingCoreFilters. + # + # ModPagespeedRewriteLevel PassThrough + + # Explicitly disables specific filters. This is useful in + # conjuction with ModPagespeedRewriteLevel. For instance, if one + # of the filters in the CoreFilters needs to be disabled for a + # site, that filter can be added to + # ModPagespeedDisableFilters. This directive contains a + # comma-separated list of filter names, and can be repeated. + # + # ModPagespeedDisableFilters rewrite_images + + # Explicitly enables specific filters. This is useful in + # conjuction with ModPagespeedRewriteLevel. For instance, filters + # not included in the CoreFilters may be enabled using this + # directive. This directive contains a comma-separated list of + # filter names, and can be repeated. + # + # ModPagespeedEnableFilters rewrite_javascript,rewrite_css + # ModPagespeedEnableFilters collapse_whitespace,elide_attributes + + # ModPagespeedDomain + # authorizes rewriting of JS, CSS, and Image files found in this + # domain. By default only resources with the same origin as the + # HTML file are rewritten. For example: + # + # ModPagespeedDomain cdn.myhost.com + # + # This will allow resources found on http://cdn.myhost.com to be + # rewritten in addition to those in the same domain as the HTML. + # + # Wildcards (* and ?) are allowed in the domain specification. Be + # careful when using them as if you rewrite domains that do not + # send you traffic, then the site receiving the traffic will not + # know how to serve the rewritten content. + + # Other defaults (cache sizes and thresholds): + # + # ModPagespeedFileCacheSizeKb 102400 + # ModPagespeedFileCacheCleanIntervalMs 3600000 + # ModPagespeedLRUCacheKbPerProcess 1024 + # ModPagespeedLRUCacheByteLimit 16384 + # ModPagespeedCssFlattenMaxBytes 2048 + # ModPagespeedCssInlineMaxBytes 2048 + # ModPagespeedCssImageInlineMaxBytes 2048 + # ModPagespeedImageInlineMaxBytes 2048 + # ModPagespeedJsInlineMaxBytes 2048 + # ModPagespeedCssOutlineMinBytes 3000 + # ModPagespeedJsOutlineMinBytes 3000 + + # Limit the number of inodes in the file cache. Set to 0 for no limit. + # The default value if this paramater is not specified is 0 (no limit). + ModPagespeedFileCacheInodeLimit 500000 + + # Bound the number of images that can be rewritten at any one time; this + # avoids overloading the CPU. Set this to 0 to remove the bound. + # + # ModPagespeedImageMaxRewritesAtOnce 8 + + # You can also customize the number of threads per Apache process + # mod_pagespeed will use to do resource optimization. Plain + # "rewrite threads" are used to do short, latency-sensitive work, + # while "expensive rewrite threads" are used for actual optimization + # work that's more computationally expensive. If you live these unset, + # or use values <= 0 the defaults will be used, which is 1 for both + # values when using non-threaded MPMs (e.g. prefork) and 4 for both + # on threaded MPMs (e.g. worker and event). These settings can only + # be changed globally, and not per virtual host. + # + # ModPagespeedNumRewriteThreads 4 + # ModPagespeedNumExpensiveRewriteThreads 4 + + + # Settings for image optimization: + # + # Jpeg recompression quality (0 to 100, -1 strips metadata): + # ModPagespeedJpegRecompressionQuality -1 + # + # Percent of original image size below which optimized images are retained: + # ModPagespeedImageLimitOptimizedPercent 100 + # + # Percent of original image area below which image resizing will be + # attempted: + # ModPagespeedImageLimitResizeAreaPercent 100 + + # When Apache is set up as a browser proxy, mod_pagespeed can record + # web-sites as they are requested, so that an image of the web is built up + # in the directory of the proxy administrator's choosing. When ReadOnly is + # on, only files already present in the SlurpDirectory are served by the + # proxy. + # + # ModPagespeedSlurpDirectory ... + # ModPagespeedSlurpReadOnly on + + # The maximum URL size is generally limited to about 2k characters + # due to IE: See http://support.microsoft.com/kb/208427/EN-US. + # Apache servers by default impose a further limitation of about + # 250 characters per URL segment (text between slashes). + # mod_pagespeed circumvents this limitation, but if you employ + # proxy servers in your path you may need to re-impose it by + # overriding the setting here. The default setting is 1024 + # characters. + # + # ModPagespeedMaxSegmentLength 250 + + # Uncomment this if you want to prevent mod_pagespeed from combining files + # (e.g. CSS files) across paths + # + # ModPagespeedCombineAcrossPaths off + + # Renaming JavaScript URLs can sometimes break them. With this + # option enabled, mod_pagespeed uses a simple heuristic to decide + # not to rename JavaScript that it thinks is introspective. + # + # You can turn this off to let mod_pagespeed rename all JS files. + ModPagespeedAvoidRenamingIntrospectiveJavascript on + + # Certain common JavaScript libraries are available from Google, which acts + # as a CDN and allows you to benefit from browser caching if a new visitor + # to your site previously visited another site that makes use of the same + # libraries as you do. Enable the following filter to turn on this feature. + # + # ModPagespeedEnableFilters canonicalize_javascript_libraries + + # The following lines configure libraries that are recognized by + # canonicalize_javascript_libraries. These will have no effect unless you + # enable this filter (generally by uncommenting the last line in the + # previous stanza). It simply provides a sensible default configuration + # when the filter is switched on. + # The format is: + # ModPagespeedLibrary bytes md5 canonical_url + # Where bytes and md5 are with respect to the *minified* JS; use + # js_minify --print_size_and_hash to obtain this data. + # Note that we can register multiple hashes for the same canonical url; + # we do this if there are versions available that have already been minified + # with more sophisticated tools. + ModPagespeedLibrary 105527 ltVVzzYxo0 //ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js + ModPagespeedLibrary 92501 J8KF47pYOq //ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js + ModPagespeedLibrary 141547 GKjMUuF4PK //ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js + ModPagespeedLibrary 43 1o978_K0_L http://www.modpagespeed.com/rewrite_javascript.js + + # Explicitly tell mod_pagespeed to load some resources from disk. + # This will speed up load time and update frequency. + # + # This should only be used for static resources which do not need + # specific headers set or other processing by Apache. + # + # Both URL and filesystem path should specify directories and + # filesystem path must be absolute (for now). + # + # ModPagespeedLoadFromFile "http://example.com/static/" "/var/www/static/" + + + # Enables server-side instrumentation and statistics. If this rewriter is + # enabled, then each rewritten HTML page will have instrumentation javacript + # added that sends latency beacons to /mod_pagespeed_beacon. These + # statistics can be accessed at /mod_pagespeed_statistics. You must also + # enable the mod_pagespeed_statistics and mod_pagespeed_beacon handlers + # below. + # + # ModPagespeedEnableFilters add_instrumentation + + # The add_instrumentation filter sends a beacon after the page onload + # handler is called. The user might navigate to a new URL before this. If + # you enable the following directive, the beacon is sent as part of an + # onbeforeunload handler, for pages where navigation happens before the + # onload event. + # + # ModPagespeedReportUnloadTime on + + # Uncomment the following line so that ModPagespeed will not cache or + # rewrite resources with Vary: in the header, e.g. Vary: User-Agent. + # ModPagespeedRespectVary on + + # This handles the client-side instrumentation callbacks which are injected + # by the add_instrumentation filter. + # You can use a different location by adding the ModPagespeedBeaconUrl + # directive; see the documentation on add_instrumentation. + + SetHandler mod_pagespeed_beacon + + + # Uncomment the following line if you want to disable statistics entirely. + # + # ModPagespeedStatistics off + + # This page lets you view statistics about the mod_pagespeed module. + + Order allow,deny + # You may insert other "Allow from" lines to add hosts you want to + # allow to look at generated statistics. Another possibility is + # to comment out the "Order" and "Allow" options from the config + # file, to allow any client that can reach your server to examine + # statistics. This might be appropriate in an experimental setup or + # if the Apache server is protected by a reverse proxy that will + # filter URLs in some fashion. + Allow from localhost + Allow from 127.0.0.1 + SetHandler mod_pagespeed_statistics + + + # Uncomment the following line if you want to enable statistics logging. + # ModPagespeedStatistics is required to be enabled. + # + # ModPagespeedStatisticsLogging on + # + # The base filename to use to store logged statistics. + # Required if logging is enabled. + # + # ModPagespeedStatisticsLoggingFile "@@MOD_PAGESPEED_STATS_LOG@@" + # + # The interval at which statistics will be logged, in milliseconds. + # Optional; default is 3000. + # + # ModPagespeedStatisticsLoggingIntervalMs 3000 + + # If both of the below are set, the console will use offline copies of the + # files needed for the Google Chart Tools API rather than connecting to the + # Internet to obtain them. This is experimental, as the only supported + # loading mechanism for the Chart Tools API requires an Internet connexion. + # + # Where to find an offline copy of the CSS file required for the Google + # Chart Tools API. At the time of writing, the Google Chart Tools API CSS + # file can be found at: + # https://ajax.googleapis.com/ajax/static/modules/gviz/1.0/core/tooltip.css + # + # ModPagespeedStatisticsLoggingChartsCSS http://example.com/charts.css + # + # Where to find an offline copy of the JS file required for the Google + # Chart Tools API. At the time of writing, the Google Chart Tools API JS + # file can be found at: + # https://www.google.com/uds/api/visualization/1.0/d7d36793f7a886b687850d2813583db9/format+en,default,corechart.I.js + # + # ModPagespeedStatisticsLoggingChartsJS http://example.com/charts.js + + # This page lets you view a graphical console displaying statistics about + # the mod_pagespeed module. + + Order allow,deny + # This can be configured similarly to mod_pagespeed_statistics above. + Allow from localhost + Allow from 127.0.0.1 + SetHandler mod_pagespeed_console + + + # Page /mod_pagespeed_message lets you view the latest messages from + # mod_pagespeed, regardless of log-level in your httpd.conf + # ModPagespeedMessageBufferSize is the maximum number of bytes you would + # like to dump to your /mod_pagespeed_message page at one time, + # its default value is 100k bytes. + # Set it to 0 if you want to disable this feature. + ModPagespeedMessageBufferSize 100000 + + + Allow from localhost + Allow from 127.0.0.1 + SetHandler mod_pagespeed_message + + + Allow from localhost + Allow from 127.0.0.1 + SetHandler mod_pagespeed_referer_statistics + + \ No newline at end of file diff --git a/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb b/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb index 46407a1..553a3ca 100644 --- a/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb @@ -1,19 +1,19 @@ - #turning ProxyRequests on and allowing proxying from all may allow - #spammers to use your proxy to send email. + #turning ProxyRequests on and allowing proxying from all may allow + #spammers to use your proxy to send email. - ProxyRequests Off + ProxyRequests Off - - AddDefaultCharset off - Order deny,allow - Deny from all - #Allow from .example.com - + + AddDefaultCharset off + Order <%= node['apache']['proxy']['order'] %> + Deny from <%= node['apache']['proxy']['deny_from'] %> + Allow from <%= node['apache']['proxy']['allow_from'] %> + - # Enable/disable the handling of HTTP/1.1 "Via:" headers. - # ("Full" adds the server version; "Block" removes all outgoing Via: headers) - # Set to one of: Off | On | Full | Block + # Enable/disable the handling of HTTP/1.1 "Via:" headers. + # ("Full" adds the server version; "Block" removes all outgoing Via: headers) + # Set to one of: Off | On | Full | Block - ProxyVia On + ProxyVia On diff --git a/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb b/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb index 6b7d6e2..832fb1b 100644 --- a/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb @@ -1,28 +1,26 @@ + # + # The following directives modify normal HTTP response behavior to + # handle known problems with browser implementations. + # + BrowserMatch "Mozilla/2" nokeepalive + BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 + BrowserMatch "RealPlayer 4\.0" force-response-1.0 + BrowserMatch "Java/1\.0" force-response-1.0 + BrowserMatch "JDK/1\.0" force-response-1.0 -# -# The following directives modify normal HTTP response behavior to -# handle known problems with browser implementations. -# -BrowserMatch "Mozilla/2" nokeepalive -BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 -BrowserMatch "RealPlayer 4\.0" force-response-1.0 -BrowserMatch "Java/1\.0" force-response-1.0 -BrowserMatch "JDK/1\.0" force-response-1.0 - -# -# The following directive disables redirects on non-GET requests for -# a directory that does not include the trailing slash. This fixes a -# problem with Microsoft WebFolders which does not appropriately handle -# redirects for folders with DAV methods. -# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV. -# -BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully -BrowserMatch "MS FrontPage" redirect-carefully -BrowserMatch "^WebDrive" redirect-carefully -BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully -BrowserMatch "^gnome-vfs/1.0" redirect-carefully -BrowserMatch "^XML Spy" redirect-carefully -BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully - + # + # The following directive disables redirects on non-GET requests for + # a directory that does not include the trailing slash. This fixes a + # problem with Microsoft WebFolders which does not appropriately handle + # redirects for folders with DAV methods. + # Same deal with Apple's DAV filesystem and Gnome VFS support for DAV. + # + BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully + BrowserMatch "MS FrontPage" redirect-carefully + BrowserMatch "^WebDrive" redirect-carefully + BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully + BrowserMatch "^gnome-vfs/1.0" redirect-carefully + BrowserMatch "^XML Spy" redirect-carefully + BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully diff --git a/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb b/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb index f98c621..6154b64 100644 --- a/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb @@ -1,83 +1,76 @@ -# -# Pseudo Random Number Generator (PRNG): -# Configure one or more sources to seed the PRNG of the SSL library. -# The seed data should be of good random quality. -# WARNING! On some platforms /dev/random blocks if not enough entropy -# is available. This means you then cannot use the /dev/random device -# because it would lead to very long connection times (as long as -# it requires to make more entropy available). But usually those -# platforms additionally provide a /dev/urandom device which doesn't -# block. So, if available, use this one instead. Read the mod_ssl User -# Manual for more details. -# -SSLRandomSeed startup builtin -SSLRandomSeed startup file:/dev/urandom 512 -SSLRandomSeed connect builtin -SSLRandomSeed connect file:/dev/urandom 512 + # + # Pseudo Random Number Generator (PRNG): + # Configure one or more sources to seed the PRNG of the SSL library. + # The seed data should be of good random quality. + # WARNING! On some platforms /dev/random blocks if not enough entropy + # is available. This means you then cannot use the /dev/random device + # because it would lead to very long connection times (as long as + # it requires to make more entropy available). But usually those + # platforms additionally provide a /dev/urandom device which doesn't + # block. So, if available, use this one instead. Read the mod_ssl User + # Manual for more details. + # + SSLRandomSeed startup builtin + SSLRandomSeed startup file:/dev/urandom 512 + SSLRandomSeed connect builtin + SSLRandomSeed connect file:/dev/urandom 512 -## -## SSL Global Context -## -## All SSL configuration in this context applies both to -## the main server and all SSL-enabled virtual hosts. -## + ## + ## SSL Global Context + ## + ## All SSL configuration in this context applies both to + ## the main server and all SSL-enabled virtual hosts. + ## -# -# Some MIME-types for downloading Certificates and CRLs -# -AddType application/x-x509-ca-cert .crt -AddType application/x-pkcs7-crl .crl + # + # Some MIME-types for downloading Certificates and CRLs + # + AddType application/x-x509-ca-cert .crt + AddType application/x-pkcs7-crl .crl -# Pass Phrase Dialog: -# Configure the pass phrase gathering process. -# The filtering dialog program (`builtin' is a internal -# terminal dialog) has to provide the pass phrase on stdout. -SSLPassPhraseDialog builtin + # Pass Phrase Dialog: + # Configure the pass phrase gathering process. + # The filtering dialog program (`builtin' is a internal + # terminal dialog) has to provide the pass phrase on stdout. + SSLPassPhraseDialog builtin -# Inter-Process Session Cache: -# Configure the SSL Session Cache: First the mechanism -# to use and second the expiring timeout (in seconds). -#SSLSessionCache dbm:/var/run/apache2/ssl_scache -<% if %w{ rhel fedora suse }.include?(node['platform_family']) -%> -SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) -<% elsif %w{ freebsd }.include?(node['platform_family']) -%> -SSLSessionCache shmcb:/var/run/ssl_scache(512000) -<% else -%> -SSLSessionCache shmcb:/var/run/apache2/ssl_scache -<% end -%> -SSLSessionCacheTimeout 300 - -# Semaphore: -# Configure the path to the mutual exclusion semaphore the -# SSL engine uses internally for inter-process synchronization. -<% if %w{debian}.include?(node['platform_family']) -%> - <% if node['platform_version'].to_f >= 14 -%> -Mutex file:/var/run/apache2 default - <% else %> -SSLMutex file:/var/run/apache2/ssl_mutex + # Inter-Process Session Cache: + # Configure the SSL Session Cache: First the mechanism + # to use and second the expiring timeout (in seconds). + #SSLSessionCache dbm:/var/run/apache2/ssl_scache + <% if %w[rhel fedora suse].include?(node['platform_family']) -%> + SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) + <% elsif %w[freebsd].include?(node['platform_family']) -%> + SSLSessionCache shmcb:/var/run/ssl_scache(512000) + <% else -%> + SSLSessionCache shmcb:/var/run/apache2/ssl_scache <% end -%> -<% elsif %w{ rhel fedora suse }.include?(node['platform_family']) -%> -SSLMutex default -<% elsif %w{ freebsd }.include?(node['platform_family']) -%> -SSLMutex file:/var/run/ssl_mutex -<% else -%> -SSLMutex file:/var/run/apache2/ssl_mutex -<% end -%> + SSLSessionCacheTimeout 300 -SSLHonorCipherOrder On -# SSL Cipher Suite: -# List the ciphers that the client is permitted to negotiate. -# See the mod_ssl documentation for a complete list. -# enable only secure ciphers: -SSLCipherSuite <%= node['apache']['mod_ssl']['cipher_suite'] %> -# Use this instead if you want to allow cipher upgrades via SGC facility. -# In this case you also have to use something like -# SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 -# see http://httpd.apache.org/docs/2.2/ssl/ssl_howto.html.en#upgradeenc -#SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + # Semaphore: + # Configure the path to the mutual exclusion semaphore the + # SSL engine uses internally for inter-process synchronization. + <% if %w[rhel fedora suse].include?(node['platform_family']) -%> + SSLMutex default + <% elsif %w[freebsd].include?(node['platform_family']) -%> + SSLMutex file:/var/run/ssl_mutex + <% else -%> + SSLMutex file:/var/run/apache2/ssl_mutex + <% end -%> -# enable only secure protocols: SSLv3 and TLSv1, but not SSLv2 -SSLProtocol all -SSLv2 + SSLHonorCipherOrder On + # SSL Cipher Suite: + # List the ciphers that the client is permitted to negotiate. + # See the mod_ssl documentation for a complete list. + # enable only secure ciphers: + SSLCipherSuite <%= node['apache']['mod_ssl']['cipher_suite'] %> + # Use this instead if you want to allow cipher upgrades via SGC facility. + # In this case you also have to use something like + # SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 + # see http://httpd.apache.org/docs/2.2/ssl/ssl_howto.html.en#upgradeenc + #SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + # enable only secure protocols: SSLv3 and TLSv1, but not SSLv2 + SSLProtocol all -SSLv2 diff --git a/chef/cookbooks/apache2/templates/default/mods/status.conf.erb b/chef/cookbooks/apache2/templates/default/mods/status.conf.erb index 8bafc52..a279f9c 100644 --- a/chef/cookbooks/apache2/templates/default/mods/status.conf.erb +++ b/chef/cookbooks/apache2/templates/default/mods/status.conf.erb @@ -1,26 +1,25 @@ -# -# Allow server status reports generated by mod_status, -# with the URL of http://servername/server-status -# Uncomment and change the ".example.com" to allow -# access from other hosts. -# - + # + # Allow server status reports generated by mod_status, + # with the URL of http://servername/server-status + # Uncomment and change the ".example.com" to allow + # access from other hosts. + # + SetHandler server-status Order deny,allow Deny from all - Allow from <%=node['apache']['status_allow_list']%> -# Allow from .example.com - -# -# ExtendedStatus controls whether Apache will generate "full" status -# information (ExtendedStatus On) or just basic information (ExtendedStatus -# Off) when the "server-status" handler is called. The default is Off. -# -<% if node['apache']['ext_status'] %> -ExtendedStatus On -<% else -%> -ExtendedStatus Off -<% end -%> -# + Allow from <%= node['apache']['status_allow_list'] %> + + + # + # ExtendedStatus controls whether Apache will generate "full" status + # information (ExtendedStatus On) or just basic information (ExtendedStatus + # Off) when the "server-status" handler is called. The default is Off. + # + <% if node['apache']['ext_status'] -%> + ExtendedStatus On + <% else -%> + ExtendedStatus Off + <% end -%> diff --git a/chef/cookbooks/apache2/templates/default/ports.conf.erb b/chef/cookbooks/apache2/templates/default/ports.conf.erb index cc3631e..dcbefc4 100644 --- a/chef/cookbooks/apache2/templates/default/ports.conf.erb +++ b/chef/cookbooks/apache2/templates/default/ports.conf.erb @@ -1,6 +1,9 @@ -#This file generated via template by Chef. -<% @apache_listen_ports.each do |port| -%> -Listen <%= port %> -NameVirtualHost *:<%= port %> +# This file was generated by Chef for <%= node['fqdn'] %>. +# Do NOT modify this file by hand! +<% node['apache']['listen_ports'].map(&:to_i).uniq.each do |port| -%> +<% node['apache']['listen_addresses'].uniq.each do |address| -%> +Listen <%= address.length > 0 ? "#{address}:" : '' %><%= port %> +<% end -%> +NameVirtualHost *:<%= port %> <% end -%> diff --git a/chef/cookbooks/apache2/templates/default/security.erb b/chef/cookbooks/apache2/templates/default/security.erb index d40dbb4..d26e172 100644 --- a/chef/cookbooks/apache2/templates/default/security.erb +++ b/chef/cookbooks/apache2/templates/default/security.erb @@ -11,7 +11,6 @@ # Deny from all # - # Changing the following options will not really affect the security of the # server, but might make attacks slightly more difficult in some cases. @@ -23,8 +22,7 @@ # Set to one of: Full | OS | Minimal | Minor | Major | Prod # where Full conveys the most information, and Prod the least. # -#ServerTokens Minimal -ServerTokens <%= node['apache']['servertokens'] %> +ServerTokens <%= node['apache']['servertokens'] %> # # Optionally add a line containing the server version and virtual host @@ -34,8 +32,7 @@ ServerTokens <%= node['apache']['servertokens'] %> # Set to "EMail" to also include a mailto: link to the ServerAdmin. # Set to one of: On | Off | EMail # -#ServerSignature Off -ServerSignature <%= node['apache']['serversignature'] %> +ServerSignature <%= node['apache']['serversignature'] %> # # Allow TRACE method @@ -45,6 +42,5 @@ ServerSignature <%= node['apache']['serversignature'] %> # # Set to one of: On | Off | extended # -#TraceEnable Off -TraceEnable <%= node['apache']['traceenable'] %> +TraceEnable <%= node['apache']['traceenable'] %> diff --git a/chef/cookbooks/apache2/templates/default/web_app.conf.erb b/chef/cookbooks/apache2/templates/default/web_app.conf.erb index c5d9f95..5999d1e 100644 --- a/chef/cookbooks/apache2/templates/default/web_app.conf.erb +++ b/chef/cookbooks/apache2/templates/default/web_app.conf.erb @@ -1,4 +1,4 @@ - +> ServerName <%= @params[:server_name] %> ServerAlias <% @params[:server_aliases].each do |a| %><%= a %> <% end %> DocumentRoot <%= @params[:docroot] %> diff --git a/chef/cookbooks/apache2/test/features/alias_paths.feature b/chef/cookbooks/apache2/test/features/alias_paths.feature deleted file mode 100644 index 796cba2..0000000 --- a/chef/cookbooks/apache2/test/features/alias_paths.feature +++ /dev/null @@ -1,12 +0,0 @@ -Feature: Alias Paths - -In order to host a website with the URL structure different to the filesystem structure -As a developer -I want to be able to alias paths - - @default @mod_alias - Scenario: Aliased directory - Given a new webserver with aliasing enabled - And an alias defined - When I request the alias path - Then the aliased resource should be returned successfully diff --git a/chef/cookbooks/apache2/test/features/authenticate_basic.feature b/chef/cookbooks/apache2/test/features/authenticate_basic.feature deleted file mode 100644 index f0c56a4..0000000 --- a/chef/cookbooks/apache2/test/features/authenticate_basic.feature +++ /dev/null @@ -1,20 +0,0 @@ -@mod_auth_basic -Feature: Basic Authentication - -In order to perform authorization or vary the provided content -As a developer -I want to authenticate the remote user - - Scenario: Authenticate access to a page - Given a new webserver configured to require authentication to access a page - When the user requests the secure page with no credentials - Then access will be rejected requiring authentication - - Scenario Outline: Authenticate access to a page (basic authentication) - Given a new webserver configured to require authentication to access a page - When the user requests the secure page authenticating with over basic auth - Then access will be - Examples: - | credentials | access | - | valid credentials | granted | - | invalid credentials | rejected requiring authentication | diff --git a/chef/cookbooks/apache2/test/features/authenticate_digest.feature b/chef/cookbooks/apache2/test/features/authenticate_digest.feature deleted file mode 100644 index 1cc65ed..0000000 --- a/chef/cookbooks/apache2/test/features/authenticate_digest.feature +++ /dev/null @@ -1,20 +0,0 @@ -@mod_auth_digest -Feature: Digest Authentication - -In order to perform authorization or vary the provided content -As a developer -I want to authenticate the remote user - - Scenario: Authenticate access to a page - Given a new webserver configured to require authentication to access a page - When the user requests the secure page with no credentials - Then access will be rejected requiring authentication - - Scenario Outline: Authenticate access to a page (digest authentication) - Given a new webserver configured to require authentication to access a page - When the user requests the secure page authenticating with over digest auth - Then access will be - Examples: - | credentials | access | - | valid credentials | granted | - | invalid credentials | rejected requiring authentication | diff --git a/chef/cookbooks/apache2/test/features/authenticate_openid.feature b/chef/cookbooks/apache2/test/features/authenticate_openid.feature deleted file mode 100644 index 597f4cd..0000000 --- a/chef/cookbooks/apache2/test/features/authenticate_openid.feature +++ /dev/null @@ -1,11 +0,0 @@ -@mod_auth_openid -Feature: OpenID Authentication - -In order to perform authorization or vary the provided content -As a developer -I want to authenticate the remote user - - Scenario: Authenticate access to a page - Given a new webserver configured to require authentication to access a page - When the user requests the secure page with no credentials - Then access will be rejected requiring OpenID authentication diff --git a/chef/cookbooks/apache2/test/features/authorize_groupfile.feature b/chef/cookbooks/apache2/test/features/authorize_groupfile.feature deleted file mode 100644 index ef80446..0000000 --- a/chef/cookbooks/apache2/test/features/authorize_groupfile.feature +++ /dev/null @@ -1,16 +0,0 @@ -@mod_authz_groupfile -Feature: Authorize access to content by user - -In order to restrict part of my website -As a developer -I want to restrict access to specific users - - Scenario: Authorize based on group file - Given a new webserver configured to authorize users listed in a group file - When the authenticated user is listed in the file - Then access will be granted - - Scenario: Valid authentication but not a member of the group - Given a new webserver configured to authorize users listed in a group file - When the authenticated user is not listed in the file - Then access will be rejected requiring authentication diff --git a/chef/cookbooks/apache2/test/features/authorize_host.feature b/chef/cookbooks/apache2/test/features/authorize_host.feature deleted file mode 100644 index f2c2742..0000000 --- a/chef/cookbooks/apache2/test/features/authorize_host.feature +++ /dev/null @@ -1,17 +0,0 @@ -Feature: Authorize access to content by host - -In order to restrict part of my website -As a developer -I want to restrict access to known remote hosts - - @mod_authz_listed_host - Scenario: Known remote address - Given a new webserver configured to authorize access based on the remote address - When the remote address is listed as authorized - Then access will be granted - - @mod_authz_unlisted_host - Scenario: Unlisted remote address - Given a new webserver configured to authorize access based on the remote address - When the remote address is not listed as authorized - Then access will be denied diff --git a/chef/cookbooks/apache2/test/features/authorize_ldap.feature b/chef/cookbooks/apache2/test/features/authorize_ldap.feature deleted file mode 100644 index cc1eb55..0000000 --- a/chef/cookbooks/apache2/test/features/authorize_ldap.feature +++ /dev/null @@ -1,16 +0,0 @@ -@mod_authnz_ldap -Feature: Authorize access to content against corporate directory - -In order to restrict part of my website -As a developer -I want to restrict access to people in my corporate directory - - Scenario: Authorized user access - Given a new webserver configured to authorize against a corporate directory - When the authenticated user is listed in the directory as authorized - Then access will be granted - - Scenario: User not in directory - Given a new webserver configured to authorize against a corporate directory - When the authenticated user is not listed in the directory as authorized - Then access will be rejected requiring authentication diff --git a/chef/cookbooks/apache2/test/features/authorize_users.feature b/chef/cookbooks/apache2/test/features/authorize_users.feature deleted file mode 100644 index 1ca1394..0000000 --- a/chef/cookbooks/apache2/test/features/authorize_users.feature +++ /dev/null @@ -1,16 +0,0 @@ -@mod_authz_user -Feature: Authorize access to content by user - -In order to restrict part of my website -As a developer -I want to restrict access to specific users - - Scenario: Authorize named users - Given a new webserver configured to authorize access to specific named users - When the authenticated user is listed as authorized - Then access will be granted - - Scenario: Authorize named users - Given a new webserver configured to authorize access to specific named users - When the authenticated user is not listed as authorized - Then access will be rejected requiring authentication diff --git a/chef/cookbooks/apache2/test/features/basic_web_app.feature b/chef/cookbooks/apache2/test/features/basic_web_app.feature deleted file mode 100644 index 4031c0c..0000000 --- a/chef/cookbooks/apache2/test/features/basic_web_app.feature +++ /dev/null @@ -1,11 +0,0 @@ -@basic_web_app -Feature: Deploy basic webapp - -In order to run my application -As a developer -I want to deploy a basic web application - - Scenario: Deploy basic webapp - Given a new webserver - When I request the root path of the webapp - Then the webapp default page will be returned diff --git a/chef/cookbooks/apache2/test/features/basic_webserver.feature b/chef/cookbooks/apache2/test/features/basic_webserver.feature deleted file mode 100644 index 014ae3b..0000000 --- a/chef/cookbooks/apache2/test/features/basic_webserver.feature +++ /dev/null @@ -1,16 +0,0 @@ -@default -Feature: Serve web pages - -In order to run my application -As a developer -I want to respond to website requests - - Scenario: Request homepage - Given a new webserver - When I request the root url - Then the default page should be returned - - Scenario: Missing page - Given a new webserver - When I request a URL known not to exist - Then page not found should be returned diff --git a/chef/cookbooks/apache2/test/features/compress_server_response.feature b/chef/cookbooks/apache2/test/features/compress_server_response.feature deleted file mode 100644 index 943e982..0000000 --- a/chef/cookbooks/apache2/test/features/compress_server_response.feature +++ /dev/null @@ -1,16 +0,0 @@ -@default @mod_deflate -Feature: Compress server response - -In order to reduce the time taken to retrieve web pages -As a developer -I want to enable compression on server responses - - Scenario: Deflate compression - Given a new webserver with deflate compression enabled - When the browser requests a page specifying that it supports compression - Then the response will be sent compressed - - Scenario: Deflate compression (no client support) - Given a new webserver with deflate compression enabled - When the browser requests a page specifying that it does not support compression - Then the response will be sent uncompressed diff --git a/chef/cookbooks/apache2/test/features/control_caching.feature b/chef/cookbooks/apache2/test/features/control_caching.feature deleted file mode 100644 index d32afe9..0000000 --- a/chef/cookbooks/apache2/test/features/control_caching.feature +++ /dev/null @@ -1,11 +0,0 @@ -@mod_expires -Feature: Control caching - -In order to control caching of responses by intermediate servers -As a developer -I want to control the expiry times on served pages - - Scenario: Set expiry time - Given a new webserver with support for setting expiry times enabled - When I request a path which has a cache directive applied - Then the expiry time returned will match that configured diff --git a/chef/cookbooks/apache2/test/features/directory_listing.feature b/chef/cookbooks/apache2/test/features/directory_listing.feature deleted file mode 100644 index 72aa287..0000000 --- a/chef/cookbooks/apache2/test/features/directory_listing.feature +++ /dev/null @@ -1,19 +0,0 @@ -@default @mod_autoindex -Feature: Directory listing - -In order to allow browsing of the webserver filesystem -As a developer -I want to enable directory listing - - Scenario: View directory listing - Given a new webserver with directory listing enabled - And a path configured to allow directory listing - When I request the directory listing path - Then the directory listing should be returned successfully - - Scenario: Re-order files listed - Given a new webserver with directory listing enabled - And a path configured to allow directory listing with fancy indexing - When I request the directory listing path - Then the directory listing should be returned successfully - And I will be able to sort the files by size diff --git a/chef/cookbooks/apache2/test/features/host_cgi_scripts.feature b/chef/cookbooks/apache2/test/features/host_cgi_scripts.feature deleted file mode 100644 index bc518ee..0000000 --- a/chef/cookbooks/apache2/test/features/host_cgi_scripts.feature +++ /dev/null @@ -1,11 +0,0 @@ -@mod_cgi -Feature: Host CGI scripts - -In order to host dynamic websites -As a developer -I want to be able to host CGI scripts - - Scenario: Host CGI scripts - Given a new webserver with CGI support enabled - When a request is made to a CGI script that generates a list of environment variables - Then the expected environment variables will be present diff --git a/chef/cookbooks/apache2/test/features/host_perl_applications.feature b/chef/cookbooks/apache2/test/features/host_perl_applications.feature deleted file mode 100644 index b9816d4..0000000 --- a/chef/cookbooks/apache2/test/features/host_perl_applications.feature +++ /dev/null @@ -1,11 +0,0 @@ -@mod_perl -Feature: Host Perl applications - -In order to host dynamic websites -As a developer -I want to be able to host Perl applications - - Scenario: Host Perl application - Given a new webserver with Perl support enabled - When a request is made to a Perl script that generates a list of environment variables - Then the expected environment variables will be present diff --git a/chef/cookbooks/apache2/test/features/host_php_applications.feature b/chef/cookbooks/apache2/test/features/host_php_applications.feature deleted file mode 100644 index 618b999..0000000 --- a/chef/cookbooks/apache2/test/features/host_php_applications.feature +++ /dev/null @@ -1,11 +0,0 @@ -@mod_php5 -Feature: Host PHP applications - -In order to host dynamic websites -As a developer -I want to be able to host PHP websites - - Scenario: Host PHP website - Given a new webserver with PHP support enabled - When a request is made to a PHP script that generates a list of environment variables - Then the expected environment variables will be present diff --git a/chef/cookbooks/apache2/test/features/host_python_applications.feature b/chef/cookbooks/apache2/test/features/host_python_applications.feature deleted file mode 100644 index ae6f0d7..0000000 --- a/chef/cookbooks/apache2/test/features/host_python_applications.feature +++ /dev/null @@ -1,11 +0,0 @@ -@mod_python -Feature: Host Python applications - -In order to host dynamic websites -As a developer -I want to be able to host Python applications - - Scenario: Host Python website - Given a new webserver with Python support enabled - When a request is made to a Python script that generates a list of environment variables - Then the expected environment variables will be present diff --git a/chef/cookbooks/apache2/test/features/host_source_control_repositories.feature b/chef/cookbooks/apache2/test/features/host_source_control_repositories.feature deleted file mode 100644 index 21daa4c..0000000 --- a/chef/cookbooks/apache2/test/features/host_source_control_repositories.feature +++ /dev/null @@ -1,12 +0,0 @@ -Feature: Host source control repositories - -In order to provide access to source control -As a developer -I want to host source control repositories - - @mod_dav_svn - Scenario: Commit changes - Given a new webserver with subversion support enabled - And a subversion repository - When a developer commits a change to the repository - Then the change will be visible when browsing the repository diff --git a/chef/cookbooks/apache2/test/features/proxy_java_applications.feature b/chef/cookbooks/apache2/test/features/proxy_java_applications.feature deleted file mode 100644 index 4e75649..0000000 --- a/chef/cookbooks/apache2/test/features/proxy_java_applications.feature +++ /dev/null @@ -1,12 +0,0 @@ -@java -Feature: Proxy Java applications - -In order to host dynamic websites -As a developer -I want be able to proxy requests to a Java application - - @mod_proxy_ajp - Scenario: Proxy Java application server - Given a new webserver with support for proxying to Java application servers enabled - When a request is made to a Java application that generates a list of request parameters - Then the expected request parameters will be present diff --git a/chef/cookbooks/apache2/test/features/secure_requests.feature b/chef/cookbooks/apache2/test/features/secure_requests.feature deleted file mode 100644 index 8346b54..0000000 --- a/chef/cookbooks/apache2/test/features/secure_requests.feature +++ /dev/null @@ -1,11 +0,0 @@ -@mod_ssl -Feature: Secure requests - -In order to prevent a malicious third party from eavesdropping or hijacking a user session -As a developer -I want to secure communication between the client and server - - Scenario: Request homepage - Given a new webserver - When I request the root url over HTTPS - Then the default page should be returned diff --git a/chef/cookbooks/apache2/test/features/step_definitions/svn_steps.rb b/chef/cookbooks/apache2/test/features/step_definitions/svn_steps.rb deleted file mode 100644 index f576c9a..0000000 --- a/chef/cookbooks/apache2/test/features/step_definitions/svn_steps.rb +++ /dev/null @@ -1,19 +0,0 @@ -Given 'a subversion repository' do - -end - -When 'a developer commits a change to the repository' do - svn_repository '/svn/' do - svn_commit_new_file 'README', 'Hello World' - end -end - -Then 'the change will be visible when browsing the repository' do - begin - http_request('/svn/README').must_include 'Hello World' - ensure - svn_repository '/svn/' do - svn_remove_file 'README' - end - end -end diff --git a/chef/cookbooks/apache2/test/features/step_definitions/webserver_steps.rb b/chef/cookbooks/apache2/test/features/step_definitions/webserver_steps.rb deleted file mode 100644 index f743189..0000000 --- a/chef/cookbooks/apache2/test/features/step_definitions/webserver_steps.rb +++ /dev/null @@ -1,156 +0,0 @@ -Given /^a new webserver.*$/ do - -end - -Given /^an alias defined|a path configured to allow directory listing.*$/ do - # /icons/ is defined by default -end - -When /^a request is made to a (CGI|Java|Perl|Python|PHP) (?:script|application) that generates a list of (?:environment variables|request parameters)$/ do |script_type| - http_request case script_type - when 'CGI' then '/cgi-bin/env' - when 'Python' then '/env/python.py' - else "/env/#{script_type.downcase}" - end -end - -When 'I request a path which has a cache directive applied' do - http_request '/cachetest/' -end - -When 'I request a URL known not to exist' do - http_request '/this-path-does-not-exist' -end - -When 'I request as a known browser that only supports HTTP/1.0' do - @response_version = http_response_version('JDK/1.0', '1.0') -end - -When /^I request the (?:alias|directory listing) path$/ do - http_request '/icons/' -end - -When 'I request the root path of the webapp' do - http_request '/basic_web_app/' -end - -When /^I request the root url( over HTTPS)?$/ do |secure| - if secure - https_request '/' - else - http_request '/' - end -end - -When 'I request the status page from a remote host' do - http_request '/server-status/' -end - -When /^the authenticated user is (not )?listed (?:in the directory )(?:in the file|as authorized)$/ do |not_listed| - http_request '/secure/', - :basic_auth => {:username => not_listed ? 'meatballs' : 'bork', - :password => 'secret'} -end - -When 'the browser requests a page specifying that it does not support compression' do - @response_was_compressed = compresses_response?(:client_no_support) -end - -When 'the browser requests a page specifying that it supports compression' do - @response_was_compressed = compresses_response?(:client_supports) -end - -When /^the remote address is (not )?listed as authorized$/ do |not_listed| - http_request '/secure/' -end - -When /^the user requests the secure page authenticating with (in)?valid credentials over (basic|digest) auth$/ do |invalid, auth_type| - http_request '/secure/', "#{auth_type}_auth".to_sym => {:username => 'bork', - :password => invalid ? 'squirrel' : 'secret'} -end - -When 'the user requests the secure page with no credentials' do - http_request '/secure/' -end - -Then /^access will be (denied|rejected requiring (?:OpenID )?authentication|granted)$/ do |access| - http_response.code.must_equal({ - 'denied' => 403, - 'rejected requiring authentication' => 401, - 'rejected requiring OpenID authentication' => 200, - 'granted' => 200 - }[access]) - if access == 'rejected requiring OpenID authentication' - http_response.body.must_include 'This site is protected and requires that you identify yourself with an OpenID url.' - end -end - -Then 'I will be able to sort the files by size' do - http_request '/icons/?C=S;O=A' - # icons differ on different distros - dir_listing_entries[1].must_equal 'small/' -end - -Then 'page not found should be returned' do - http_response.body.must_include 'Not Found' - http_response.code.must_equal 404 -end - -Then 'simple statistics will be shown' do - http_response.body.must_include 'Apache Status' - ['Server uptime', 'requests currently being processed', 'idle workers'].each do |stat| - http_response.body.must_include stat - end -end - -Then 'the aliased resource should be returned successfully' do - http_response.body.must_include 'Index of /icons' - http_response.code.must_equal 200 -end - -Then 'the default page should be returned' do - assert default_page_present?(http_response.body) -end - -Then 'the directory listing should be returned successfully' do - http_response.body.must_include 'Index of /icons' - http_response.body.must_include 'Parent Directory' - dir_listing_entries.must_include 'README' - dir_listing_entries.must_include 'a.png' - http_response.code.must_equal 200 -end - -Then 'the expected environment variables will be present' do - env = environment_variables(http_response.body) - env['GATEWAY_INTERFACE'].must_include 'CGI/1.1' - env['SERVER_SOFTWARE'].must_equal 'Apache' -end - -Then 'the expected request parameters will be present' do - params = request_parameters(http_response.body) - params['Method'].must_equal 'GET' - params['Protocol'].must_equal 'HTTP/1.1' - params['Request URI'].must_equal '/examples/servlets/servlet/RequestInfoExample' -end - -Then 'the expiry time returned will match that configured' do - http_response.code.must_equal 200 - cache_time_seconds(http_response.headers).must_equal 60 - max_age_seconds(http_response.headers).must_equal 60 -end - -Then 'the response should be HTTP/1.0 also' do - @response_version.must_equal '1.0' -end - -Then /^the response will be sent (un)?compressed$/ do |expect_uncompressed| - if expect_uncompressed - refute @response_was_compressed - else - assert @response_was_compressed - end -end - -Then 'the webapp default page will be returned' do - http_response.body.must_include 'Hello World' -end diff --git a/chef/cookbooks/apache2/test/features/support/env.rb b/chef/cookbooks/apache2/test/features/support/env.rb deleted file mode 100644 index 8fb0fc0..0000000 --- a/chef/cookbooks/apache2/test/features/support/env.rb +++ /dev/null @@ -1,3 +0,0 @@ -require 'minitest/spec' -World(MiniTest::Assertions) -MiniTest::Spec.new(nil) diff --git a/chef/cookbooks/apache2/test/features/support/svn_helpers.rb b/chef/cookbooks/apache2/test/features/support/svn_helpers.rb deleted file mode 100644 index d9b3855..0000000 --- a/chef/cookbooks/apache2/test/features/support/svn_helpers.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'tmpdir' - -def run(cmd) - %x{#{cmd}} - assert $?.success? -end - -def svn_commit_new_file(filename, content) - File.open(filename, 'w') {|f| f.write(content) } - run "svn add #{filename} && svn commit -m 'Committed a change.'" -end - -def svn_remove_file(filename) - run "svn rm #{filename} && svn commit -m 'Revert previous commit.'" -end - -def svn_repository(path) - Dir.mktmpdir do |dir| - Dir.chdir dir - run "svn co http://#{test_host}#{path}" - Dir.chdir File.join(dir, path) - yield - end -end diff --git a/chef/cookbooks/apache2/test/features/support/web_helpers.rb b/chef/cookbooks/apache2/test/features/support/web_helpers.rb deleted file mode 100644 index 4d49322..0000000 --- a/chef/cookbooks/apache2/test/features/support/web_helpers.rb +++ /dev/null @@ -1,86 +0,0 @@ -require 'httparty' -require 'nokogiri' - -def test_host - ENV['TEST_HOST'] || 'localhost' -end - -def http_port - ENV['TEST_HTTP_PORT'] || 80 -end - -def https_port - ENV['TEST_HTTPS_PORT'] || 443 -end - -def cache_time_seconds(http_headers) - expiry_time = Time.parse(http_headers['expires']) - server_time = Time.parse(http_headers['date']) - expiry_time - server_time -end - -def compresses_response?(request_type) - # httparty rewrites the response to hide compression from us - encoding = %x{curl -s -i #{'--compressed ' if request_type == :client_supports} 'http://#{test_host}/' | grep 'Content-Encoding' | awk -F' ' '{print $2}'}.strip - %w{deflate gzip}.include?(encoding) -end - -def default_page_present?(body) - ['This is the default web page for this server.', - 'Apache HTTP Server Test Page'].any?{|msg| body.include? msg} -end - -# Filenames in a directory listing response -def dir_listing_entries - Nokogiri::HTML(http_response.body).xpath("//td/a/text()").map{|a| a.to_s} -end - -def environment_variables(response_body) - Hash[response_body.split("\n").map{|v| v.split('=')}] -end - -def http_request(path, options={}) - if options.key?(:digest_auth) - # HTTParty digest doesn't appear to work - @response = http_request_digest_curl(path, options) - else - @response = HTTParty.get("http://#{test_host}:#{http_port}#{path}", options) - end - @response -end - -def http_request_digest_curl(path, options) - credentials = "#{options[:digest_auth][:username]}:#{options[:digest_auth][:password]}" - curl_response = %x{curl -s -i --digest -u #{credentials} http://#{test_host}:#{http_port}#{path}} - assert $?.success? - @response = Class.new do - def initialize(response) - @curl_response = response - end - def code - @curl_response.scan(%r{HTTP/1.1 ([0-9]+)}).flatten.last.to_i - end - end.new(curl_response) -end - -def https_request(path) - @response = HTTParty.get("https://#{test_host}:#{https_port}#{path}") -end - -def http_response - @response -end - -def http_response_version(user_agent, protocol_version) - response_line = %x{curl -s #{'-0 ' if protocol_version == '1.0'} -i -A '#{user_agent}' 'http://#{test_host}/' | head -n1} - assert $?.success? - response_line.scan(/HTTP\/([0-9]+\.[0-9]+) [0-9]+.*/).flatten.first -end - -def max_age_seconds(http_headers) - http_headers['cache-control'].scan(/^max-age=([0-9]+)$/).flatten.first.to_i -end - -def request_parameters(response_body) - Hash[*Nokogiri::HTML(response_body).xpath("//td/text()").map{|h| h.to_s.strip.sub(/:$/, '')}] -end diff --git a/chef/cookbooks/apache2/test/features/support_older_browsers.feature b/chef/cookbooks/apache2/test/features/support_older_browsers.feature deleted file mode 100644 index 5158f66..0000000 --- a/chef/cookbooks/apache2/test/features/support_older_browsers.feature +++ /dev/null @@ -1,11 +0,0 @@ -@default @mod_setenvif -Feature: Support older browsers - - In order to be a good netizen - As a developer - I want to ensure that my server will respond to requests from older browsers - - Scenario: Support HTTP/1.0 - Given a new webserver - When I request as a known browser that only supports HTTP/1.0 - Then the response should be HTTP/1.0 also diff --git a/chef/cookbooks/apache2/test/kitchen/Kitchenfile b/chef/cookbooks/apache2/test/kitchen/Kitchenfile deleted file mode 100644 index a0ed0ff..0000000 --- a/chef/cookbooks/apache2/test/kitchen/Kitchenfile +++ /dev/null @@ -1,50 +0,0 @@ -# -# Author:: Andrew Crump -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# -# 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. -# - -cookbook "apache2" do - configuration "default" - # basic_web_app tests are in apache2_test cookbook - configuration "basic_web_app" - # module_recipes tests are in apache2_test cookbook - configuration "modules" - configuration "mod_auth_basic" - configuration "mod_auth_digest" - configuration "mod_auth_openid" - configuration "mod_auth_cas" - configuration "mod_authnz_ldap" - configuration "mod_authz_groupfile" - configuration "mod_authz_listed_host" - configuration "mod_authz_unlisted_host" - configuration "mod_authz_user" - configuration "mod_cgi" - configuration "mod_dav_svn" - configuration "mod_expires" - configuration "mod_fastcgi" - configuration "mod_include" - configuration "mod_perl" - configuration "mod_apreq2" - configuration "mod_php5" - configuration "mod_proxy_ajp" - configuration "mod_python" - configuration "mod_ssl" - configuration "mod_status_remote" - # placeholder until COOK-744 is fixed - #configuration "god_monitor" - exclude :platform => 'centos', :configuration => 'mod_authnz_ldap' - exclude :platform => 'centos', :configuration => 'mod_auth_cas' - run_list_extras ['apache2_test::setup'] -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/Cheffile b/chef/cookbooks/apache2/test/kitchen/cookbooks/Cheffile deleted file mode 100644 index ff6ca50..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/Cheffile +++ /dev/null @@ -1,4 +0,0 @@ -cookbook 'openldap', - :git => 'https://github.com/opscode-cookbooks/openldap',:ref => 'foodcritic' - -cookbook 'god' diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/README.md b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/README.md deleted file mode 100644 index f6f3a39..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/README.md +++ /dev/null @@ -1,82 +0,0 @@ -Description -=========== - -This cookbook defines acceptance tests for Apache2. It includes: - -* A `features` sub-directory where the Cucumber features for the webserver - are defined. -* Recipes that configure individual modules for use in order to be tested. - -Requirements -============ - -## Cookbooks: - -This cookbook depends on the `apache2` cookbook. It also relies on the `yum` -cookbook in order to add the EPEL repository on RHEL-derived distributions. - -## Platforms: - -* Ubuntu -* CentOS - -Attributes -========== - -* `node['apache_test']['auth_username']` - The username of the user for testing - authentication and authorization. -* `node['apache_test']['auth_password']` - The password of the user for testing - authentication and authorization. -* `node['apache_test']['cache_expiry_seconds']` - The cache expiry time in - seconds. -* `node['apache_test']['app_dir']` - The local directory where test applications - will be deployed. -* `node['apache_test']['cgi_dir']` - The local directory where CGI applications - will be deployed. -* `node['apache_test']['root_dir']` - The root directory of the webserver. -* `node['apache_test']['remote_host_ip']` - The remote host IP address for - authorization. -* `node['apache_test']['ssl_dir']` - The local directory containing the generated SSL key and certificate. -* `node['apache_test']['ssl_cert_file']` - The SSL certificate file. -* `node['apache_test']['ssl_cert_key_file']` - The private key. - -Recipes -======= - -* `default` - Simply includes apache2::default for a vanilla apache install. -* `mod_auth_basic` - Adds a web_app behind basic authentication for testing. -* `mod_auth_digest` - Adds a web_app behind digest authenticaiton for testing. -* `mod_auth_openid` - Adds a web_app behind openid authentication for testing. -* `mod_authnz_ldap` - Adds a web_app behind ldap-based authorization for testing. -* `mod_authz_groupfile` - Adds a web_app behind groupfile-based authorization for testing. -* `mod_authz_listed_host` - Adds a web_app behind host-based authorization for testing. -* `mod_authz_unlisted_host` - Adds a web_app behind host-based authorization for testing. -* `mod_authz_user` - Adds a web_app behind username-based authorization for testing. -* `mod_cgi` - Adds a CGI script (bash) that prints environment variables for testing. -* `mod_dav_svn` - Adds a web_app with an empty Subversion repository for testing. -* `mod_expires` - Adds a web_app that sets caching expiry headers for testing. -* `mod_perl` - Adds a Perl script running under mod_perl that prints environment variables for testing. -* `mod_php5` - Adds a PHP script running under mod_php5 that prints environment variables for testing. -* `mod_proxy_ajp` - Installs Tomcat with examples and configures proxying over AJP. -* `mod_python` - Adds a Python script running under mod_python that prints environment variables for testing. -* `mod_ssl` - Adds a self-signed SSL certificate and default website for testing. -* `mod_status_remote` - Enables remote access to stats for testing. - -License and Authors -=================== - -Author:: Andrew Crump - - Copyright:: 2012, Opscode, Inc - - 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. diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/attributes/default.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/attributes/default.rb deleted file mode 100644 index 323b63e..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/attributes/default.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Attributes:: default -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -default['apache_test']['auth_username'] = 'bork' -default['apache_test']['auth_password'] = 'secret' -default['apache_test']['cache_expiry_seconds'] = 60 -default['apache_test']['app_dir'] = '/home/apache2/env' -default['apache_test']['cgi_dir'] = '/usr/lib/cgi-bin' -default['apache_test']['root_dir'] = '/var/www' -default['apache_test']['remote_host_ip'] = '127.0.0.1' -default['apache_test']['ssl_dir'] = '/home/apache2' -default['apache_test']['ssl_cert_file'] = "#{node['apache_test']['ssl_dir']}/server.crt" -default['apache_test']['ssl_cert_key_file'] = "#{node['apache_test']['ssl_dir']}/server.key" -default['apache_test']['svn_dir'] = '/home/apache2/svn' -default['domain'] = 'example.com' -default['openldap']['rootpw'] = '{SSHA}6BjlvtSbVCL88li8IorkqMSofkLio58/' -default['openldap']['rootpw_plain'] = 'secretsauce' -default['openldap']['slapd_rid'] = '000' -default['openldap']['auth_bindpw'] = 'yoltUnVik3' diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/ssl/ldap.example.com.pem b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/ssl/ldap.example.com.pem deleted file mode 100644 index 7da82a1..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/ssl/ldap.example.com.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDrjCCApYCCQCMPzF4wnKEjjANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMC -VVMxEDAOBgNVBAgTB1NldmVyYWwxETAPBgNVBAcTCExvY2FsaXR5MRQwEgYDVQQK -EwtFeGFtcGxlIENvbTETMBEGA1UECxMKT3BlcmF0aW9uczEZMBcGA1UEAxMQbGRh -cC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPb3BzQGV4YW1wbGUuY29tMB4X -DTEyMTAwODIwNTgxOFoXDTIyMTAwNjIwNTgxOFowgZgxCzAJBgNVBAYTAlVTMRAw -DgYDVQQIEwdTZXZlcmFsMREwDwYDVQQHEwhMb2NhbGl0eTEUMBIGA1UEChMLRXhh -bXBsZSBDb20xEzARBgNVBAsTCk9wZXJhdGlvbnMxGTAXBgNVBAMTEGxkYXAuZXhh -bXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD29wc0BleGFtcGxlLmNvbTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMduncR64JPmOzDvtSU8U88+JX8StlZG -Ovd06tZ4x58jmIaqFXDLKmwU/+9f9mwSeT7jokX9Y/cLWP0HcAGs1KmGnodryYCW -DQ6+BBeRbBOw1RoaqgP4r3/ooXiTTUuVXxYcyKkx3Pn300iDHNo53/e9hxZB9DDv -KfFHL5A9kHA/bUpI8FBxa/SEGiV5gIkr15b3Jk7Zzydpqlz/6dlqaQxQBGqcm6yY -Ey/OqOuP+i7fKQfb9taPEAbnZNKbV4KajLlt/bl+60VHsdSBKZPROMt1bJZpdzcF -1SlIjFvFPp/G39Vkc8hvBXsUO8qivSiNirOhFMqxkS/f90N/8nD9Mu0CAwEAATAN -BgkqhkiG9w0BAQUFAAOCAQEArwmwsnhgAug1/ZuJAiT9VOR2yWhhU98IozoYLcE7 -45aRsv0G9qxd/zt4uN+xkgUP1xilDkuzVDuU2jdKgKTJAaDNy4mm7xtMoLzPNtEq -W+12EqxKzKKyGJz1B9iH7UsnM1ZCm6rTWe+Ij/hPU+A/qqOsOvLyi+Z93xauLW6E -tcLVlrsuoJ8k/P+u2s5Isxz+NOSeryg5WcNVGzkCjyXkxgIF05zl0bqN9RL0WukU -hWmJ3pAE15bz88tI2e1Z+5RPPo3cxD4Bw8+jH0HPKW8Cz51OUyr6stva3mgS0UXF -KhyTpqhhau4GE9cKpK+2n1iF6exdq9TAkdAhViW3uj7mJg== ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAx26dxHrgk+Y7MO+1JTxTzz4lfxK2VkY693Tq1njHnyOYhqoV -cMsqbBT/71/2bBJ5PuOiRf1j9wtY/QdwAazUqYaeh2vJgJYNDr4EF5FsE7DVGhqq -A/ivf+iheJNNS5VfFhzIqTHc+ffTSIMc2jnf972HFkH0MO8p8UcvkD2QcD9tSkjw -UHFr9IQaJXmAiSvXlvcmTtnPJ2mqXP/p2WppDFAEapybrJgTL86o64/6Lt8pB9v2 -1o8QBudk0ptXgpqMuW39uX7rRUex1IEpk9E4y3Vslml3NwXVKUiMW8U+n8bf1WRz -yG8FexQ7yqK9KI2Ks6EUyrGRL9/3Q3/ycP0y7QIDAQABAoIBAQCdbQci8vBWL3Zo -AcCXfRCXVnJY5deDMPsZHXTHCh5h08JyAs7b87QwDz+coL6vvsFw0FXnM8d2WsHL -MtTKrbhNfdOkCITOeSQjkAFA7W1H+d2uNoAglG1M5cCYooZwdJ/Tn7MSRCcwNt3U -rpLW/Lp8IGMDQdrVyeTs7glwrfhXcYRpUFtT5AoweXFYIgGmZXD0LxmSdiY37V2X -zQ8vIjDn0wcNJYeqmOswP7iPS21HhAZGC4vfIji9DquQ4q6Pf8wRkW3KF7/BrmUl -9UXKb//Ja4xV50MSQoDQxmtjyrjnEfqYse2zLAn0QjVgqKgAzkP8+WpVrAmol6rG -KLd+VRtNAoGBAPIuvUIvRrjFVBWaAzv5pkE/DfpDA+6QSifSAJbqQOwSw3qAOBwq -RIQ//iBcHmxYyX0jhMVpP9wKZnVjDSr/9iLd4O7gvimxnYZp9vuvf2KZNbHaeky5 -niYIF65ObBRtAcAPETCEPhkYT0hFdyxJSRSBK73cQ9kzZAyROZYPvEivAoGBANLP -eUTGa25BG5xwzYF4NECSum9QK6QelH1TkO7nkv1c+Wp6HQ7G6hor5khxVkj5MpYk -nMdyZV4zU25OptM0QGMPwUwhj/MytpdUvBI2I5PAOq7lJbBCewW8lkagn+Pd1HD7 -IMBpvxe+M6LFftDiKnko+Z2zABkkRzVqGAPbL60jAoGAIKMr3j+AyGXPbxnSVcLP -JPvaZl+hqATJ+ZPTgIMRPL9KmLeu2BzaHviAxtujPfa5MKQYwIHumTjNlgRDQgg8 -o3ZDWe3vsq69C+A76K89+4uqMM3ArZZWOcndZyAqcJZAJiHhrygjNj6QcKzr4ov9 -zUWbH3sPqbXDRe5MVGzKcwECgYBxhpN66xPdqyhQZhr1lyMkhx/pZBYsat9yYndR -gNoSoWgb5CkT1SEq+OsppgdegvywCIV5juxx/1f1tlt2r9PgxRJGimh4Ap57/oDN -meQs5D92Aib6tcKEg1u2KzgPwV1vfn3TwN7MzXwHMy4pFTLkTqGmQEhUQcorRLgs -E3SoWwKBgEFEHZkpxcrjhs+S3Vr56ZSyYaMsXTbBx5Yz2e0/+ZASyXT9joFQ3au4 -IZoJCAhdSnRpj57FOuBHLwAJDSrUw/QxwD5VGI1i1MzzyejDW0590BYtNysXXdFF -r/Fp9Hh7Ms+cjzR7Fv8ccLQZPuie/nuNWT78gm8TkjNfwiMGgfrf ------END RSA PRIVATE KEY----- diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/modules_test.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/modules_test.rb deleted file mode 100644 index 5d14e78..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/modules_test.rb +++ /dev/null @@ -1,34 +0,0 @@ -require File.expand_path('../support/helpers', __FILE__) - -# Test all the modules that are not specifically tested through a -# Kitchenfile configuration -# -# Does not test the modules in the default_modules attribute (those -# are tested in default_test) -%w{ - auth_digest - authnz_ldap - dav_fs - deflate - expires - fcgid - headers - ldap - proxy - proxy_balancer - proxy_connect - proxy_http - rewrite - wsgi - xsendfile -}.each do |expected_module| - - describe "apache2::mod_#{expected_module}" do - include Helpers::Apache - - it "installs mod_#{expected_module}" do - apache_enabled_modules.must_include "#{expected_module}_module" - end - - end -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/support/helpers.rb deleted file mode 100644 index 5054866..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/support/helpers.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Helpers - module Apache - require 'chef/mixin/shell_out' - include Chef::Mixin::ShellOut - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - def apache_config_parses? - acp = shell_out("#{node['apache']['binary']} -t") - acp.exitstatus == 0 - end - - def apache_configured_ports - port_config = File.read("#{node['apache']['dir']}/ports.conf") - port_config.scan(/^Listen ([0-9]+)/).flatten.map{|p| p.to_i} - end - - def apache_enabled_modules - apache_modules = shell_out("#{node['apache']['binary']} -M") - apache_modules.send( - if node['platform_family'] == 'rhel' && node['platform_version'].to_f < 6.0 - :stderr - else - :stdout - end - ).split.select! {|i| i =~ /_module$/} - end - - def apache_service - service(case node['platform'] - when "debian","ubuntu" then "apache2" - when "freebsd" then "apache22" - else "httpd" - end) - end - - def config - file(case node['platform'] - when "debian","ubuntu" then "#{node['apache']['dir']}/apache2.conf" - when "freebsd" then "#{node['apache']['dir']}/httpd.conf" - else "#{node['apache']['dir']}/conf/httpd.conf" - end) - end - - def ran_recipe?(recipe) - node.run_state[:seen_recipes].keys.include?(recipe) - end - end -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/metadata.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/metadata.rb deleted file mode 100644 index db67142..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/metadata.rb +++ /dev/null @@ -1,88 +0,0 @@ -maintainer "Andrew Crump" -maintainer_email "andrew@kotirisoftware.com" -license "Apache 2.0" -description "Acceptance tests for apache2" -long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "0.1.0" - -depends "apache2" -depends "jpackage" -depends "openldap" -depends "tomcat" -depends "yum" - -recipe "apache2_test::default", "Test example for default recipe" -recipe "apache2_test::mod_auth_basic", "Test example for basic authentication" -recipe "apache2_test::mod_auth_digest", "Test example for digest authentication" -recipe "apache2_test::mod_auth_openid", "Test example for openid authentication" -recipe "apache2_test::mod_authnz_ldap", "Test example for LDAP authentication" -recipe "apache2_test::mod_authz_groupfile", "Test example for group file authorization" -recipe "apache2_test::mod_authz_listed_host", "Test example for host-based authorization" -recipe "apache2_test::mod_authz_unlisted_host", "Test example for hosted-based authorization" -recipe "apache2_test::mod_authz_user", "Test example for named user authorization" -recipe "apache2_test::mod_cgi", "Test example for hosting a CGI script" -recipe "apache2_test::mod_expires", "Test example for setting cache expiry headers" -recipe "apache2_test::mod_dav_svn", "Test example for Subversion repository hosting" -recipe "apache2_test::mod_perl", "Test example for hosting a Perl application" -recipe "apache2_test::mod_proxy_ajp", "Test example for proxying requests to a Java application" -recipe "apache2_test::mod_php5", "Test example for hosting a PHP application" -recipe "apache2_test::mod_python", "Test example for hosting a Python application" -recipe "apache2_test::mod_ssl", "Test example for SSL" -recipe "apache2_test::mod_status_remote", "Test example for viewing server status" - -%w{centos ubuntu}.each do |os| - supports os -end - -attribute "apache_test/auth_username", - :display_name => "Test Username", - :description => "Username for the test user", - :default => "bork" - -attribute "apache_test/auth_password", - :display_name => "Test Password", - :description => "Password for the test user", - :default => "secret" - -attribute "apache_test/cache_expiry_seconds", - :display_name => "Cache Expiry (Seconds)", - :description => "The expiry time to set in caching response headers", - :default => "60" - -attribute "apache_test/app_dir", - :display_name => "Application Directory", - :description => "Parent directory to deploy test applications under", - :default => "/home/apache2/env" - -attribute "apache_test/cgi_dir", - :display_name => "CGI Directory", - :description => "Directory to install CGI scripts into", - :default => "/usr/lib/cgi-bin" - -attribute "apache_test/root_dir", - :display_name => "Root Directory", - :description => "Webserver document root directory", - :default => "/var/www" - -attribute "apache_test/remote_host_ip", - :display_name => "Remote Host IP", - :description => "IP Address to allow requests from", - :default => "192.168" - -attribute "apache_test/ssl_dir", - :display_name => "SSL Directory", - :description => "Directory for SSL certificates", - :default => "/home/apache2" - -attribute "apache_test/ssl_cert_file", - :display_name => "SSL Certificate Path", - :description => "File path for the generated self-signed certificate" - -attribute "apache_test/ssl_cert_key_file", - :display_name => "SSL Certificate Private Key", - :description => "File path for the generated private key" - -attribute "apache_test/svn_dir", - :display_name => "Subversion Directory", - :description => "File path for test Subversion repository", - :default => "/home/apache2/svn" diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/basic_web_app.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/basic_web_app.rb deleted file mode 100644 index 468b8d8..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/basic_web_app.rb +++ /dev/null @@ -1,38 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: basic_web_app -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" - -app_dir = "#{node['apache_test']['root_dir']}/basic_web_app" - -directory app_dir do - action :create -end - -file "#{app_dir}/index.html" do - content "Hello World" - action :create -end - -web_app "basic_webapp" do - cookbook "apache2" - server_name node['hostname'] - server_aliases [node['fqdn']] - docroot app_dir -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/god_monitor.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/god_monitor.rb deleted file mode 100644 index 1488168..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/god_monitor.rb +++ /dev/null @@ -1,19 +0,0 @@ -# -# Author:: Joshua Timberman -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# License:: Apache License, Version 2.0 -# -# 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. -# - -include_recipe "apache2::god_monitor" diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_basic.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_basic.rb deleted file mode 100644 index 93022a7..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_basic.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_auth_basic -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" -include_recipe "apache2::mod_auth_basic" - -directory "#{node['apache_test']['root_dir']}/secure" do - action :create -end - -execute "add-credentials" do - command "htpasswd -b -c #{node['apache_test']['root_dir']}/secure/.htpasswd #{node['apache_test']['auth_username']} #{node['apache_test']['auth_password']}" - action :run -end - -web_app "secure" do - template "auth_basic.conf.erb" - auth_user_file "#{node['apache_test']['root_dir']}/secure/.htpasswd" -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_digest.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_digest.rb deleted file mode 100644 index cf13ee5..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_digest.rb +++ /dev/null @@ -1,37 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_auth_digest -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" -include_recipe "apache2::mod_auth_digest" - -directory "#{node['apache_test']['root_dir']}/secure" do - action :create -end - -# htdigest won't read the password from STDIN -bash "add_credentials" do - code %Q{ - (echo -n "#{node['apache_test']['auth_username']}:private area:" && echo -n "#{node['apache_test']['auth_username']}:private area:#{node['apache_test']['auth_password']}" | md5sum | awk '{print $1}') > /#{node['apache_test']['root_dir']}/secure/.htdigest - } -end - -web_app "secure" do - template "auth_digest.conf.erb" - auth_user_file "#{node['apache_test']['root_dir']}/secure/.htdigest" -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authnz_ldap.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authnz_ldap.rb deleted file mode 100644 index e4359be..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authnz_ldap.rb +++ /dev/null @@ -1,63 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_authnz_ldap -# -# Copyright 2012, Opscode, Inc. -# -# 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. - -directory "/var/cache/local/preseeding" do - recursive true - action :create - only_if { platform?('debian', 'ubuntu') } -end - -include_recipe "openldap::server" - -service "slapd" do - action :start -end - -cbf = resources("cookbook_file[#{node['openldap']['ssl_dir']}/#{node['openldap']['server']}.pem]") -cbf.cookbook "apache2_test" - -ldif_path = "/tmp/entries.ldif" - -template ldif_path do - source "entries.ldif.erb" - action :create -end - -bash "load-directory-entries" do - code %Q{ - ldapsearch -x -D 'cn=admin,#{node['openldap']['basedn']}' -w '#{node['openldap']['rootpw_plain']}' -b '#{node['openldap']['basedn']}' - if [ $? -ne 0 ] - then - ldapadd -x -D 'cn=admin,#{node['openldap']['basedn']}' -w '#{node['openldap']['rootpw_plain']}' -f #{ldif_path} - fi - } - action :run -end - -include_recipe "apache2::default" -include_recipe "apache2::mod_ldap" -include_recipe "apache2::mod_authnz_ldap" - -directory "#{node['apache_test']['root_dir']}/secure" do - action :create -end - -web_app "secure" do - template "authnz_ldap.conf.erb" - base_dn node['openldap']['basedn'] -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_groupfile.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_groupfile.rb deleted file mode 100644 index a18bc32..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_groupfile.rb +++ /dev/null @@ -1,46 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_authz_groupfile -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" - -secure_dir = "#{node['apache_test']['root_dir']}/secure" -group_name = 'swedishchef' - -directory secure_dir do - action :create -end - -bash "add-credentials" do - code %Q{ - htpasswd -b -c #{secure_dir}/.htpasswd #{node['apache_test']['auth_username']} #{node['apache_test']['auth_password']} - htpasswd -b #{secure_dir}/.htpasswd meatballs secret - } - action :run -end - -file "#{secure_dir}/.htgroups" do - content "#{group_name}:#{node['apache_test']['auth_username']}" -end - -include_recipe "apache2::mod_authz_groupfile" -web_app "secure" do - template "authz_groupfile.conf.erb" - secure_dir secure_dir - group_name group_name -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_unlisted_host.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_unlisted_host.rb deleted file mode 100644 index 707a04d..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_unlisted_host.rb +++ /dev/null @@ -1,30 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_authz_unlisted_host -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" -include_recipe "apache2::mod_authz_host" - -directory "#{node['apache_test']['root_dir']}/secure" do - action :create -end - -web_app "secure" do - template "authz_host.conf.erb" - remote_host_ip '8.8.8.8' -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_user.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_user.rb deleted file mode 100644 index aa9204f..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_user.rb +++ /dev/null @@ -1,41 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_authz_user -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# -include_recipe "apache2::default" - -secure_dir = "#{node['apache_test']['root_dir']}/secure" - -directory secure_dir do - action :create -end - -bash "add-credentials" do - code %Q{ - htpasswd -b -c #{secure_dir}/.htpasswd #{node['apache_test']['auth_username']} #{node['apache_test']['auth_password']} - htpasswd -b #{secure_dir}/.htpasswd meatballs secret - } - action :run -end - -include_recipe "apache2::mod_authz_user" - -web_app "secure" do - template "authz_user.conf.erb" - secure_dir secure_dir - username node['apache_test']['auth_username'] -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_dav_svn.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_dav_svn.rb deleted file mode 100644 index ae365cd..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_dav_svn.rb +++ /dev/null @@ -1,45 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_dav_svn -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" - -package "subversion" do - action :install -end - -include_recipe "apache2::mod_dav" -include_recipe "apache2::mod_dav_svn" - -directory node['apache_test']['svn_dir'] do - owner node['apache']['user'] - group node['apache']['group'] - recursive true - action :create -end - -execute "create-repo" do - user node['apache']['user'] - command "svnadmin create --config-dir #{Chef::Config[:file_cache_path]} #{node['apache_test']['svn_dir']}" - not_if "bash -c 'svnadmin verify #{node['apache_test']['svn_dir']}'" -end - -web_app "svn" do - template "svn_repo.conf.erb" - repo_dir node['apache_test']['svn_dir'] -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_expires.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_expires.rb deleted file mode 100644 index bc6bfd7..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_expires.rb +++ /dev/null @@ -1,30 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_expires -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" -include_recipe "apache2::mod_expires" - -directory "#{node['apache_test']['root_dir']}/cachetest" do - action :create -end - -web_app "cachetest" do - template "cache_test.conf.erb" - cache_expiry_seconds node['apache_test']['cache_expiry_seconds'] -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_perl.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_perl.rb deleted file mode 100644 index 56aaec7..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_perl.rb +++ /dev/null @@ -1,66 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_perl -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" - -include_recipe "yum::epel" if platform?("centos") - -include_recipe "apache2::mod_perl" - -package "perl-CGI-SpeedyCGI" do - action :install - only_if { platform?("redhat", "centos", "scientific", "fedora", "amazon") } -end - -file "#{node['apache']['dir']}/conf.d/apreq.conf" do - action :delete - only_if { platform?("redhat", "centos", "scientific", "fedora", "amazon") } -end - -file "#{node['apache']['dir']}/conf.d/perl.conf" do - action :delete - only_if { platform?("redhat", "centos", "scientific", "fedora", "amazon") } -end - -directory node['apache_test']['app_dir'] do - recursive true - action :create -end - -file "#{node['apache_test']['app_dir']}/perl" do - content %q{ -#!/usr/bin/perl -wT -use strict; -use CGI qw(:standard); -use CGI::Carp qw(warningsToBrowser fatalsToBrowser); - -print header('text/plain'); - -foreach my $key (sort(keys(%ENV))) { - print "$key=$ENV{$key}\n"; -} -}.strip - mode "0755" - action :create -end - -web_app "perl_env" do - template "perl_env.conf.erb" - app_dir node['apache_test']['app_dir'] -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_php5.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_php5.rb deleted file mode 100644 index 3378123..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_php5.rb +++ /dev/null @@ -1,50 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_php5 -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" - -package "which" do - action :install - only_if { platform_family?("rhel", "fedora") } -end - -include_recipe "apache2::mod_php5" - -directory node['apache_test']['app_dir'] do - recursive true - action :create -end - -file "#{node['apache_test']['app_dir']}/php" do - content %q{ - $key_value) { - print $key_name . "=" . $key_value . "\n"; -} -?> -}.strip - mode "0755" - action :create -end - -web_app "php_env" do - template "php_env.conf.erb" - app_dir node['apache_test']['app_dir'] -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_python.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_python.rb deleted file mode 100644 index ea22473..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_python.rb +++ /dev/null @@ -1,54 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_python -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" - -yum_repository "epel" do - url 'http://dl.fedoraproject.org/pub/epel/$releasever/$basearch/' - only_if { platform_family?("rhel", "fedora") } -end - -include_recipe "apache2::mod_python" - -directory node['apache_test']['app_dir'] do - recursive true - action :create -end - -file "#{node['apache_test']['app_dir']}/python.py" do - content %q{ -#!/usr/bin/python -import sys -sys.stderr = sys.stdout -import os -from cgi import escape - -print "Content-type: text/plain" -print -for k in sorted(os.environ): - print "%s=%s" %(escape(k), escape(os.environ[k])) -}.strip - mode "0755" - action :create -end - -web_app "python_env" do - template "python_env.conf.erb" - app_dir node['apache_test']['app_dir'] -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_ssl.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_ssl.rb deleted file mode 100644 index b5b0e84..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_ssl.rb +++ /dev/null @@ -1,54 +0,0 @@ -# -# Cookbook Name:: apache2_test -# Recipe:: mod_ssl -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" -include_recipe "apache2::mod_ssl" - -directory node['apache_test']['ssl_dir'] do - owner node['apache']['user'] - group node['apache']['group'] - recursive true - action :create -end - -execute "create-private-key" do - command "openssl genrsa > #{node['apache_test']['ssl_cert_key_file']}" - not_if "test -f #{node['apache_test']['ssl_cert_key_file']}" -end - -execute "create-certficate" do - command %Q{openssl req -new -x509 -key #{node['apache_test']['ssl_cert_key_file']} -out #{node['apache_test']['ssl_cert_file']} -days 1 < node['apache_test']['remote_host_ip']}) -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/modules.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/modules.rb deleted file mode 100644 index 74735e7..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/modules.rb +++ /dev/null @@ -1,43 +0,0 @@ -# -# Cookbook:: apache2_test -# Recipe:: modules -# -# Author:: Joshua Timberman -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apache2::default" - -# Duplicates the list in the modules_test minitest, which is -# distasteful duplication. -%w{ - auth_digest - authnz_ldap - dav_fs - deflate - expires - fcgid - headers - ldap - proxy - proxy_balancer - proxy_connect - proxy_http - rewrite - wsgi - xsendfile -}.each do |a2mod| - include_recipe "apache2::mod_#{a2mod}" -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/setup.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/setup.rb deleted file mode 100644 index 99c3ce7..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/setup.rb +++ /dev/null @@ -1,18 +0,0 @@ -case node['platform_family'] - when 'debian' - %w{libxml2 libxml2-dev libxslt1-dev}.each do |pkg| - package pkg do - action :install - end - end - when 'rhel' - %w{gcc make ruby-devel libxml2 libxml2-devel libxslt libxslt-devel}.each do |pkg| - package pkg do - action :install - end - end -end - -package "curl" do - action :install -end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_basic.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_basic.conf.erb deleted file mode 100644 index b28fecd..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_basic.conf.erb +++ /dev/null @@ -1,6 +0,0 @@ - - AuthUserFile "<%= @params[:auth_user_file] %>" - AuthType basic - AuthName "private area" - Require valid-user - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_digest.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_digest.conf.erb deleted file mode 100644 index c0014ef..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_digest.conf.erb +++ /dev/null @@ -1,7 +0,0 @@ - - AuthUserFile "<%= @params[:auth_user_file] %>" - AuthType digest - AuthDigestDomain /secure/ - AuthName "private area" - Require valid-user - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_openid.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_openid.conf.erb deleted file mode 100644 index 5e55f84..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_openid.conf.erb +++ /dev/null @@ -1,8 +0,0 @@ - - - AuthType OpenID - AuthOpenIDTrustRoot http://opscode.example.com - AuthName "private area" - Require valid-user - - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authnz_ldap.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authnz_ldap.conf.erb deleted file mode 100644 index 958aecc..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authnz_ldap.conf.erb +++ /dev/null @@ -1,9 +0,0 @@ - - - AuthType basic - AuthBasicProvider "ldap" - AuthLDAPUrl "ldap://localhost:389/<%= @params[:base_dn] %>?uid?sub?objectClass=inetOrgPerson" - AuthName "private area" - Require valid-user - - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_groupfile.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_groupfile.conf.erb deleted file mode 100644 index b57087f..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_groupfile.conf.erb +++ /dev/null @@ -1,7 +0,0 @@ - - AuthUserFile "<%= @params[:secure_dir] %>/.htpasswd" - AuthGroupFile "<%= @params[:secure_dir] %>/.htgroups" - AuthType basic - AuthName "private area" - Require group <%= @params[:group_name] %> - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_host.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_host.conf.erb deleted file mode 100644 index 6ef29d4..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_host.conf.erb +++ /dev/null @@ -1,4 +0,0 @@ - - Deny from all - Allow from <%= @params[:remote_host_ip] %> - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_user.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_user.conf.erb deleted file mode 100644 index 80b632c..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_user.conf.erb +++ /dev/null @@ -1,6 +0,0 @@ - - AuthUserFile "<%= @params[:secure_dir] %>/.htpasswd" - AuthType basic - AuthName "private area" - Require user <%= @params[:username] %> - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/cache_test.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/cache_test.conf.erb deleted file mode 100644 index 2c12972..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/cache_test.conf.erb +++ /dev/null @@ -1,4 +0,0 @@ - - ExpiresActive On - ExpiresDefault A<%= @params[:cache_expiry_seconds] %> - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/entries.ldif.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/entries.ldif.erb deleted file mode 100644 index 24d9c3b..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/entries.ldif.erb +++ /dev/null @@ -1,18 +0,0 @@ -dn: dc=example,dc=com -dc: example -objectClass: dcObject -objectClass: organization -o: Example, Inc. - -dn: ou=people,dc=example,dc=com -ou: people -objectclass: organizationalunit - -dn: cn=bork,ou=people,dc=example,dc=com -objectclass: inetOrgPerson -cn: bork -sn: bork -uid: bork -userpassword: secret -mail: bork@example.com -ou: Catering diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/java_env.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/java_env.conf.erb deleted file mode 100644 index 428ffc6..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/java_env.conf.erb +++ /dev/null @@ -1,6 +0,0 @@ - - ProxyPass /env/java ajp://<%= @params[:ajp_host] %>:<%= @params[:ajp_port] %>/examples/servlets/servlet/RequestInfoExample - - Allow from all - - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/perl_env.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/perl_env.conf.erb deleted file mode 100644 index c76a9e8..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/perl_env.conf.erb +++ /dev/null @@ -1,9 +0,0 @@ - - Alias /env/ <%= @params[:app_dir] %>/ - - - SetHandler perl-script - PerlResponseHandler ModPerl::Registry - Options ExecCGI - - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/php_env.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/php_env.conf.erb deleted file mode 100644 index 66a4e60..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/php_env.conf.erb +++ /dev/null @@ -1,7 +0,0 @@ - - Alias /env/ <%= @params[:app_dir] %>/ - - - SetHandler php5-script - - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/python_env.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/python_env.conf.erb deleted file mode 100644 index 9c101a4..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/python_env.conf.erb +++ /dev/null @@ -1,8 +0,0 @@ - - Alias /env/ <%= @params[:app_dir] %>/ - - AddHandler mod_python .py - PythonHandler mod_python.cgihandler - PythonDebug On - - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/ssl.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/ssl.conf.erb deleted file mode 100644 index a8bc39f..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/ssl.conf.erb +++ /dev/null @@ -1,13 +0,0 @@ - - ServerName <%= @params[:server_name] %> - DocumentRoot <%= @params[:document_root] %> - - Options -Indexes - ErrorDocument 403 /error/noindex.html - - - SSLEngine on - SSLCertificateFile <%= @params[:ssl_cert_file] %> - SSLCertificateKeyFile <%= @params[:ssl_cert_key_file] %> - - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/status.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/status.conf.erb deleted file mode 100644 index 9ff8c78..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/status.conf.erb +++ /dev/null @@ -1,9 +0,0 @@ - - - SetHandler server-status - Order deny,allow - Deny from all - Allow from localhost ip6-localhost - Allow from <%= @remote_host %> - - diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/svn_repo.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/svn_repo.conf.erb deleted file mode 100644 index cf8154e..0000000 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/svn_repo.conf.erb +++ /dev/null @@ -1,6 +0,0 @@ - - -DAV svn -SVNPath <%= @params[:repo_dir] %> - - diff --git a/chef/cookbooks/apt/.kitchen.yml b/chef/cookbooks/apt/.kitchen.yml deleted file mode 100644 index 49c3a45..0000000 --- a/chef/cookbooks/apt/.kitchen.yml +++ /dev/null @@ -1,68 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: - - recipe[apt] - -- name: debian-7.1.0 - driver_config: - box: opscode-debian-7.1.0 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-7.1.0_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-13.04 - driver_config: - box: opscode-ubuntu-13.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-13.04_provisionerless.box - run_list: - - recipe[apt] - -suites: -- name: default - run_list: - - recipe[minitest-handler] - - recipe[apt_test] - attributes: {} - -- name: cacher-client - run_list: - - recipe[minitest-handler] - - recipe[apt_test::cacher-client] - attributes: {} - -- name: cacher-ng - run_list: - - recipe[minitest-handler] - - recipe[apt_test::cacher-ng] - attributes: {} - -- name: cacher-ng-client - run_list: - - recipe[minitest-handler] - - recipe[apt_test::cacher-ng-client] - attributes: - apt: - cacher_dir: '/tmp/apt-cacher' - cacher_port: '9876' - compiletime: true - -- name: lwrps - run_list: - - recipe[minitest-handler] - - recipe[apt_test::lwrps] - attributes: {} diff --git a/chef/cookbooks/apt/Berksfile b/chef/cookbooks/apt/Berksfile deleted file mode 100644 index 91b26e6..0000000 --- a/chef/cookbooks/apt/Berksfile +++ /dev/null @@ -1,8 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook "minitest-handler" - cookbook "apt_test", :path => "./test/cookbooks/apt_test" -end diff --git a/chef/cookbooks/apt/CHANGELOG.md b/chef/cookbooks/apt/CHANGELOG.md index fb2046c..769fd7e 100644 --- a/chef/cookbooks/apt/CHANGELOG.md +++ b/chef/cookbooks/apt/CHANGELOG.md @@ -2,6 +2,60 @@ apt Cookbook CHANGELOG ====================== This file is used to list changes made in each version of the apt cookbook. +v2.3.8 (2014-02-14) +------------------- +### Bug +- **[COOK-4287](https://tickets.opscode.com/browse/COOK-4287)** - Cleanup the Kitchen + + +v2.3.6 +------ +* [COOK-4154] - Add chefspec matchers.rb file to apt cookbook +* [COOK-4102] - Only index created repository + + +v2.3.6 +------ +* [COOK-4154] - Add chefspec matchers.rb file to apt cookbook +* [COOK-4102] - Only index created repository + + +v2.3.4 +------ +No change. Version bump for toolchain sanity + + +v2.3.2 +------ +- [COOK-3905] apt-get-update-periodic: configuration for the update period +- Updating style for rubocops +- Updating test-kitchen harness + + +v2.3.0 +------ +### Bug +- **[COOK-3812](https://tickets.opscode.com/browse/COOK-3812)** - Add a way to bypass the apt existence check + +### Improvement +- **[COOK-3567](https://tickets.opscode.com/browse/COOK-3567)** - Allow users to bypass apt-cache via attributes + + +v2.2.1 +------ +### Improvement +- **[COOK-664](https://tickets.opscode.com/browse/COOK-664)** - Check platform before running apt-specific commands + + +v2.2.0 +------ +### Bug +- **[COOK-3707](https://tickets.opscode.com/browse/COOK-3707)** - multiple nics confuse apt::cacher-client + +v2.1.2 +------ +### Improvement +- **[COOK-3551](https://tickets.opscode.com/browse/COOK-3551)** - Allow user to set up a trusted APT repository v2.1.1 ------ diff --git a/chef/cookbooks/apt/README.md b/chef/cookbooks/apt/README.md index 32831bd..f23212a 100644 --- a/chef/cookbooks/apt/README.md +++ b/chef/cookbooks/apt/README.md @@ -1,36 +1,19 @@ -Description -=========== +apt Cookbook +============ +This cookbook includes recipes to execute apt-get update to ensure the local APT package cache is up to date. There are recipes for managing the apt-cacher-ng caching proxy and proxy clients. It also includes a LWRP for managing APT repositories in /etc/apt/sources.list.d as well as an LWRP for pinning packages via /etc/apt/preferences.d. -This cookbook includes recipes to execute apt-get update to ensure the -local APT package cache is up to date. There are recipes for managing -the apt-cacher-ng caching proxy and proxy clients. It also includes a -LWRP for managing APT repositories in /etc/apt/sources.list.d as well as -an LWRP for pinning packages via /etc/apt/preferences.d. Requirements -============ +------------ +**Version 2.0.0+ of this cookbook requires Chef 11.0.0 or later**. If your Chef version is earlier than 11.0.0, use version 1.10.0 of this cookbook. -Version 2.0.0+ of this cookbook requires **Chef 11.0.0** or later. +Version 1.8.2 to 1.10.0 of this cookbook requires **Chef 10.16.4** or later. -If your Chef version is earlier than 11.0.0, use version 1.10.0 of -this cookbook. - -See [COOK-2258](http://tickets.opscode.com/browse/COOK-2258) for more -information on this requirement. - -Version 1.8.2 to 1.10.0 of this cookbook requires **Chef 10.16.4** or -later. - -If your Chef version is earlier than 10.16.4, use version 1.7.0 of -this cookbook. - -See [CHEF-3493](http://tickets.opscode.com/browse/CHEF-3493) and -[this code comment](http://bit.ly/VgvCgf) for more information on this -requirement. - -## Platform +If your Chef version is earlier than 10.16.4, use version 1.7.0 of this cookbook. +### Platform Please refer to the [TESTING file](TESTING.md) to see the currently (and passing) tested platforms. The release was tested on: + * Ubuntu 10.04 * Ubuntu 12.04 * Ubuntu 13.04 @@ -39,192 +22,217 @@ Please refer to the [TESTING file](TESTING.md) to see the currently (and passing May work with or without modification on other Debian derivatives. -Recipes -======= -## default +------- +### default +This recipe installs the `update-notifier-common` package to provide the timestamp file used to only run `apt-get update` if the cache is more than one day old. -This recipe installs the `update-notifier-common` package to provide -the timestamp file used to only run `apt-get update` if the cache is -more than one day old. - -This recipe should appear first in the run list of Debian or Ubuntu -nodes to ensure that the package cache is up to date before managing -any `package` resources with Chef. +This recipe should appear first in the run list of Debian or Ubuntu nodes to ensure that the package cache is up to date before managing any `package` resources with Chef. This recipe also sets up a local cache directory for preseeding packages. -## cacher-client +**Including the default recipe on a node that does not support apt (such as Windows) results in a noop.** +### cacher-client Configures the node to use the `apt-cacher-ng` server as a client. -## cacher-ng +#### Bypassing the cache +Occasionally you may come across repositories that do not play nicely when the node is using an `apt-cacher-ng` server. You can configure `cacher-client` to bypass the server and connect directly to the repository with the `cache_bypass` attribute. -Installs the `apt-cacher-ng` package and service so the system can -provide APT caching. You can check the usage report at -http://{hostname}:3142/acng-report.html. +To do this, you need to override the `cache_bypass` attribute with an array of repositories, with each array key as the repository URL and value as the protocol to use: + +```json +{ + ..., + 'apt': { + ..., + 'cache_bypass': { + URL: PROTOCOL + } + } +} +``` + +For example, to prevent caching and directly connect to the repository at `download.oracle.com` via http: + +```json +{ + 'apt': { + 'cache_bypass': { + 'download.oracle.com': 'http' + } + } +} +``` + +### cacher-ng +Installs the `apt-cacher-ng` package and service so the system can provide APT caching. You can check the usage report at http://{hostname}:3142/acng-report.html. + +If you wish to help the `cacher-ng` recipe seed itself, you must now explicitly include the `cacher-client` recipe in your run list **after** `cacher-ng` or you will block your ability to install any packages (ie. `apt-cacher-ng`). -If you wish to help the `cacher-ng` recipe seed itself, you must now explicitly -include the `cacher-client` recipe in your run list **after** `cacher-ng` or you -will block your ability to install any packages (ie. `apt-cacher-ng`). Attributes -========== - +---------- * `['apt']['cacher_ipaddress']` - use a cacher server (or standard proxy server) not available via search +* `['apt']['cacher_interface]` - interface to connect to the cacher-ng service, no default. * `['apt']['cacher_port']` - port for the cacher-ng service (either client or server), default is '3142' * `['apt']['cacher_dir']` - directory used by cacher-ng service, default is '/var/cache/apt-cacher-ng' * `['apt']['cacher-client']['restrict_environment']` - restrict your node to using the `apt-cacher-ng` server in your Environment, default is 'false' * `['apt']['compiletime']` - force the `cacher-client` recipe to run before other recipes. It forces apt to use the proxy before other recipes run. Useful if your nodes have limited access to public apt repositories. This is overridden if the `cacher-ng` recipe is in your run list. Default is 'false' +* `['apt']['cache_bypass']` - array of URLs to bypass the cache. Accepts the URL and protocol to fetch directly from the remote repository and not attempt to cache +* `['apt']['periodic_update_min_delay']` - minimum delay (in seconds) beetween two actual executions of `apt-get update` by the `execute[apt-get-update-periodic]` resource, default is '86400' (24 hours) + +Libraries +--------- +There is an `interface_ipaddress` method that returns the IP address for a particular host and interface, used by the `cacher-client` recipe. To enable it on the server use the `['apt']['cacher_interface']` attribute. Resources/Providers -=================== - -## Managing repositories - -This LWRP provides an easy way to manage additional APT repositories. -Adding a new repository will notify running the `execute[apt-get-update]` -resource immediately. - -### Actions +------------------- +### `apt_repository` +This LWRP provides an easy way to manage additional APT repositories. Adding a new repository will notify running the `execute[apt-get-update]` resource immediately. +#### Actions - :add: creates a repository file and builds the repository listing - :remove: removes the repository file -### Attribute Parameters - +#### Attribute Parameters - repo_name: name attribute. The name of the channel to discover - uri: the base of the Debian distribution -- distribution: this is usually your release's codename...ie something - like `karmic`, `lucid` or `maverick` +- distribution: this is usually your release's codename...ie something like `karmic`, `lucid` or `maverick` - components: package groupings..when it doubt use `main` -- arch: constrain package to a particular arch like `i386`, `amd64` or - even `armhf` or `powerpc`. Defaults to nil. -- deb_src: whether or not to add the repository as a source repo as - well - value can be `true` or `false`, default `false`. +- arch: constrain package to a particular arch like `i386`, `amd64` or even `armhf` or `powerpc`. Defaults to nil. +- trusted: treat all packages from this repository as authenticated regardless of signature +- deb_src: whether or not to add the repository as a source repo as well - value can be `true` or `false`, default `false`. - keyserver: the GPG keyserver where the key for the repo should be retrieved -- key: if a `keyserver` is provided, this is assumed to be the - fingerprint, otherwise it can be either the URI to the GPG key for - the repo, or a cookbook_file. +- key: if a `keyserver` is provided, this is assumed to be the fingerprint, otherwise it can be either the URI to the GPG key for the repo, or a cookbook_file. - key_proxy: if set, pass the specified proxy via `http-proxy=` to GPG. -- cookbook: if key should be a cookbook_file, specify a cookbook where - the key is located for files/default. Defaults to nil, so it will - use the cookbook where the resource is used. +- cookbook: if key should be a cookbook_file, specify a cookbook where the key is located for files/default. Defaults to nil, so it will use the cookbook where the resource is used. -### Examples +#### Examples - # add the Zenoss repo - apt_repository "zenoss" do - uri "http://dev.zenoss.org/deb" - components ["main","stable"] - end +Add the Zenoss repo: - # add the Nginx PPA; grab key from keyserver - apt_repository "nginx-php" do - uri "http://ppa.launchpad.net/nginx/php5/ubuntu" - distribution node['lsb']['codename'] - components ["main"] - keyserver "keyserver.ubuntu.com" - key "C300EE8C" - end +```ruby +apt_repository 'zenoss' do + uri 'http://dev.zenoss.org/deb' + components ['main', 'stable'] +end +``` - # add the Nginx PPA; grab key from keyserver, also add source repo - apt_repository "nginx-php" do - uri "http://ppa.launchpad.net/nginx/php5/ubuntu" - distribution node['lsb']['codename'] - components ["main"] - keyserver "keyserver.ubuntu.com" - key "C300EE8C" - deb_src true - end +Add the Nginx PPA, grabbing the key from keyserver: - # add the Cloudera Repo of CDH4 packages for Ubuntu 12.04 on AMD64 - apt_repository "cloudera" do - uri "http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh" - arch "amd64" - distribution "precise-cdh4" - components ["contrib"] - key "http://archive.cloudera.com/debian/archive.key" - end +```ruby +apt_repository 'nginx-php' do + uri 'http://ppa.launchpad.net/nginx/php5/ubuntu' + distribution node['lsb']['codename'] + components ['main'] + keyserver 'keyserver.ubuntu.com' + key 'C300EE8C' +end +``` - # remove Zenoss repo - apt_repository "zenoss" do - action :remove - end +Add the Nginx PPA, grab the key from the keyserver, and add source repo: -## Pinning packages +```ruby +apt_repository 'nginx-php' do + uri 'http://ppa.launchpad.net/nginx/php5/ubuntu' + distribution node['lsb']['codename'] + components ['main'] + keyserver 'keyserver.ubuntu.com' + key 'C300EE8C' + deb_src true +end +``` -This LWRP provides an easy way to pin packages in /etc/apt/preferences.d. -Although apt-pinning is quite helpful from time to time please note that Debian -does not encourage its use without thorough consideration. +Add the Cloudera Repo of CDH4 packages for Ubuntu 12.04 on AMD64: -Further information regarding apt-pinning is available via -http://wiki.debian.org/AptPreferences. +```ruby +apt_repository 'cloudera' do + uri 'http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh' + arch 'amd64' + distribution 'precise-cdh4' + components ['contrib'] + key 'http://archive.cloudera.com/debian/archive.key' +end +``` -### Actions +Remove Zenoss repo: +```ruby +apt_repository 'zenoss' do + action :remove +end +``` + +### `apt_preference` +This LWRP provides an easy way to pin packages in /etc/apt/preferences.d. Although apt-pinning is quite helpful from time to time please note that Debian does not encourage its use without thorough consideration. + +Further information regarding apt-pinning is available via http://wiki.debian.org/AptPreferences. + +#### Actions - :add: creates a preferences file under /etc/apt/preferences.d - :remove: Removes the file, therefore unpin the package -### Attribute Parameters - +#### Attribute Parameters - package_name: name attribute. The name of the package - glob: Pin by glob() expression or regexp surrounded by /. - pin: The package version/repository to pin - pin_priority: The pinning priority aka "the highest package version wins" -### Examples +#### Examples +Pin libmysqlclient16 to version 5.1.49-3: - # Pin libmysqlclient16 to version 5.1.49-3 - apt_preference "libmysqlclient16" do - pin "version 5.1.49-3" - pin_priority "700" - end +```ruby +apt_preference 'libmysqlclient16' do + pin 'version 5.1.49-3' + pin_priority '700' +end +``` - # Unpin libmysqlclient16 - apt_preference "libmysqlclient16" do - action :remove - end +Unpin libmysqlclient16: + +```ruby +apt_preference 'libmysqlclient16' do + action :remove +end +``` + +Pin all packages from dotdeb.org: + +```ruby +apt_preference 'dotdeb' do + glob '*' + pin 'origin packages.dotdeb.org' + pin_priority '700' +end +``` - # Pin all packages from dotdeb.org - apt_preference "dotdeb" do - glob "*" - pin "origin packages.dotdeb.org " - pin_priority "700" - end Usage -===== +----- +Put `recipe[apt]` first in the run list. If you have other recipes that you want to use to configure how apt behaves, like new sources, notify the execute resource to run, e.g.: -Put `recipe[apt]` first in the run list. If you have other recipes -that you want to use to configure how apt behaves, like new sources, -notify the execute resource to run, e.g.: +```ruby +template '/etc/apt/sources.list.d/my_apt_sources.list' do + notifies :run, 'execute[apt-get update]', :immediately +end +``` - template "/etc/apt/sources.list.d/my_apt_sources.list" do - notifies :run, resources(:execute => "apt-get update"), :immediately - end +The above will run during execution phase since it is a normal template resource, and should appear before other package resources that need the sources in the template. -The above will run during execution phase since it is a normal -template resource, and should appear before other package resources -that need the sources in the template. +Put `recipe[apt::cacher-ng]` in the run_list for a server to provide APT caching and add `recipe[apt::cacher-client]` on the rest of the Debian-based nodes to take advantage of the caching server. -Put `recipe[apt::cacher-ng]` in the run_list for a server to provide -APT caching and add `recipe[apt::cacher-client]` on the rest of the -Debian-based nodes to take advantage of the caching server. +If you want to cleanup unused packages, there is also the `apt-get autoclean` and `apt-get autoremove` resources provided for automated cleanup. -If you want to cleanup unused packages, there is also the `apt-get autoclean` -and `apt-get autoremove` resources provided for automated cleanup. -License and Author -================== +License & Authors +----------------- +- Author:: Joshua Timberman (joshua@opscode.com) +- Author:: Matt Ray (matt@opscode.com) +- Author:: Seth Chisamore (schisamo@opscode.com) -| | | -|:---------------------|:----------------------------------------| -| **Author** | Joshua Timberman | -| **Author** | Matt Ray () | -| **Author** | Seth Chisamore () | -| | | -| **Copyright** | Copyright (c) 2009-2013, Opscode, Inc. | +```text +Copyright 2009-2013, Opscode, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -237,3 +245,4 @@ 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. +``` diff --git a/chef/cookbooks/apt/TESTING.md b/chef/cookbooks/apt/TESTING.md deleted file mode 100644 index a875cdc..0000000 --- a/chef/cookbooks/apt/TESTING.md +++ /dev/null @@ -1,44 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0) and ChefSpec. This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test - -This cookbook has the following Test-Kitchen coverage: - -| Test Coverage | Ubuntu 10.04 | Ubuntu 12.04 | Ubuntu 13.04 | Debian 7.1 | -| ---------------- |:-------------:|:------------:|:------------:|:----------:| -| default | **Y** | **Y** | **Y** | **Y** | -| cacher-client | **Y** | **Y** | **Y** | **Y** | -| cacher-ng | **Y** | **Y** | **Y** | **Y** | -| cacher-ng-client | **Y** | **Y** | **Y** | **Y** | -| lwrps | **Y** | **Y** | **Y** | **Y** | - -If you wish to run unit tests with ChefSpec, install this additional requirement: - - gem install chefspec - -and you can run the tests with: - - rspec - diff --git a/chef/cookbooks/apt/attributes/default.rb b/chef/cookbooks/apt/attributes/default.rb index 0f919e8..2f6b4e1 100644 --- a/chef/cookbooks/apt/attributes/default.rb +++ b/chef/cookbooks/apt/attributes/default.rb @@ -1,6 +1,28 @@ +# +# Cookbook Name:: apt +# Attributes:: default +# +# Copyright 2009-2013, Opscode, Inc. +# +# 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. +# + default['apt']['cacher-client']['restrict_environment'] = false default['apt']['cacher_dir'] = '/var/cache/apt-cacher-ng' +default['apt']['cacher_interface'] = nil default['apt']['cacher_port'] = 3142 default['apt']['caching_server'] = false default['apt']['compiletime'] = false default['apt']['key_proxy'] = '' +default['apt']['cache_bypass'] = {} +default['apt']['periodic_update_min_delay'] = 86_400 diff --git a/chef/cookbooks/apt/libraries/helpers.rb b/chef/cookbooks/apt/libraries/helpers.rb new file mode 100644 index 0000000..6fe95a9 --- /dev/null +++ b/chef/cookbooks/apt/libraries/helpers.rb @@ -0,0 +1,48 @@ +# +# Cookbook Name:: apt +# Library:: helpers +# +# Copyright 2013 Opscode, Inc. +# +# 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. +# + +module Apt + # Helpers for apt + module Helpers + # Determines if apt is installed on a system. + # + # @return [Boolean] + def apt_installed? + !which('apt-get').nil? + end + + # Finds a command in $PATH + # + # @return [String, nil] + def which(cmd) + paths = (ENV['PATH'].split(::File::PATH_SEPARATOR) + %w(/bin /usr/bin /sbin /usr/sbin)) + + paths.each do |path| + possible = File.join(path, cmd) + return possible if File.executable?(possible) + end + + nil + end + end +end + +Chef::Recipe.send(:include, ::Apt::Helpers) +Chef::Resource.send(:include, ::Apt::Helpers) +Chef::Provider.send(:include, ::Apt::Helpers) diff --git a/chef/cookbooks/apt/libraries/matchers.rb b/chef/cookbooks/apt/libraries/matchers.rb new file mode 100644 index 0000000..aafce4d --- /dev/null +++ b/chef/cookbooks/apt/libraries/matchers.rb @@ -0,0 +1,17 @@ +if defined?(ChefSpec) + def add_apt_preference(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:apt_preference, :add, resource_name) + end + + def remove_apt_preference(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:apt_preference, :remove, resource_name) + end + + def add_apt_repository(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:apt_repository, :add, resource_name) + end + + def remove_apt_repository(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:apt_repository, :remove, resource_name) + end +end diff --git a/chef/cookbooks/yum/files/default/tests/minitest/default_test.rb b/chef/cookbooks/apt/libraries/network.rb similarity index 64% rename from chef/cookbooks/yum/files/default/tests/minitest/default_test.rb rename to chef/cookbooks/apt/libraries/network.rb index 76f1a14..8535d6d 100644 --- a/chef/cookbooks/yum/files/default/tests/minitest/default_test.rb +++ b/chef/cookbooks/apt/libraries/network.rb @@ -1,6 +1,6 @@ # -# Cookbook Name:: yum -# Recipe:: default +# Cookbook Name:: apt +# library:: network # # Copyright 2013, Opscode, Inc. # @@ -17,12 +17,15 @@ # limitations under the License. # -require File.expand_path('../support/helpers', __FILE__) - -describe "yum::default" do - include Helpers::YumTest - - it "Default recipe does nothing, so default_test does nothing" do - skip "Default recipe does nothing so default test does nothing" +module ::Apt + def interface_ipaddress(host, interface) + if interface + addresses = host['network']['interfaces'][interface]['addresses'] + addresses.select do |ip, data| + return ip if data['family'].eql?('inet') + end + else + return host.ipaddress + end end end diff --git a/chef/cookbooks/apt/metadata.json b/chef/cookbooks/apt/metadata.json new file mode 100644 index 0000000..7a5316e --- /dev/null +++ b/chef/cookbooks/apt/metadata.json @@ -0,0 +1,54 @@ +{ + "name": "apt", + "version": "2.3.8", + "description": "Configures apt and apt services and LWRPs for managing apt repositories and preferences", + "long_description": "apt Cookbook\n============\nThis cookbook includes recipes to execute apt-get update to ensure the local APT package cache is up to date. There are recipes for managing the apt-cacher-ng caching proxy and proxy clients. It also includes a LWRP for managing APT repositories in /etc/apt/sources.list.d as well as an LWRP for pinning packages via /etc/apt/preferences.d.\n\n\nRequirements\n------------\n**Version 2.0.0+ of this cookbook requires Chef 11.0.0 or later**. If your Chef version is earlier than 11.0.0, use version 1.10.0 of this cookbook.\n\nVersion 1.8.2 to 1.10.0 of this cookbook requires **Chef 10.16.4** or later.\n\nIf your Chef version is earlier than 10.16.4, use version 1.7.0 of this cookbook.\n\n### Platform\nPlease refer to the [TESTING file](TESTING.md) to see the currently (and passing) tested platforms. The release was tested on:\n\n* Ubuntu 10.04\n* Ubuntu 12.04\n* Ubuntu 13.04\n* Debian 7.1\n* Debian 6.0 (have with manual testing)\n\nMay work with or without modification on other Debian derivatives.\n\n\n-------\n### default\nThis recipe installs the `update-notifier-common` package to provide the timestamp file used to only run `apt-get update` if the cache is more than one day old.\n\nThis recipe should appear first in the run list of Debian or Ubuntu nodes to ensure that the package cache is up to date before managing any `package` resources with Chef.\n\nThis recipe also sets up a local cache directory for preseeding packages.\n\n**Including the default recipe on a node that does not support apt (such as Windows) results in a noop.**\n\n### cacher-client\nConfigures the node to use the `apt-cacher-ng` server as a client.\n\n#### Bypassing the cache\nOccasionally you may come across repositories that do not play nicely when the node is using an `apt-cacher-ng` server. You can configure `cacher-client` to bypass the server and connect directly to the repository with the `cache_bypass` attribute.\n\nTo do this, you need to override the `cache_bypass` attribute with an array of repositories, with each array key as the repository URL and value as the protocol to use:\n\n```json\n{\n ...,\n 'apt': {\n ...,\n 'cache_bypass': {\n URL: PROTOCOL\n }\n }\n}\n```\n\nFor example, to prevent caching and directly connect to the repository at `download.oracle.com` via http:\n\n```json\n{\n 'apt': {\n 'cache_bypass': {\n 'download.oracle.com': 'http'\n }\n }\n}\n```\n\n### cacher-ng\nInstalls the `apt-cacher-ng` package and service so the system can provide APT caching. You can check the usage report at http://{hostname}:3142/acng-report.html.\n\nIf you wish to help the `cacher-ng` recipe seed itself, you must now explicitly include the `cacher-client` recipe in your run list **after** `cacher-ng` or you will block your ability to install any packages (ie. `apt-cacher-ng`).\n\n\nAttributes\n----------\n* `['apt']['cacher_ipaddress']` - use a cacher server (or standard proxy server) not available via search\n* `['apt']['cacher_interface]` - interface to connect to the cacher-ng service, no default.\n* `['apt']['cacher_port']` - port for the cacher-ng service (either client or server), default is '3142'\n* `['apt']['cacher_dir']` - directory used by cacher-ng service, default is '/var/cache/apt-cacher-ng'\n* `['apt']['cacher-client']['restrict_environment']` - restrict your node to using the `apt-cacher-ng` server in your Environment, default is 'false'\n* `['apt']['compiletime']` - force the `cacher-client` recipe to run before other recipes. It forces apt to use the proxy before other recipes run. Useful if your nodes have limited access to public apt repositories. This is overridden if the `cacher-ng` recipe is in your run list. Default is 'false'\n* `['apt']['cache_bypass']` - array of URLs to bypass the cache. Accepts the URL and protocol to fetch directly from the remote repository and not attempt to cache\n* `['apt']['periodic_update_min_delay']` - minimum delay (in seconds) beetween two actual executions of `apt-get update` by the `execute[apt-get-update-periodic]` resource, default is '86400' (24 hours)\n\nLibraries\n---------\nThere is an `interface_ipaddress` method that returns the IP address for a particular host and interface, used by the `cacher-client` recipe. To enable it on the server use the `['apt']['cacher_interface']` attribute.\n\nResources/Providers\n-------------------\n### `apt_repository`\nThis LWRP provides an easy way to manage additional APT repositories. Adding a new repository will notify running the `execute[apt-get-update]` resource immediately.\n\n#### Actions\n- :add: creates a repository file and builds the repository listing\n- :remove: removes the repository file\n\n#### Attribute Parameters\n- repo_name: name attribute. The name of the channel to discover\n- uri: the base of the Debian distribution\n- distribution: this is usually your release's codename...ie something like `karmic`, `lucid` or `maverick`\n- components: package groupings..when it doubt use `main`\n- arch: constrain package to a particular arch like `i386`, `amd64` or even `armhf` or `powerpc`. Defaults to nil.\n- trusted: treat all packages from this repository as authenticated regardless of signature\n- deb_src: whether or not to add the repository as a source repo as well - value can be `true` or `false`, default `false`.\n- keyserver: the GPG keyserver where the key for the repo should be retrieved\n- key: if a `keyserver` is provided, this is assumed to be the fingerprint, otherwise it can be either the URI to the GPG key for the repo, or a cookbook_file.\n- key_proxy: if set, pass the specified proxy via `http-proxy=` to GPG.\n- cookbook: if key should be a cookbook_file, specify a cookbook where the key is located for files/default. Defaults to nil, so it will use the cookbook where the resource is used.\n\n#### Examples\n\nAdd the Zenoss repo:\n\n```ruby\napt_repository 'zenoss' do\n uri 'http://dev.zenoss.org/deb'\n components ['main', 'stable']\nend\n```\n\nAdd the Nginx PPA, grabbing the key from keyserver:\n\n```ruby\napt_repository 'nginx-php' do\n uri 'http://ppa.launchpad.net/nginx/php5/ubuntu'\n distribution node['lsb']['codename']\n components ['main']\n keyserver 'keyserver.ubuntu.com'\n key 'C300EE8C'\nend\n```\n\nAdd the Nginx PPA, grab the key from the keyserver, and add source repo:\n\n```ruby\napt_repository 'nginx-php' do\n uri 'http://ppa.launchpad.net/nginx/php5/ubuntu'\n distribution node['lsb']['codename']\n components ['main']\n keyserver 'keyserver.ubuntu.com'\n key 'C300EE8C'\n deb_src true\nend\n```\n\nAdd the Cloudera Repo of CDH4 packages for Ubuntu 12.04 on AMD64:\n\n```ruby\napt_repository 'cloudera' do\n uri 'http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh'\n arch 'amd64'\n distribution 'precise-cdh4'\n components ['contrib']\n key 'http://archive.cloudera.com/debian/archive.key'\nend\n```\n\nRemove Zenoss repo:\n\n```ruby\napt_repository 'zenoss' do\n action :remove\nend\n```\n\n### `apt_preference`\nThis LWRP provides an easy way to pin packages in /etc/apt/preferences.d. Although apt-pinning is quite helpful from time to time please note that Debian does not encourage its use without thorough consideration.\n\nFurther information regarding apt-pinning is available via http://wiki.debian.org/AptPreferences.\n\n#### Actions\n- :add: creates a preferences file under /etc/apt/preferences.d\n- :remove: Removes the file, therefore unpin the package\n\n#### Attribute Parameters\n- package_name: name attribute. The name of the package\n- glob: Pin by glob() expression or regexp surrounded by /.\n- pin: The package version/repository to pin\n- pin_priority: The pinning priority aka \"the highest package version wins\"\n\n#### Examples\nPin libmysqlclient16 to version 5.1.49-3:\n\n```ruby\napt_preference 'libmysqlclient16' do\n pin 'version 5.1.49-3'\n pin_priority '700'\nend\n```\n\nUnpin libmysqlclient16:\n\n```ruby\napt_preference 'libmysqlclient16' do\n action :remove\nend\n```\n\nPin all packages from dotdeb.org:\n\n```ruby\napt_preference 'dotdeb' do\n glob '*'\n pin 'origin packages.dotdeb.org'\n pin_priority '700'\nend\n```\n\n\nUsage\n-----\nPut `recipe[apt]` first in the run list. If you have other recipes that you want to use to configure how apt behaves, like new sources, notify the execute resource to run, e.g.:\n\n```ruby\ntemplate '/etc/apt/sources.list.d/my_apt_sources.list' do\n notifies :run, 'execute[apt-get update]', :immediately\nend\n```\n\nThe above will run during execution phase since it is a normal template resource, and should appear before other package resources that need the sources in the template.\n\nPut `recipe[apt::cacher-ng]` in the run_list for a server to provide APT caching and add `recipe[apt::cacher-client]` on the rest of the Debian-based nodes to take advantage of the caching server.\n\nIf you want to cleanup unused packages, there is also the `apt-get autoclean` and `apt-get autoremove` resources provided for automated cleanup.\n\n\nLicense & Authors\n-----------------\n- Author:: Joshua Timberman (joshua@opscode.com)\n- Author:: Matt Ray (matt@opscode.com)\n- Author:: Seth Chisamore (schisamo@opscode.com)\n\n```text\nCopyright 2009-2013, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "ubuntu": ">= 0.0.0", + "debian": ">= 0.0.0" + }, + "dependencies": { + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + "apt/cacher-client/restrict_environment": { + "description": "Whether to restrict the search for the caching server to the same environment as this node", + "default": "false" + }, + "apt/cacher_port": { + "description": "Default listen port for the caching server", + "default": "3142" + }, + "apt/cacher_interface": { + "description": "Default listen interface for the caching server", + "default": null + }, + "apt/key_proxy": { + "description": "Passed as the proxy passed to GPG for the apt_repository resource", + "default": "" + }, + "apt/caching_server": { + "description": "Set this to true if the node is a caching server", + "default": "false" + } + }, + "groupings": { + }, + "recipes": { + "apt": "Runs apt-get update during compile phase and sets up preseed directories", + "apt::cacher-ng": "Set up an apt-cacher-ng caching proxy", + "apt::cacher-client": "Client for the apt::cacher-ng caching proxy" + } +} \ No newline at end of file diff --git a/chef/cookbooks/apt/metadata.rb b/chef/cookbooks/apt/metadata.rb index de988f3..4ca3ffc 100644 --- a/chef/cookbooks/apt/metadata.rb +++ b/chef/cookbooks/apt/metadata.rb @@ -1,30 +1,34 @@ -name "apt" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Configures apt and apt services and LWRPs for managing apt repositories and preferences" +name 'apt' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Configures apt and apt services and LWRPs for managing apt repositories and preferences' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "2.1.1" -recipe "apt", "Runs apt-get update during compile phase and sets up preseed directories" -recipe "apt::cacher-ng", "Set up an apt-cacher-ng caching proxy" -recipe "apt::cacher-client", "Client for the apt::cacher-ng caching proxy" +version '2.3.8' +recipe 'apt', 'Runs apt-get update during compile phase and sets up preseed directories' +recipe 'apt::cacher-ng', 'Set up an apt-cacher-ng caching proxy' +recipe 'apt::cacher-client', 'Client for the apt::cacher-ng caching proxy' %w{ ubuntu debian }.each do |os| supports os end -attribute "apt/cacher-client/restrict_environment", - :description => "Whether to restrict the search for the caching server to the same environment as this node", - :default => "false" +attribute 'apt/cacher-client/restrict_environment', + :description => 'Whether to restrict the search for the caching server to the same environment as this node', + :default => 'false' -attribute "apt/cacher_port", - :description => "Default listen port for the caching server", - :default => "3142" +attribute 'apt/cacher_port', + :description => 'Default listen port for the caching server', + :default => '3142' -attribute "apt/key_proxy", - :description => "Passed as the proxy passed to GPG for the apt_repository resource", - :default => "" +attribute 'apt/cacher_interface', + :description => 'Default listen interface for the caching server', + :default => nil -attribute "apt/caching_server", - :description => "Set this to true if the node is a caching server", - :default => "false" +attribute 'apt/key_proxy', + :description => 'Passed as the proxy passed to GPG for the apt_repository resource', + :default => '' + +attribute 'apt/caching_server', + :description => 'Set this to true if the node is a caching server', + :default => 'false' diff --git a/chef/cookbooks/apt/providers/preference.rb b/chef/cookbooks/apt/providers/preference.rb index 8f34e74..fd9f624 100644 --- a/chef/cookbooks/apt/providers/preference.rb +++ b/chef/cookbooks/apt/providers/preference.rb @@ -19,27 +19,29 @@ # Build preferences.d file contents def build_pref(package_name, pin, pin_priority) - preference_content = "Package: #{package_name}\nPin: #{pin}\nPin-Priority: #{pin_priority}\n" + "Package: #{package_name}\nPin: #{pin}\nPin-Priority: #{pin_priority}\n" end action :add do new_resource.updated_by_last_action(false) - preference = build_pref(new_resource.glob || new_resource.package_name, - new_resource.pin, - new_resource.pin_priority) + preference = build_pref( + new_resource.glob || new_resource.package_name, + new_resource.pin, + new_resource.pin_priority + ) - preference_dir = directory "/etc/apt/preferences.d" do - owner "root" - group "root" + preference_dir = directory '/etc/apt/preferences.d' do + owner 'root' + group 'root' mode 00755 recursive true action :nothing end preference_file = file "/etc/apt/preferences.d/#{new_resource.name}" do - owner "root" - group "root" + owner 'root' + group 'root' mode 00644 content preference action :nothing diff --git a/chef/cookbooks/apt/providers/repository.rb b/chef/cookbooks/apt/providers/repository.rb index b3e0528..a481050 100644 --- a/chef/cookbooks/apt/providers/repository.rb +++ b/chef/cookbooks/apt/providers/repository.rb @@ -33,9 +33,9 @@ def install_key_from_keyserver(key, keyserver) end action :run not_if do - extract_fingerprints_from_cmd("apt-key finger").any? do |fingerprint| - fingerprint.end_with?(key.upcase) - end + extract_fingerprints_from_cmd('apt-key finger').any? do |fingerprint| + fingerprint.end_with?(key.upcase) + end end end end @@ -44,7 +44,7 @@ end def extract_fingerprints_from_cmd(cmd) so = Mixlib::ShellOut.new(cmd) so.run_command - so.stdout.split(/\n/).collect do |t| + so.stdout.split(/\n/).map do |t| if z = t.match(/^ +Key fingerprint = ([0-9A-F ]+)/) z[1].split.join end @@ -74,7 +74,7 @@ def install_key_from_uri(uri) command "apt-key add #{cached_keyfile}" action :run not_if do - installed_keys = extract_fingerprints_from_cmd("apt-key finger") + installed_keys = extract_fingerprints_from_cmd('apt-key finger') proposed_keys = extract_fingerprints_from_cmd("gpg --with-fingerprint #{cached_keyfile}") (installed_keys & proposed_keys).sort == proposed_keys.sort end @@ -82,10 +82,14 @@ def install_key_from_uri(uri) end # build repo file contents -def build_repo(uri, distribution, components, arch, add_deb_src) +def build_repo(uri, distribution, components, trusted, arch, add_deb_src) components = components.join(' ') if components.respond_to?(:join) + repo_options = [] + repo_options << "arch=#{arch}" if arch + repo_options << 'trusted=yes' if trusted + repo_options = '[' + repo_options.join(' ') + ']' unless repo_options.empty? repo_info = "#{uri} #{distribution} #{components}\n" - repo_info = "[arch=#{arch}] #{repo_info}" if arch + repo_info = "#{repo_options} #{repo_info}" unless repo_options.empty? repo = "deb #{repo_info}" repo << "deb-src #{repo_info}" if add_deb_src repo @@ -99,30 +103,40 @@ action :add do install_key_from_uri(new_resource.key) end - file "/var/lib/apt/periodic/update-success-stamp" do + file '/var/lib/apt/periodic/update-success-stamp' do action :nothing end - execute "apt-get update" do + execute 'apt-cache gencaches' do ignore_failure true action :nothing end + execute 'apt-get update' do + command "apt-get update -o Dir::Etc::sourcelist='sources.list.d/#{new_resource.name}.list' -o Dir::Etc::sourceparts='-' -o APT::Get::List-Cleanup='0'" + ignore_failure true + action :nothing + notifies :run, 'execute[apt-cache gencaches]', :immediately + end + # build repo file - repository = build_repo(new_resource.uri, - new_resource.distribution, - new_resource.components, - new_resource.arch, - new_resource.deb_src) + repository = build_repo( + new_resource.uri, + new_resource.distribution, + new_resource.components, + new_resource.trusted, + new_resource.arch, + new_resource.deb_src + ) file "/etc/apt/sources.list.d/#{new_resource.name}.list" do - owner "root" - group "root" + owner 'root' + group 'root' mode 00644 content repository action :create - notifies :delete, "file[/var/lib/apt/periodic/update-success-stamp]", :immediately - notifies :run, "execute[apt-get update]", :immediately if new_resource.cache_rebuild + notifies :delete, 'file[/var/lib/apt/periodic/update-success-stamp]', :immediately + notifies :run, 'execute[apt-get update]', :immediately if new_resource.cache_rebuild end end diff --git a/chef/cookbooks/apt/recipes/cacher-client.rb b/chef/cookbooks/apt/recipes/cacher-client.rb index 542191d..bee010f 100644 --- a/chef/cookbooks/apt/recipes/cacher-client.rb +++ b/chef/cookbooks/apt/recipes/cacher-client.rb @@ -17,11 +17,15 @@ # limitations under the License. # -#remove Acquire::http::Proxy lines from /etc/apt/apt.conf since we use 01proxy -#these are leftover from preseed installs +class ::Chef::Recipe + include ::Apt +end + +# remove Acquire::http::Proxy lines from /etc/apt/apt.conf since we use 01proxy +# these are leftover from preseed installs execute 'Remove proxy from /etc/apt/apt.conf' do command "sed --in-place '/^Acquire::http::Proxy/d' /etc/apt/apt.conf" - only_if "grep Acquire::http::Proxy /etc/apt/apt.conf" + only_if 'grep Acquire::http::Proxy /etc/apt/apt.conf' end servers = [] @@ -31,6 +35,7 @@ if node['apt'] cacher.default.name = node['apt']['cacher_ipaddress'] cacher.default.ipaddress = node['apt']['cacher_ipaddress'] cacher.default.apt.cacher_port = node['apt']['cacher_port'] + cacher.default.apt_cacher_interface = node['apt']['cacher_interface'] servers << cacher elsif node['apt']['caching_server'] node.override['apt']['compiletime'] = false @@ -38,8 +43,8 @@ if node['apt'] end end -unless (Chef::Config[:solo] || servers.length > 0) - query = "apt_caching_server:true" +unless Chef::Config[:solo] || servers.length > 0 + query = 'apt_caching_server:true' query += " AND chef_environment:#{node.chef_environment}" if node['apt']['cacher-client']['restrict_environment'] Chef::Log.debug("apt::cacher-client searching for '#{query}'") servers += search(:node, query) @@ -47,16 +52,22 @@ end if servers.length > 0 Chef::Log.info("apt-cacher-ng server found on #{servers[0]}.") + if servers[0]['apt']['cacher_interface'] + cacher_ipaddress = interface_ipaddress(servers[0], servers[0]['apt']['cacher_interface']) + else + cacher_ipaddress = servers[0].ipaddress + end t = template '/etc/apt/apt.conf.d/01proxy' do source '01proxy.erb' owner 'root' group 'root' mode 00644 variables( - :proxy => servers[0]['ipaddress'], - :port => servers[0]['apt']['cacher_port'] + :proxy => cacher_ipaddress, + :port => servers[0]['apt']['cacher_port'], + :bypass => node['apt']['cache_bypass'] ) - action( node['apt']['compiletime'] ? :nothing : :create ) + action(node['apt']['compiletime'] ? :nothing : :create) notifies :run, 'execute[apt-get update]', :immediately end t.run_action(:create) if node['apt']['compiletime'] diff --git a/chef/cookbooks/apt/recipes/cacher-ng.rb b/chef/cookbooks/apt/recipes/cacher-ng.rb index 30e7af3..8629dcf 100644 --- a/chef/cookbooks/apt/recipes/cacher-ng.rb +++ b/chef/cookbooks/apt/recipes/cacher-ng.rb @@ -4,14 +4,14 @@ # # Copyright 2008-2013, Opscode, Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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. @@ -19,25 +19,25 @@ node.set['apt']['caching_server'] = true -package "apt-cacher-ng" do +package 'apt-cacher-ng' do action :install end directory node['apt']['cacher_dir'] do - owner "apt-cacher-ng" - group "apt-cacher-ng" + owner 'apt-cacher-ng' + group 'apt-cacher-ng' mode 0755 end -template "/etc/apt-cacher-ng/acng.conf" do - source "acng.conf.erb" - owner "root" - group "root" +template '/etc/apt-cacher-ng/acng.conf' do + source 'acng.conf.erb' + owner 'root' + group 'root' mode 00644 - notifies :restart, "service[apt-cacher-ng]", :immediately + notifies :restart, 'service[apt-cacher-ng]', :immediately end -service "apt-cacher-ng" do +service 'apt-cacher-ng' do supports :restart => true, :status => false action [:enable, :start] end diff --git a/chef/cookbooks/apt/recipes/default.rb b/chef/cookbooks/apt/recipes/default.rb index a6bd8c4..9ce8cbe 100644 --- a/chef/cookbooks/apt/recipes/default.rb +++ b/chef/cookbooks/apt/recipes/default.rb @@ -2,67 +2,81 @@ # Cookbook Name:: apt # Recipe:: default # -# Copyright 2008-2011, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # Copyright 2009, Bryan McLellan # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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. # +# On systems where apt is not installed, the resources in this recipe are not +# executed. However, they _must_ still be present in the resource collection +# or other cookbooks which notify these resources will fail on non-apt-enabled +# systems. + +Chef::Log.debug 'apt is not installed. Apt-specific resources will not be executed.' unless apt_installed? + # Run apt-get update to create the stamp file -execute "apt-get-update" do - command "apt-get update" +execute 'apt-get-update' do + command 'apt-get update' ignore_failure true - not_if do ::File.exists?('/var/lib/apt/periodic/update-success-stamp') end + only_if { apt_installed? } + not_if { ::File.exists?('/var/lib/apt/periodic/update-success-stamp') } end # For other recipes to call to force an update -execute "apt-get update" do - command "apt-get update" +execute 'apt-get update' do + command 'apt-get update' ignore_failure true + only_if { apt_installed? } action :nothing end # Automatically remove packages that are no longer needed for dependencies -execute "apt-get autoremove" do - command "apt-get -y autoremove" +execute 'apt-get autoremove' do + command 'apt-get -y autoremove' + only_if { apt_installed? } action :nothing end # Automatically remove .deb files for packages no longer on your system -execute "apt-get autoclean" do - command "apt-get -y autoclean" +execute 'apt-get autoclean' do + command 'apt-get -y autoclean' + only_if { apt_installed? } action :nothing end # provides /var/lib/apt/periodic/update-success-stamp on apt-get update -package "update-notifier-common" do +package 'update-notifier-common' do notifies :run, 'execute[apt-get-update]', :immediately + only_if { apt_installed? } end -execute "apt-get-update-periodic" do - command "apt-get update" +execute 'apt-get-update-periodic' do + command 'apt-get update' ignore_failure true only_if do + apt_installed? && ::File.exists?('/var/lib/apt/periodic/update-success-stamp') && - ::File.mtime('/var/lib/apt/periodic/update-success-stamp') < Time.now - 86400 + ::File.mtime('/var/lib/apt/periodic/update-success-stamp') < Time.now - node['apt']['periodic_update_min_delay'] end end %w{/var/cache/local /var/cache/local/preseeding}.each do |dirname| directory dirname do - owner "root" - group "root" + owner 'root' + group 'root' mode 00755 action :create + only_if { apt_installed? } end end diff --git a/chef/cookbooks/apt/resources/preference.rb b/chef/cookbooks/apt/resources/preference.rb index 3ad7207..21589ee 100644 --- a/chef/cookbooks/apt/resources/preference.rb +++ b/chef/cookbooks/apt/resources/preference.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apt # Resource:: preference # -# Copyright 2010-2011, Opscode, Inc. +# Copyright 2010-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +18,9 @@ # actions :add, :remove +default_action :add if defined?(default_action) # Chef > 10.8 +# Needed for Chef versions < 0.10.10 def initialize(*args) super @action = :add diff --git a/chef/cookbooks/apt/resources/repository.rb b/chef/cookbooks/apt/resources/repository.rb index 7515da4..be737fe 100644 --- a/chef/cookbooks/apt/resources/repository.rb +++ b/chef/cookbooks/apt/resources/repository.rb @@ -2,7 +2,7 @@ # Cookbook Name:: apt # Resource:: repository # -# Copyright 2010-2011, Opscode, Inc. +# Copyright 2010-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,23 +18,26 @@ # actions :add, :remove +default_action :add if defined?(default_action) # Chef > 10.8 +# Needed for Chef versions < 0.10.10 def initialize(*args) super @action = :add end -#name of the repo, used for source.list filename +# name of the repo, used for source.list filename attribute :repo_name, :kind_of => String, :name_attribute => true attribute :uri, :kind_of => String attribute :distribution, :kind_of => String attribute :components, :kind_of => Array, :default => [] attribute :arch, :kind_of => String, :default => nil -#whether or not to add the repository as a source repo as well +attribute :trusted, :kind_of => [TrueClass, FalseClass], :default => false +# whether or not to add the repository as a source repo as well attribute :deb_src, :default => false attribute :keyserver, :kind_of => String, :default => nil attribute :key, :kind_of => String, :default => nil attribute :cookbook, :kind_of => String, :default => nil -#trigger cache rebuild -#If not you can trigger in the recipe itself after checking the status of resource.updated{_by_last_action}? +# trigger cache rebuild +# If not you can trigger in the recipe itself after checking the status of resource.updated{_by_last_action}? attribute :cache_rebuild, :kind_of => [TrueClass, FalseClass], :default => true diff --git a/chef/cookbooks/apt/spec/cacher-client_spec.rb b/chef/cookbooks/apt/spec/cacher-client_spec.rb deleted file mode 100644 index 923d9cb..0000000 --- a/chef/cookbooks/apt/spec/cacher-client_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'spec_helper' - -describe 'apt::cacher-client' do - - context 'no server' do - let(:chef_run) do - runner = ChefSpec::ChefRunner.new - runner.converge('apt::cacher-client') - end - - it 'does not create 01proxy file' do - expect(chef_run).not_to create_file('/etc/apt/apt.conf.d/01proxy') - end - end - - context 'server provided' do - let(:chef_run) do - runner = ChefSpec::ChefRunner.new - runner.node.set['apt']['cacher_ipaddress'] = '22.33.44.55' - runner.node.set['apt']['cacher_port'] = '9876' - runner.converge('apt::cacher-client') - end - - it 'creates 01proxy file' do - expect(chef_run).to create_file('/etc/apt/apt.conf.d/01proxy') - expect(chef_run).to create_file_with_content('/etc/apt/apt.conf.d/01proxy', 'Acquire::http::Proxy "http://22.33.44.55:9876";') - end - - end - -end diff --git a/chef/cookbooks/apt/spec/cacher-ng_spec.rb b/chef/cookbooks/apt/spec/cacher-ng_spec.rb deleted file mode 100644 index 16dc724..0000000 --- a/chef/cookbooks/apt/spec/cacher-ng_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' - -describe 'apt::cacher-ng' do - - context 'server' do - let(:chef_run) do - runner = ChefSpec::ChefRunner.new - runner.node.set['apt']['cacher_port'] = '9876' - runner.converge('apt::cacher-ng') - end - - it 'installs apt-cacher-ng' do - expect(chef_run).to install_package('apt-cacher-ng') - end - - it 'creates acng.conf file' do - expect(chef_run).to create_file('/etc/apt-cacher-ng/acng.conf') - end - - it 'enables and starts apt-cacher-ng' do - expect(chef_run).to set_service_to_start_on_boot 'apt-cacher-ng' - expect(chef_run).to start_service 'apt-cacher-ng' - end - - end - -end diff --git a/chef/cookbooks/apt/spec/default_spec.rb b/chef/cookbooks/apt/spec/default_spec.rb deleted file mode 100644 index dde466a..0000000 --- a/chef/cookbooks/apt/spec/default_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'spec_helper' - -describe 'apt::default' do - let(:chef_run) do - runner = ChefSpec::ChefRunner.new() - runner.converge('apt::default') - end - - it 'installs update-notifier-common' do - expect(chef_run).to install_package 'update-notifier-common' - end - - it 'apt-get updates' do - expect(chef_run).to execute_command 'apt-get update' - end - - it 'creates preseeding directory' do - expect(chef_run).to create_directory('/var/cache/local') - expect(chef_run).to create_directory('/var/cache/local/preseeding') - end - -end diff --git a/chef/cookbooks/apt/spec/spec_helper.rb b/chef/cookbooks/apt/spec/spec_helper.rb deleted file mode 100644 index cab2b59..0000000 --- a/chef/cookbooks/apt/spec/spec_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'chefspec' - -RSpec.configure do |config| - config.color_enabled = true - config.tty = true - config.formatter = :documentation - config.treat_symbols_as_metadata_keys_with_true_values = true - config.filter_run :focus => true - config.run_all_when_everything_filtered = true - config.expect_with :rspec do |c| - c.syntax = :expect - end -end diff --git a/chef/cookbooks/apt/templates/default/01proxy.erb b/chef/cookbooks/apt/templates/default/01proxy.erb index eea71c2..37bce87 100644 --- a/chef/cookbooks/apt/templates/default/01proxy.erb +++ b/chef/cookbooks/apt/templates/default/01proxy.erb @@ -1,2 +1,5 @@ Acquire::http::Proxy "http://<%= @proxy %>:<%= @port %>"; Acquire::https::Proxy "DIRECT"; +<% @bypass.each do |bypass, type| %> +Acquire::<%= type %>::Proxy::<%= bypass %> "DIRECT"; +<% end %> diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/README.md b/chef/cookbooks/apt/test/cookbooks/apt_test/README.md deleted file mode 100644 index 6e1f578..0000000 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/README.md +++ /dev/null @@ -1 +0,0 @@ -This cookbook is used with test-kitchen to test the parent, apt cookbok diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng-client_test.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng-client_test.rb deleted file mode 100644 index 75532cc..0000000 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng-client_test.rb +++ /dev/null @@ -1,41 +0,0 @@ -# -# Cookbook Name:: apt_test -# Recipe:: cacher-ng-client_test.rb -# -# Copyright 2013, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe "apt_test::cacher-ng-client" do - include Helpers::AptTest - - it 'creates the cacher_dir' do - directory(node['apt']['cacher_dir']).must_exist.with(:owner, "apt-cacher-ng") - end - - it 'runs the cacher service' do - service("apt-cacher-ng").must_be_running - end - - it 'creates 01proxy' do - file('/etc/apt/apt.conf.d/01proxy').must_include "Acquire::http::Proxy \"http://#{node['ipaddress']}:#{node['apt']['cacher_port']}\";" - end - - it 'installed colordiff' do - package('colordiff').must_be_installed - end - -end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/lwrps_test.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/lwrps_test.rb deleted file mode 100644 index b9c5cb3..0000000 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/lwrps_test.rb +++ /dev/null @@ -1,48 +0,0 @@ -# -# Cookbook Name:: apt_test -# Recipe:: lwrps -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe "apt_test::lwrps" do - include Helpers::AptTest - - it 'creates the Opscode sources.list' do - file("/etc/apt/sources.list.d/opscode.list").must_exist - end - - it 'adds the Opscode package signing key' do - opscode_key = shell_out("apt-key list") - assert opscode_key.stdout.include?("Opscode Packages ") - end - - it 'creates the correct pinning preferences for chef' do - pinning_prefs = "Package: chef\nPin: version 10.16.2-1" - file("/etc/apt/preferences.d/chef").must_match(/#{pinning_prefs}/) - end - - it 'creates a repo with an architecture' do - cloudera = "deb\s+\\[arch=amd64\\] http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh precise-cdh4 contrib" - file("/etc/apt/sources.list.d/cloudera.list").must_match(/#{cloudera}/) - end - - it 'creates the correct pinning preferences with a glob' do - pinning_prefs = "Package: \\*\nPin: origin packages.dotdeb.org" - file("/etc/apt/preferences.d/dotdeb").must_match(/#{pinning_prefs}/) - end -end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/metadata.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/metadata.rb deleted file mode 100644 index f909851..0000000 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/metadata.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "apt_test" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "This cookbook is used with test-kitchen to test the parent, apt cookbok" -version "1.0.0" diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng.rb deleted file mode 100644 index 20ae214..0000000 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng.rb +++ /dev/null @@ -1,20 +0,0 @@ -# -# Cookbook Name:: apt_test -# Recipe:: cacher-ng -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apt::cacher-ng" diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/lwrps.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/lwrps.rb deleted file mode 100644 index a0a3a38..0000000 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/lwrps.rb +++ /dev/null @@ -1,66 +0,0 @@ -# -# Cookbook Name:: apt_test -# Recipe:: lwrps -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "apt" - -# Apt Repository -apt_repository "opscode" do - uri "http://apt.opscode.com" - components ["main"] - distribution "#{node['lsb']['codename']}-0.10" - key "2940ABA983EF826A" - keyserver "pgpkeys.mit.edu" - action :add -end - -# Apt Repository with arch -apt_repository "cloudera" do - uri "http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh" - arch "amd64" - distribution "precise-cdh4" - components ["contrib"] - key "http://archive.cloudera.com/debian/archive.key" - action :add -end - -# Apt repository and install a package it contains -apt_repository "nginx" do - uri "http://nginx.org/packages/#{node['platform']}" - distribution node['lsb']['codename'] - components ["nginx"] - key "http://nginx.org/keys/nginx_signing.key" - deb_src true -end - -package "nginx-debug" do - action :upgrade -end - -# Apt Preferences -apt_preference "chef" do - pin "version 10.16.2-1" - pin_priority "700" -end - -# COOK-2338 -apt_preference "dotdeb" do - glob "*" - pin "origin packages.dotdeb.org " - pin_priority "700" -end diff --git a/chef/cookbooks/aws/CHANGELOG.md b/chef/cookbooks/aws/CHANGELOG.md index 84f49ea..5ac09f4 100644 --- a/chef/cookbooks/aws/CHANGELOG.md +++ b/chef/cookbooks/aws/CHANGELOG.md @@ -3,6 +3,40 @@ aws Cookbook CHANGELOG This file is used to list changes made in each version of the aws cookbook. +v2.1.1 (2014-03-18) +------------------- +- [COOK-4415] disk_existing_raid resource name inconsistency + + +v2.1.0 (2014-02-25) +------------------- +### Improvement +- **[COOK-4008](https://tickets.opscode.com/browse/COOK-4008)** - Add name property for aws_elastic_ip LWRP + + +v2.0.0 (2014-02-19) +------------------- +[COOK-2755] Add allocate action to the elastic ip resource +[COOK-2829] Expose AWS credentials for ebs_raid LWRP as parameters +[COOK-2935] +[COOK-4213] Use use_inline_resources +[COOK-3467] Support IAM role +[COOK-4344] Add support for mounting existing raids and reusing volume +[COOK-3859] Add VPC support (allocation_id) to AWS elastic_ip LWRPJoseph Smith + + +v1.0.0 +------ +### Improvement +- [COOK-2829] -Expose AWS credentials for ebs_raid LWRP as parameters +- Changing attribute defaults begs a major version bump + + +v0.101.6 +-------- +### Bug +- **[COOK-3475](https://tickets.opscode.com/browse/COOK-3475)** - Fix an issuw were invoking action detach in the `ebs_volume `provider when the volume is already detached resulted in a failure + v0.101.4 -------- ### Improvement diff --git a/chef/cookbooks/aws/CONTRIBUTING b/chef/cookbooks/aws/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/aws/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/aws/README.md b/chef/cookbooks/aws/README.md index f3ee250..1cd49f9 100644 --- a/chef/cookbooks/aws/README.md +++ b/chef/cookbooks/aws/README.md @@ -39,9 +39,17 @@ AWS Credentials =============== In order to manage AWS components, authentication credentials need to -be available to the node. There are a number of ways to handle this, -such as node attributes or roles. We recommend storing these in a -databag (Chef 0.8+), and loading them in the recipe where the +be available to the node. There are 2 way to handle this: +1. explicitly pass credentials parameter to the resource +2. or let the resource pick up credentials from the IAM role assigned to the instance + + +## Using resource parameters + +To pass the credentials to the resource, credentials should be available to the node. +There are a number of ways to handle this, such as node attributes or Chef roles. + +We recommend storing these in a databag (Chef 0.8+), and loading them in the recipe where the resources are needed. DataBag recommendation: @@ -64,6 +72,59 @@ And to access the values: We'll look at specific usage below. +## Using IAM instance role + +If your instance has an IAM role, then the credentials can be automatically resolved by the cookbook +using Amazon instance metadata API. + +You can then omit the resource parameters `aws_secret_access_key` and `aws_access_key`. + +Of course, the instance role must have the required policies. Here is a sample policy for EBS volume +management: +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:AttachVolume", + "ec2:CreateVolume", + "ec2:ModifyVolumeAttribute", + "ec2:DescribeVolumeAttribute", + "ec2:DescribeVolumeStatus", + "ec2:DescribeVolumes", + "ec2:DetachVolume", + "ec2:EnableVolumeIO" + ], + "Sid": "Stmt1381536011000", + "Resource": [ + "*" + ], + "Effect": "Allow" + } + ] +} +``` + +For resource tags: +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:CreateTags" + ], + "Sid": "Stmt1381536708000", + "Resource": [ + "*" + ], + "Effect": "Allow" + } + ] +} +``` + Recipes ======= @@ -115,7 +176,7 @@ Actions: Attribute Parameters: * `aws_secret_access_key`, `aws_access_key` - passed to - `Opscode::AWS:Ec2` to authenticate, required. + `Opscode::AWS:Ec2` to authenticate required, unless using IAM roles for authentication. * `size` - size of the volume in gigabytes. * `snapshot_id` - snapshot to build EBS volume from. * most_recent_snapshot - use the most recent snapshot when creating a @@ -131,7 +192,8 @@ Attribute Parameters: snapshots to maintain. * `description` - used to set the description of an EBS snapshot * `volume_type` - "standard" or "io1" (io1 is the type for IOPS volume) -* `piops` - number of Provisioned IOPS to provision, must be > 100 +* `piops` - number of Provisioned IOPS to provision, must be >= 100 +* `existing_raid` - whether or not to assume the raid was previously assembled on existing volumes (default no) ## ebs_raid.rb @@ -139,6 +201,8 @@ Manage Elastic Block Store (EBS) raid devices with this resource. Attribute Parameters: +* `aws_secret_access_key`, `aws_access_key` - passed to + `Opscode::AWS:Ec2` to authenticate, required. * `mount_point` - where to mount the RAID volume * `mount_point_owner` - the owner of the mount point (default root) * `mount_point_group` - the group of the mount point (default root) @@ -165,7 +229,7 @@ Actions: Attribute Parameters: * `aws_secret_access_key`, `aws_access_key` - passed to - `Opscode::AWS:Ec2` to authenticate, required. + `Opscode::AWS:Ec2` to authenticate, required, unless using IAM roles for authentication. * `ip` - the IP address. * `timeout` - connection timeout for EC2 API. @@ -179,7 +243,7 @@ Actions: Attribute Parameters: * `aws_secret_access_key`, `aws_access_key` - passed to - `Opscode::AWS:Ec2` to authenticate, required. + `Opscode::AWS:Ec2` to authenticate, required, unless using IAM roles for authentication. * `name` - the name of the LB, required. ## resource_tag.rb @@ -197,7 +261,7 @@ Actions: Attribute Parameters * `aws_secret_access_key`, `aws_access_key` - passed to - `Opscode::AWS:Ec2` to authenticate, required. + `Opscode::AWS:Ec2` to authenticate, required, unless using IAM roles for authentication. * `tags` - a hash of key value pairs to be used as resource tags, (e.g. `{ "Name" => "foo", "Environment" => node.chef_environment }`,) required. @@ -299,7 +363,7 @@ For example, to register the node in the 'QA' ELB: more AWS resources, i.e. ec2 instances, ebs volumes or ebs volume snapshots. -Assigining tags to a node to reflect it's role and environment: +Assigning tags to a node to reflect it's role and environment: aws_resource_tag node['ec2']['instance_id'] do aws_access_key aws['aws_access_key_id'] @@ -320,6 +384,21 @@ disk set: "Environment" => node.chef_environment}) end +**Note** If you would like to use the normal attribute + `node['aws']['ebs_volume']['db_ebs_volume']['volume_id']` generated by the + aws_ebs_volume resource examples above as input for the resource_id attribute of + the aws_resource_tag resource, you must employ the "lazy" attribute feature + from Chef 10.28 or Chef 11.x and higher. "lazy" will delay the + evaluation of the resource_id attribute's value until the normal/set node attribute is + available. + +``` ruby +aws_resource_tag "db_ebs_volume" do + resource_id lazy { node['aws']['ebs_volume']['db_ebs_volume']['volume_id'] } + tags ({"Service" => "Frontend"}) +end +``` + ## aws_s3_file `s3_file` can be used to download a file from s3 that requires aws authorization. This diff --git a/chef/cookbooks/aws/attributes/default.rb b/chef/cookbooks/aws/attributes/default.rb index eb65095..3485e34 100644 --- a/chef/cookbooks/aws/attributes/default.rb +++ b/chef/cookbooks/aws/attributes/default.rb @@ -18,5 +18,5 @@ # default['aws']['right_aws_version'] = "3.0.5" -default['aws']['databag_name'] = "aws" -default['aws']['databag_entry'] = "main" +default['aws']['databag_name'] = nil +default['aws']['databag_entry'] = nil diff --git a/chef/cookbooks/aws/libraries/ec2.rb b/chef/cookbooks/aws/libraries/ec2.rb index 23de193..406d919 100644 --- a/chef/cookbooks/aws/libraries/ec2.rb +++ b/chef/cookbooks/aws/libraries/ec2.rb @@ -48,7 +48,14 @@ module Opscode region = instance_availability_zone region = region[0, region.length-1] - @@ec2 ||= RightAws::Ec2.new(new_resource.aws_access_key, new_resource.aws_secret_access_key, { :logger => Chef::Log, :region => region }) + + if new_resource.aws_access_key and new_resource.aws_secret_access_key + @@ec2 ||= RightAws::Ec2.new(new_resource.aws_access_key, new_resource.aws_secret_access_key, {:logger => Chef::Log, :region => region}) + else + creds = query_role_credentials + @@ec2 ||= RightAws::Ec2.new(creds['AccessKeyId'], creds['SecretAccessKey'], {:logger => Chef::Log, :region => region, :token => creds['Token']}) + end + end def instance_id @@ -61,6 +68,18 @@ module Opscode private + def query_role + r = open("http://169.254.169.254/latest/meta-data/iam/security-credentials/").readlines.first + r + end + + def query_role_credentials(role = query_role) + fail "Instance has no IAM role." if role.to_s.empty? + creds = open("http://169.254.169.254/latest/meta-data/iam/security-credentials/#{role}"){|f| JSON.parse(f.string)} + Chef::Log.debug("Retrieved instance credentials for IAM role #{role}") + creds + end + def query_instance_id instance_id = open('http://169.254.169.254/latest/meta-data/instance-id'){|f| f.gets} raise "Cannot find instance id!" unless instance_id diff --git a/chef/cookbooks/aws/metadata.json b/chef/cookbooks/aws/metadata.json new file mode 100644 index 0000000..66c0731 --- /dev/null +++ b/chef/cookbooks/aws/metadata.json @@ -0,0 +1,30 @@ +{ + "name": "aws", + "version": "2.1.1", + "description": "LWRPs for managing AWS resources", + "long_description": "Description\n===========\n\nThis cookbook provides libraries, resources and providers to configure\nand manage Amazon Web Services components and offerings with the EC2\nAPI. Currently supported resources:\n\n* EBS Volumes (`ebs_volume`)\n* EBS Raid (`ebs_raid`)\n* Elastic IPs (`elastic_ip`)\n* Elastic Load Balancer (`elastic_lb`)\n* AWS Resource Tags (`resource_tag`)\n\nUnsupported AWS resources that have other cookbooks include but are\nnot limited to:\n\n* [Route53](http://community.opscode.com/cookbooks/route53)\n\n**Note** This cookbook uses the `right_aws` RubyGem to interact with\n the AWS API because at the time it was written, `fog` and `aws-sdk`\n were not available. Further, both of those gems require `nokogiri`\n which requires compiling native extensions, which means build tools\n are required. We do not plan at this time to change the underlying\n Ruby library used in order to limit the external dependencies for\n this cookbook.\n\nRequirements\n============\n\nRequires Chef 0.7.10 or higher for Lightweight Resource and Provider\nsupport. Chef 0.8+ is recommended. While this cookbook can be used in\n`chef-solo` mode, to gain the most flexibility, we recommend using\n`chef-client` with a Chef Server.\n\nAn Amazon Web Services account is required. The Access Key and Secret\nAccess Key are used to authenticate with EC2.\n\nAWS Credentials\n===============\n\nIn order to manage AWS components, authentication credentials need to\nbe available to the node. There are 2 way to handle this:\n1. explicitly pass credentials parameter to the resource\n2. or let the resource pick up credentials from the IAM role assigned to the instance\n\n\n## Using resource parameters\n\nTo pass the credentials to the resource, credentials should be available to the node.\nThere are a number of ways to handle this, such as node attributes or Chef roles.\n\nWe recommend storing these in a databag (Chef 0.8+), and loading them in the recipe where the\nresources are needed.\n\nDataBag recommendation:\n\n % knife data bag show aws main\n {\n \"id\": \"main\",\n \"aws_access_key_id\": \"YOUR_ACCESS_KEY\",\n \"aws_secret_access_key\": \"YOUR_SECRET_ACCESS_KEY\"\n }\n\nThis can be loaded in a recipe with:\n\n aws = data_bag_item(\"aws\", \"main\")\n\nAnd to access the values:\n\n aws['aws_access_key_id']\n aws['aws_secret_access_key']\n\nWe'll look at specific usage below.\n\n## Using IAM instance role\n\nIf your instance has an IAM role, then the credentials can be automatically resolved by the cookbook\nusing Amazon instance metadata API.\n\nYou can then omit the resource parameters `aws_secret_access_key` and `aws_access_key`.\n\nOf course, the instance role must have the required policies. Here is a sample policy for EBS volume\nmanagement:\n```json\n{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": [\n \"ec2:AttachVolume\",\n \"ec2:CreateVolume\",\n \"ec2:ModifyVolumeAttribute\",\n \"ec2:DescribeVolumeAttribute\",\n \"ec2:DescribeVolumeStatus\",\n \"ec2:DescribeVolumes\",\n \"ec2:DetachVolume\",\n \"ec2:EnableVolumeIO\"\n ],\n \"Sid\": \"Stmt1381536011000\",\n \"Resource\": [\n \"*\"\n ],\n \"Effect\": \"Allow\"\n }\n ]\n}\n```\n\nFor resource tags:\n```json\n{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": [\n \"ec2:CreateTags\"\n ],\n \"Sid\": \"Stmt1381536708000\",\n \"Resource\": [\n \"*\"\n ],\n \"Effect\": \"Allow\"\n }\n ]\n}\n```\n\nRecipes\n=======\n\ndefault.rb\n----------\n\nThe default recipe installs the `right_aws` RubyGem, which this\ncookbook requires in order to work with the EC2 API. Make sure that\nthe aws recipe is in the node or role `run_list` before any resources\nfrom this cookbook are used.\n\n \"run_list\": [\n \"recipe[aws]\"\n ]\n\nThe `gem_package` is created as a Ruby Object and thus installed\nduring the Compile Phase of the Chef run.\n\nLibraries\n=========\n\nThe cookbook has a library module, `Opscode::AWS::Ec2`, which can be\nincluded where necessary:\n\n include Opscode::Aws::Ec2\n\nThis is needed in any providers in the cookbook. Along with some\nhelper methods used in the providers, it sets up a class variable,\n`ec2` that is used along with the access and secret access keys\n\nResources and Providers\n=======================\n\nThis cookbook provides two resources and corresponding providers.\n\n## ebs_volume.rb\n\n\nManage Elastic Block Store (EBS) volumes with this resource.\n\nActions:\n\n* `create` - create a new volume.\n* `attach` - attach the specified volume.\n* `detach` - detach the specified volume.\n* `snapshot` - create a snapshot of the volume.\n* `prune` - prune snapshots.\n\nAttribute Parameters:\n\n* `aws_secret_access_key`, `aws_access_key` - passed to\n `Opscode::AWS:Ec2` to authenticate required, unless using IAM roles for authentication.\n* `size` - size of the volume in gigabytes.\n* `snapshot_id` - snapshot to build EBS volume from.\n* most_recent_snapshot - use the most recent snapshot when creating a\n volume from an existing volume (defaults to false)\n* `availability_zone` - EC2 region, and is normally automatically\n detected.\n* `device` - local block device to attach the volume to, e.g.\n `/dev/sdi` but no default value, required.\n* `volume_id` - specify an ID to attach, cannot be used with action\n `:create` because AWS assigns new volume IDs\n* `timeout` - connection timeout for EC2 API.\n* `snapshots_to_keep` - used with action `:prune` for number of\n snapshots to maintain.\n* `description` - used to set the description of an EBS snapshot\n* `volume_type` - \"standard\" or \"io1\" (io1 is the type for IOPS volume)\n* `piops` - number of Provisioned IOPS to provision, must be >= 100\n* `existing_raid` - whether or not to assume the raid was previously assembled on existing volumes (default no)\n\n## ebs_raid.rb\n\nManage Elastic Block Store (EBS) raid devices with this resource.\n\nAttribute Parameters:\n\n* `aws_secret_access_key`, `aws_access_key` - passed to\n `Opscode::AWS:Ec2` to authenticate, required.\n* `mount_point` - where to mount the RAID volume\n* `mount_point_owner` - the owner of the mount point (default root)\n* `mount_point_group` - the group of the mount point (default root)\n* `mount_point_mode` - the file mode of the mount point (default 0755)\n* `disk_count` - number of EBS volumes to raid\n* `disk_size` - size of EBS volumes to raid\n* `level` - RAID level (default 10)\n* `filesystem` - filesystem to format raid array (default ext4)\n* `snapshots` - array of EBS snapshots to restore. Snapshots must be\n taken using an ec2 consistent snapshot tool, and tagged with a\n number that indicates how many devices are in the array being backed\n up (e.g. \"Logs Backup [0-4]\" for a four-volume raid array snapshot)\n* `disk_type` - \"standard\" or \"io1\" (io1 is the type for IOPS volume)\n* `disk_piops` - number of Provisioned IOPS to provision per disk,\n must be > 100\n\n## elastic_ip.rb\n\nActions:\n\n* `associate` - associate the IP.\n* `disassociate` - disassociate the IP.\n\nAttribute Parameters:\n\n* `aws_secret_access_key`, `aws_access_key` - passed to\n `Opscode::AWS:Ec2` to authenticate, required, unless using IAM roles for authentication.\n* `ip` - the IP address.\n* `timeout` - connection timeout for EC2 API.\n\n## elastic_lb.rb\n\nActions:\n\n* `register` - Add this instance to the LB\n* `deregister` - Remove this instance from the LB\n\nAttribute Parameters:\n\n* `aws_secret_access_key`, `aws_access_key` - passed to\n `Opscode::AWS:Ec2` to authenticate, required, unless using IAM roles for authentication.\n* `name` - the name of the LB, required.\n\n## resource_tag.rb\n\nActions:\n\n* `add` - Add tags to a resource.\n* `update` - Add or modify existing tags on a resource -- this is the\n default action.\n* `remove` - Remove tags from a resource, but only if the specified\n values match the existing ones.\n* `force_remove` - Remove tags from a resource, regardless of their\n values.\n\nAttribute Parameters\n\n* `aws_secret_access_key`, `aws_access_key` - passed to\n `Opscode::AWS:Ec2` to authenticate, required, unless using IAM roles for authentication.\n* `tags` - a hash of key value pairs to be used as resource tags,\n (e.g. `{ \"Name\" => \"foo\", \"Environment\" => node.chef_environment\n }`,) required.\n* `resource_id` - resources whose tags will be modified. The value may\n be a single ID as a string or multiple IDs in an array. If no\n `resource_id` is specified the name attribute will be used.\n\nUsage\n=====\n\nThe following examples assume that the recommended data bag item has\nbeen created and that the following has been included at the top of\nthe recipe where they are used.\n\n include_recipe \"aws\"\n aws = data_bag_item(\"aws\", \"main\")\n\n## aws_ebs_volume\n\nThe resource only handles manipulating the EBS volume, additional\nresources need to be created in the recipe to manage the attached\nvolume as a filesystem or logical volume.\n\n aws_ebs_volume \"db_ebs_volume\" do\n aws_access_key aws['aws_access_key_id']\n aws_secret_access_key aws['aws_secret_access_key']\n size 50\n device \"/dev/sdi\"\n action [ :create, :attach ]\n end\n\nThis will create a 50G volume, attach it to the instance as `/dev/sdi`.\n\n aws_ebs_volume \"db_ebs_volume_from_snapshot\" do\n aws_access_key aws['aws_access_key_id']\n aws_secret_access_key aws['aws_secret_access_key']\n size 50\n device \"/dev/sdi\"\n snapshot_id \"snap-ABCDEFGH\"\n action [ :create, :attach ]\n end\n\nThis will create a new 50G volume from the snapshot ID provided and\nattach it as `/dev/sdi`.\n\n## aws_elastic_ip\n\nThe `elastic_ip` resource provider does not support allocating new\nIPs. This must be done before running a recipe that uses the resource.\nAfter allocating a new Elastic IP, we recommend storing it in a\ndatabag and loading the item in the recipe.\n\nDatabag structure:\n\n % knife data bag show aws eip_load_balancer_production\n {\n \"id\": \"eip_load_balancer_production\",\n \"public_ip\": \"YOUR_ALLOCATED_IP\"\n }\n\nThen to set up the Elastic IP on a system:\n\n ip_info = data_bag_item(\"aws\", \"eip_load_balancer_production\")\n\n aws_elastic_ip \"eip_load_balancer_production\" do\n aws_access_key aws['aws_access_key_id']\n aws_secret_access_key aws['aws_secret_access_key']\n ip ip_info['public_ip']\n action :associate\n end\n\nThis will use the loaded `aws` and `ip_info` databags to pass the\nrequired values into the resource to configure. Note that when\nassociating an Elastic IP to an instance, connectivity to the instance\nwill be lost because the public IP address is changed. You will need\nto reconnect to the instance with the new IP.\n\nYou can also store this in a role as an attribute or assign to the\nnode directly, if preferred.\n\n## aws_elastic_lb\n\n`elastic_lb` opererates similar to `elastic_ip'. Make sure that you've\ncreated the ELB and enabled your instances' availability zones prior\nto using this provider.\n\nFor example, to register the node in the 'QA' ELB:\n\n aws_elastic_lb \"elb_qa\" do\n aws_access_key aws['aws_access_key_id']\n aws_secret_access_key aws['aws_secret_access_key']\n name \"QA\"\n action :register\n end\n\n## aws_resource_tag\n\n`resource_tag` can be used to manipulate the tags assigned to one or\nmore AWS resources, i.e. ec2 instances, ebs volumes or ebs volume\nsnapshots.\n\nAssigning tags to a node to reflect it's role and environment:\n\n aws_resource_tag node['ec2']['instance_id'] do\n aws_access_key aws['aws_access_key_id']\n aws_secret_access_key aws['aws_secret_access_key']\n tags({\"Name\" => \"www.example.com app server\",\n \"Environment\" => node.chef_environment})\n action :update\n end\n\nAssigning a set of tags to multiple resources, e.g. ebs volumes in a\ndisk set:\n\n aws_resource_tag 'my awesome raid set' do\n aws_access_key aws['aws_access_key_id']\n aws_secret_access_key aws['aws_secret_access_key']\n resource_id [ \"vol-d0518cb2\", \"vol-fad31a9a\", \"vol-fb106a9f\", \"vol-74ed3b14\" ]\n tags({\"Name\" => \"My awesome RAID disk set\",\n \"Environment\" => node.chef_environment})\n end\n\n**Note** If you would like to use the normal attribute\n `node['aws']['ebs_volume']['db_ebs_volume']['volume_id']` generated by the\n aws_ebs_volume resource examples above as input for the resource_id attribute of\n the aws_resource_tag resource, you must employ the \"lazy\" attribute feature\n from Chef 10.28 or Chef 11.x and higher. \"lazy\" will delay the\n evaluation of the resource_id attribute's value until the normal/set node attribute is\n available.\n\n``` ruby\naws_resource_tag \"db_ebs_volume\" do\n resource_id lazy { node['aws']['ebs_volume']['db_ebs_volume']['volume_id'] }\n tags ({\"Service\" => \"Frontend\"})\nend\n```\n\n## aws_s3_file\n\n`s3_file` can be used to download a file from s3 that requires aws authorization. This\nis a wrapper around `remote_file` and supports the same resource attributes as `remote_file`.\n\n aws_s3_file \"/tmp/foo\" do\n bucket \"i_haz_an_s3_buckit\"\n remote_path \"path/in/s3/bukket/to/foo\"\n aws_access_key_id aws['aws_access_key_id']\n aws_secret_access_key aws['aws_secret_access_key']\n end\n\n\nLicense and Author\n==================\n\n* Author:: Chris Walters ()\n* Author:: AJ Christensen ()\n* Author:: Justin Huff ()\n\nCopyright 2009-2013, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + }, + "dependencies": { + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "aws": "Installs the right_aws gem during compile time" + } +} \ No newline at end of file diff --git a/chef/cookbooks/aws/metadata.rb b/chef/cookbooks/aws/metadata.rb index 1ab7ecd..b8432bc 100644 --- a/chef/cookbooks/aws/metadata.rb +++ b/chef/cookbooks/aws/metadata.rb @@ -4,5 +4,5 @@ maintainer_email "cookbooks@opscode.com" license "Apache 2.0" description "LWRPs for managing AWS resources" long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "0.101.4" +version "2.1.1" recipe "aws", "Installs the right_aws gem during compile time" diff --git a/chef/cookbooks/aws/providers/ebs_raid.rb b/chef/cookbooks/aws/providers/ebs_raid.rb index dba3900..0758c16 100644 --- a/chef/cookbooks/aws/providers/ebs_raid.rb +++ b/chef/cookbooks/aws/providers/ebs_raid.rb @@ -34,7 +34,8 @@ action :auto_attach do @new_resource.filesystem_options, @new_resource.snapshots, @new_resource.disk_type, - @new_resource.disk_piops) + @new_resource.disk_piops, + @new_resource.existing_raid) @new_resource.updated_by_last_action(true) end @@ -90,7 +91,7 @@ def update_node_from_md_device(md_device, mount_point) node.set[:aws][:raid][mount_point][:raid_dev] = md_device.sub(/\/dev\//,"") node.set[:aws][:raid][mount_point][:devices] = raid_devices - node.save + node.save unless Chef::Config[:solo] end # Dumb way to look for mounted raid devices. Assumes that the machine @@ -221,18 +222,15 @@ def mount_volumes(device_vol_map) attach_volume(dev_device, device_vol_map[dev_device]) end + correct_devices = correct_device_map(device_vol_map).keys.map { |dev| "/dev/#{dev}" } + # Wait until all volumes are mounted ruby_block "wait_#{new_resource.name}" do block do - count = 0 - begin - Chef::Log.info("sleeping 10 seconds until EBS volumes have re-attached") - sleep 10 - count += 1 - end while !device_vol_map.all? {|dev_path| ::File.exists?(dev_path) } - - # Accounting to see how often this code actually gets used. - node.set[:aws][:raid][mount_point][:device_attach_delay] = count * 10 + while not correct_devices.all? { |dev_path| ::File.exists?(dev_path) } + Chef::Log.info("sleeping 3 seconds until EBS volumes have re-attached") + sleep 3 + end end end end @@ -295,13 +293,12 @@ end def attach_volume(disk_dev, volume_id) disk_dev_path = "/dev/#{disk_dev}" - aws = data_bag_item(node['aws']['databag_name'], node['aws']['databag_entry']) - Chef::Log.info("Attaching existing ebs volume id #{volume_id} for device #{disk_dev_path}") + creds = aws_creds() # cannot be invoked inside the block aws_ebs_volume disk_dev_path do - aws_access_key aws['aws_access_key_id'] - aws_secret_access_key aws['aws_secret_access_key'] + aws_access_key creds['aws_access_key_id'] + aws_secret_access_key creds['aws_secret_access_key'] device disk_dev_path name disk_dev volume_id volume_id @@ -320,7 +317,7 @@ end # If it's not nil, must have exactly elements def create_raid_disks(mount_point, mount_point_owner, mount_point_group, mount_point_mode, num_disks, disk_size, - level, filesystem, filesystem_options, snapshots, disk_type, disk_piops) + level, filesystem, filesystem_options, snapshots, disk_type, disk_piops, existing_raid ) creating_from_snapshot = !(snapshots.nil? || snapshots.size == 0) @@ -337,12 +334,11 @@ def create_raid_disks(mount_point, mount_point_owner, mount_point_group, mount_p disk_dev_path = "#{disk_dev}#{i}" - aws = data_bag_item(node['aws']['databag_name'], node['aws']['databag_entry']) - Chef::Log.info "Snapshot array is #{snapshots[i-1]}" + creds = aws_creds() # cannot be invoked inside the block aws_ebs_volume disk_dev_path do - aws_access_key aws['aws_access_key_id'] - aws_secret_access_key aws['aws_secret_access_key'] + aws_access_key creds['aws_access_key_id'] + aws_secret_access_key creds['aws_secret_access_key'] size disk_size volume_type disk_type piops disk_piops @@ -372,11 +368,11 @@ def create_raid_disks(mount_point, mount_point_owner, mount_point_group, mount_p devices_string = device_map_to_string(devices) Chef::Log.info("finished sorting devices #{devices_string}") - if not creating_from_snapshot + if not creating_from_snapshot and not existing_raid # Create the raid device on our system execute "creating raid device" do Chef::Log.info("creating raid device /dev/#{raid_dev} with raid devices #{devices_string}") - command "mdadm --create /dev/#{raid_dev} --level=#{level} --raid-devices=#{devices.size} #{devices_string}" + command "[ -b /dev/#{raid_dev} ] && mdadm --stop /dev/#{raid_dev} ; yes | mdadm --create /dev/#{raid_dev} --level=#{level} --raid-devices=#{devices.size} #{devices_string}" end # NOTE: must be a better way. @@ -428,8 +424,21 @@ def create_raid_disks(mount_point, mount_point_owner, mount_point_group, mount_p # Assemble all the data bag meta data node.set[:aws][:raid][mount_point][:raid_dev] = raid_dev node.set[:aws][:raid][mount_point][:device_map] = devices - node.save + node.save unless Chef::Config[:solo] end end end + +def aws_creds + h = {} + if new_resource.aws_access_key && new_resource.aws_secret_access_key + h['aws_access_key_id'] = new_resource.aws_access_key + h['aws_secret_access_key'] = new_resource.aws_secret_access_key + elsif node['aws']['databag_name'] && node['aws']['databag_entry'] + Chef::Log.warn("DEPRECATED: node['aws']['databag_name'] and node['aws']['databag_entry'] are deprecated. Use LWRP parameters instead.") + h = data_bag_item(node['aws']['databag_name'], node['aws']['databag_entry']) + end + h +end + diff --git a/chef/cookbooks/aws/providers/ebs_volume.rb b/chef/cookbooks/aws/providers/ebs_volume.rb index 18fda93..7bd21e7 100644 --- a/chef/cookbooks/aws/providers/ebs_volume.rb +++ b/chef/cookbooks/aws/providers/ebs_volume.rb @@ -72,7 +72,6 @@ end action :detach do vol = determine_volume - return if vol[:aws_instance_id] != instance_id converge_by("detach volume with id: #{vol[:aws_id]}") do detach_volume(vol[:aws_id], new_resource.timeout) end @@ -158,7 +157,7 @@ def create_volume(snapshot_id, size, availability_zone, timeout, volume_type, pi # PIOPs requested. Must specify an iops param and probably won't be "low". if volume_type == 'io1' - raise 'IOPS value not specified.' unless piops > 100 + raise 'IOPS value not specified.' unless piops >= 100 end # Shouldn't see non-zero piops param without appropriate type. @@ -232,8 +231,12 @@ end # Detaches the volume and blocks until done (or times out) def detach_volume(volume_id, timeout) - Chef::Log.debug("Detaching #{volume_id}") vol = volume_by_id(volume_id) + if vol[:aws_instance_id] != instance_id + Chef::Log.debug("EBS Volume #{volume_id} is not attached to this instance (attached to #{vol[:aws_instance_id]}). Skipping...") + return + end + Chef::Log.debug("Detaching #{volume_id}") orig_instance_id = vol[:aws_instance_id] ec2.detach_volume(volume_id) diff --git a/chef/cookbooks/aws/providers/elastic_ip.rb b/chef/cookbooks/aws/providers/elastic_ip.rb index b538152..2793f01 100644 --- a/chef/cookbooks/aws/providers/elastic_ip.rb +++ b/chef/cookbooks/aws/providers/elastic_ip.rb @@ -6,31 +6,49 @@ def whyrun_supported? end action :associate do - addr = address(new_resource.ip) + ip = new_resource.ip || node['aws']['elastic_ip'][new_resource.name]['ip'] + addr = address(ip) if addr.nil? - raise "Elastic IP #{new_resource.ip} does not exist" + raise "Elastic IP #{ip} does not exist" elsif addr[:instance_id] == instance_id - Chef::Log.debug("Elastic IP #{new_resource.ip} is already attached to the instance") + Chef::Log.debug("Elastic IP #{ip} is already attached to the instance") else - converge_by("attach Elastic IP #{new_resource.ip} to the instance") do - Chef::Log.info("Attaching Elastic IP #{new_resource.ip} to the instance") - attach(new_resource.ip, new_resource.timeout) + converge_by("attach Elastic IP #{ip} to the instance") do + Chef::Log.info("Attaching Elastic IP #{ip} to the instance") + attach(ip, new_resource.timeout) + node.set['aws']['elastic_ip'][new_resource.name]['ip'] = ip + node.save unless Chef::Config[:solo] end end end action :disassociate do - addr = address(new_resource.ip) + ip = new_resource.ip || node['aws']['elastic_ip'][new_resource.name]['ip'] + addr = address(ip) if addr.nil? - Chef::Log.debug("Elastic IP #{new_resource.ip} does not exist, so there is nothing to detach") + Chef::Log.debug("Elastic IP #{ip} does not exist, so there is nothing to detach") elsif addr[:instance_id] != instance_id - Chef::Log.debug("Elastic IP #{new_resource.ip} is already detached from the instance") + Chef::Log.debug("Elastic IP #{ip} is already detached from the instance") else - converge_by("detach Elastic IP #{new_resource.ip} from the instance") do - Chef::Log.info("Detaching Elastic IP #{new_resource.ip} from the instance") - detach(new_resource.ip, new_resource.timeout) + converge_by("detach Elastic IP #{ip} from the instance") do + Chef::Log.info("Detaching Elastic IP #{ip} from the instance") + detach(ip, new_resource.timeout) + end + end +end + +action :allocate do + current_elastic_ip = node['aws']['elastic_ip'][new_resource.name]['ip'] + if current_elastic_ip + Chef::Log.info("An Elastic IP was already allocated for #{new_resource.name} #{current_elastic_ip} from the instance") + else + converge_by("allocate new Elastic IP for #{new_resource.name}") do + addr = ec2.allocate_address + Chef::Log.info("Allocated Elastic IP #{addr[:public_ip]} from the instance") + node.set['aws']['elastic_ip'][new_resource.name]['ip'] = addr[:public_ip] + node.save unless Chef::Config[:solo] end end end @@ -38,11 +56,16 @@ end private def address(ip) - ec2.describe_addresses.find{|a| a[:public_ip] == ip} + ec2.describe_addresses.find { |a| a[:public_ip] == ip } end def attach(ip, timeout) - ec2.associate_address(instance_id, {:public_ip => ip}) + addr = address(ip) + if addr[:domain] == 'vpc' + ec2.associate_address(instance_id, {:allocation_id => addr[:allocation_id]}) + else + ec2.associate_address(instance_id, {:public_ip => addr[:public_ip]}) + end # block until attached begin diff --git a/chef/cookbooks/aws/providers/s3_file.rb b/chef/cookbooks/aws/providers/s3_file.rb index 0aa4bea..432c6ca 100644 --- a/chef/cookbooks/aws/providers/s3_file.rb +++ b/chef/cookbooks/aws/providers/s3_file.rb @@ -1,4 +1,6 @@ +use_inline_resources if defined?(use_inline_resources) + def whyrun_supported? true end @@ -31,7 +33,7 @@ def do_s3_file(resource_action) s3url = RightAws::S3Interface.new(new_resource.aws_access_key_id, new_resource.aws_secret_access_key).get_link(new_resource.bucket, remote_path) - r = remote_file new_resource.name do + remote_file new_resource.name do path new_resource.path source s3url owner new_resource.owner @@ -54,5 +56,4 @@ def do_s3_file(resource_action) manage_symlink_source new_resource.manage_symlink_source end end - new_resource.updated_by_last_action(r.updated_by_last_action?) end diff --git a/chef/cookbooks/aws/resources/ebs_raid.rb b/chef/cookbooks/aws/resources/ebs_raid.rb index 1ae486c..eed0b6f 100644 --- a/chef/cookbooks/aws/resources/ebs_raid.rb +++ b/chef/cookbooks/aws/resources/ebs_raid.rb @@ -2,16 +2,34 @@ actions :auto_attach default_action :auto_attach -attribute :mount_point, :kind_of => String -attribute :mount_point_owner, :kind_of => String, :default => 'root' -attribute :mount_point_group, :kind_of => String, :default => 'root' -attribute :mount_point_mode, :kind_of => String, :default => 00755 -attribute :disk_count, :kind_of => Integer -attribute :disk_size, :kind_of => Integer -attribute :level, :default => 10 -attribute :filesystem, :default => "ext4" -attribute :filesystem_options, :default => "rw,noatime,nobootwait" -attribute :snapshots, :default => [] -attribute :disk_type, :kind_of => String, :default => 'standard' -attribute :disk_piops, :kind_of => Integer, :default => 0 +state_attrs :aws_access_key, + :disk_count, + :disk_piops, + :disk_size, + :disk_type, + :filesystem, + :filesystem_options, + :level, + :mount_point, + :mount_point_group, + :mount_point_mode, + :mount_point_owner, + :snapshots + + +attribute :aws_access_key, :kind_of => String +attribute :aws_secret_access_key, :kind_of => String +attribute :mount_point, :kind_of => String +attribute :mount_point_owner, :kind_of => String, :default => 'root' +attribute :mount_point_group, :kind_of => String, :default => 'root' +attribute :mount_point_mode, :kind_of => String, :default => 00755 +attribute :disk_count, :kind_of => Integer +attribute :disk_size, :kind_of => Integer +attribute :level, :default => 10 +attribute :filesystem, :default => "ext4" +attribute :filesystem_options, :default => "rw,noatime,nobootwait" +attribute :snapshots, :default => [] +attribute :disk_type, :kind_of => String, :default => 'standard' +attribute :disk_piops, :kind_of => Integer, :default => 0 +attribute :existing_raid, :kind_of => [ TrueClass, FalseClass ] diff --git a/chef/cookbooks/aws/resources/ebs_volume.rb b/chef/cookbooks/aws/resources/ebs_volume.rb index 0329eee..b6aaf30 100644 --- a/chef/cookbooks/aws/resources/ebs_volume.rb +++ b/chef/cookbooks/aws/resources/ebs_volume.rb @@ -1,5 +1,18 @@ actions :create, :attach, :detach, :snapshot, :prune +state_attrs :availability_zone, + :aws_access_key, + :description, + :device, + :most_recent_snapshot, + :piops, + :size, + :snapshot_id, + :snapshots_to_keep, + :timeout, + :volume_id, + :volume_type + attribute :aws_access_key, :kind_of => String attribute :aws_secret_access_key, :kind_of => String attribute :size, :kind_of => Integer diff --git a/chef/cookbooks/aws/resources/elastic_ip.rb b/chef/cookbooks/aws/resources/elastic_ip.rb index 3ff8954..d3d723c 100644 --- a/chef/cookbooks/aws/resources/elastic_ip.rb +++ b/chef/cookbooks/aws/resources/elastic_ip.rb @@ -1,8 +1,12 @@ -actions :associate, :disassociate +actions :associate, :disassociate, :allocate + +state_attrs :aws_access_key, + :ip, + :timeout attribute :aws_access_key, :kind_of => String attribute :aws_secret_access_key, :kind_of => String -attribute :ip, :kind_of => String +attribute :ip, :kind_of => String, :name_attribute => true attribute :timeout, :default => 3*60 # 3 mins, nil or 0 for no timeout def initialize(*args) diff --git a/chef/cookbooks/aws/resources/elastic_lb.rb b/chef/cookbooks/aws/resources/elastic_lb.rb index 81d95dd..54ea584 100644 --- a/chef/cookbooks/aws/resources/elastic_lb.rb +++ b/chef/cookbooks/aws/resources/elastic_lb.rb @@ -1,5 +1,8 @@ actions :register, :deregister +state_attrs :aws_access_key, + :name + attribute :aws_access_key, :kind_of => String attribute :aws_secret_access_key, :kind_of => String attribute :name, :kind_of => String diff --git a/chef/cookbooks/aws/resources/resource_tag.rb b/chef/cookbooks/aws/resources/resource_tag.rb index dd1375f..cd8bed7 100644 --- a/chef/cookbooks/aws/resources/resource_tag.rb +++ b/chef/cookbooks/aws/resources/resource_tag.rb @@ -5,7 +5,11 @@ end actions :add, :update, :remove, :force_remove -attribute :aws_access_key, :kind_of => String, :required => true -attribute :aws_secret_access_key, :kind_of => String, :required => true +state_attrs :aws_access_key, + :resource_id, + :tags + +attribute :aws_access_key, :kind_of => String +attribute :aws_secret_access_key, :kind_of => String attribute :resource_id, :kind_of => [ String, Array ], :regex => /(i|snap|vol)-[a-zA-Z0-9]+/ attribute :tags, :kind_of => Hash, :required => true diff --git a/chef/cookbooks/aws/resources/s3_file.rb b/chef/cookbooks/aws/resources/s3_file.rb index e17dbca..9f8f20d 100644 --- a/chef/cookbooks/aws/resources/s3_file.rb +++ b/chef/cookbooks/aws/resources/s3_file.rb @@ -1,4 +1,15 @@ actions :create, :create_if_missing, :touch, :delete + +state_attrs :aws_access_key_id, + :backup, + :bucket, + :checksum, + :group, + :mode, + :owner, + :path, + :remote_path + attribute :path, :kind_of => String, :name_attribute => true attribute :remote_path, :kind_of => String attribute :bucket, :kind_of => String diff --git a/chef/cookbooks/build-essential/.kitchen.yml b/chef/cookbooks/build-essential/.kitchen.yml deleted file mode 100644 index 9fef833..0000000 --- a/chef/cookbooks/build-essential/.kitchen.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: debian-6 - driver_config: - box: opscode-debian-6 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-6.0.7_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-13.04 - driver_config: - box: opscode-ubuntu-13.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-13.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-12.10 - driver_config: - box: opscode-ubuntu-12.10 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.10_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: - - recipe[apt] - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - -suites: -- name: default - run_list: - - recipe[build-essential] diff --git a/chef/cookbooks/build-essential/CONTRIBUTING b/chef/cookbooks/build-essential/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/build-essential/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/build-essential/LICENSE b/chef/cookbooks/build-essential/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/build-essential/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/build-essential/TESTING.md b/chef/cookbooks/build-essential/TESTING.md deleted file mode 100644 index e29ff7c..0000000 --- a/chef/cookbooks/build-essential/TESTING.md +++ /dev/null @@ -1,25 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test diff --git a/chef/cookbooks/build-essential/attributes/default.rb b/chef/cookbooks/build-essential/attributes/default.rb index 70bebcf..76b31df 100644 --- a/chef/cookbooks/build-essential/attributes/default.rb +++ b/chef/cookbooks/build-essential/attributes/default.rb @@ -17,7 +17,6 @@ # limitations under the License. # -#default['build_essential']['compiletime'] = false default['build_essential']['compiletime'] = true case node['platform_family'] diff --git a/chef/cookbooks/build-essential/metadata.json b/chef/cookbooks/build-essential/metadata.json new file mode 100644 index 0000000..5d2a556 --- /dev/null +++ b/chef/cookbooks/build-essential/metadata.json @@ -0,0 +1 @@ +{"name":"build-essential","description":"Installs C compiler / build tools","long_description":"","maintainer":"Opscode, Inc.","maintainer_email":"cookbooks@opscode.com","license":"Apache 2.0","platforms":{"fedora":">= 0.0.0","redhat":">= 0.0.0","centos":">= 0.0.0","ubuntu":">= 0.0.0","debian":">= 0.0.0","amazon":">= 0.0.0","suse":">= 0.0.0","scientific":">= 0.0.0","oracle":">= 0.0.0","smartos":">= 0.0.0","mac_os_x":">= 10.6.0","mac_os_x_server":">= 10.6.0"},"dependencies":{},"recommendations":{},"suggestions":{"pkgutil":">= 0.0.0"},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{"build-essential":"Installs packages required for compiling C software from source."},"version":"1.4.2"} \ No newline at end of file diff --git a/chef/cookbooks/build-essential/Berksfile b/chef/cookbooks/ceph/Berksfile similarity index 75% rename from chef/cookbooks/build-essential/Berksfile rename to chef/cookbooks/ceph/Berksfile index f08b074..db8f00b 100644 --- a/chef/cookbooks/build-essential/Berksfile +++ b/chef/cookbooks/ceph/Berksfile @@ -3,5 +3,5 @@ site :opscode metadata group :integration do - cookbook "apt" + cookbook 'apt' end diff --git a/chef/cookbooks/ceph/CHANGELOG.md b/chef/cookbooks/ceph/CHANGELOG.md new file mode 100644 index 0000000..bd9bf1c --- /dev/null +++ b/chef/cookbooks/ceph/CHANGELOG.md @@ -0,0 +1,17 @@ +ceph +==== + +v0.2.0 (2014-03-03) +------------------- + +- Add tests and fixes related. +- Add ceph-extra +- Fix searching feature +- Refactor RPM part +- Add iscsi tgt + + +v0.1.0 (2013-07-18) +------------------- + +- Initial changelog diff --git a/chef/cookbooks/ceph/Gemfile b/chef/cookbooks/ceph/Gemfile new file mode 100644 index 0000000..39c60fb --- /dev/null +++ b/chef/cookbooks/ceph/Gemfile @@ -0,0 +1,14 @@ +source 'https://rubygems.org' + +gem 'chef', '~> 11' +gem 'berkshelf', '~> 2.0.10' + +group :test do + gem 'foodcritic', '~> 3.0' + gem 'rubocop', '~> 0.23.0' +end + +group :integration do + gem 'test-kitchen', '~> 1.1.1' + gem 'kitchen-vagrant', '~> 0.14' +end diff --git a/chef/cookbooks/ceph/LICENSE b/chef/cookbooks/ceph/LICENSE new file mode 100644 index 0000000..5c304d1 --- /dev/null +++ b/chef/cookbooks/ceph/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/chef/cookbooks/ceph/README.md b/chef/cookbooks/ceph/README.md new file mode 100644 index 0000000..60ed16f --- /dev/null +++ b/chef/cookbooks/ceph/README.md @@ -0,0 +1,102 @@ +# Chef cookbook [![Build Status](https://travis-ci.org/ceph/ceph-cookbook.svg?branch=master)](https://travis-ci.org/ceph/ceph-cookbook) [![Gitter chat](https://badges.gitter.im/ceph/ceph-cookbook.png)](https://gitter.im/ceph/ceph-cookbook) + +## DESCRIPTION + +Installs and configures Ceph, a distributed network storage and filesystem designed to provide excellent performance, reliability, and scalability. + +The current version is focused towards deploying Monitors and OSD on Ubuntu. + +For documentation on how to use this cookbook, refer to the [USAGE](#USAGE) section. + +For help, use [Gitter chat](https://gitter.im/ceph/ceph-cookbook), [mailing-list](mailto:ceph-users-join@lists.ceph.com) or [issues](https://github.com/ceph/ceph-cookbook/issues) + +## REQUIREMENTS + +### Chef + +>= 11.6.0 + +### Platform + +Tested as working: + +* Ubuntu Precise (12.04) + +### Cookbooks + +The ceph cookbook requires the following cookbooks from Opscode: + +https://github.com/opscode/cookbooks + +* apt +* apache2 + + +## ATTRIBUTES + +### Ceph Rados Gateway + +* node[:ceph][:radosgw][:api_fqdn] +* node[:ceph][:radosgw][:admin_email] +* node[:ceph][:radosgw][:rgw_addr] + +## TEMPLATES + +## USAGE + +Ceph cluster design is beyond the scope of this README, please turn to the +public wiki, mailing lists, visit our IRC channel, or contact Inktank: + +http://ceph.com/docs/master +http://ceph.com/resources/mailing-list-irc/ +http://www.inktank.com/ + + +### Ceph Monitor + +Ceph monitor nodes should use the ceph-mon role. + +Includes: + +* ceph::default +* ceph::conf + +### Ceph Metadata Server + +Ceph metadata server nodes should use the ceph-mds role. + +Includes: + +* ceph::default + +### Ceph OSD + +Ceph OSD nodes should use the ceph-osd role + +Includes: + +* ceph::default +* ceph::conf + +### Ceph Rados Gateway + +Ceph Rados Gateway nodes should use the ceph-radosgw role + + +## LICENSE AND AUTHORS + +* Author: Kyle Bader + +* Copyright 2013, DreamHost Web Hosting and Inktank Storage Inc. + +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. diff --git a/chef/cookbooks/ceph/Rakefile b/chef/cookbooks/ceph/Rakefile new file mode 100644 index 0000000..4a798ce --- /dev/null +++ b/chef/cookbooks/ceph/Rakefile @@ -0,0 +1,48 @@ +#!/usr/bin/env rake + +# Style tests. Rubocop and Foodcritic +namespace :style do + begin + require 'rubocop/rake_task' + desc 'Run Ruby style checks' + RuboCop::RakeTask.new(:ruby) + rescue LoadError + puts '>>>>> Rubocop gem not loaded, omitting tasks' unless ENV['CI'] + end + + begin + require 'foodcritic' + + desc 'Run Chef style checks' + FoodCritic::Rake::LintTask.new(:chef) do |t| + t.options = { + fail_tags: ['any'], + tags: ['~FC003'], + chef_version: '11.6.0' + } + end + rescue LoadError + puts '>>>>> foodcritic gem not loaded, omitting tasks' unless ENV['CI'] + end +end + +desc 'Run all style checks' +task style: ['style:chef', 'style:ruby'] + +# Integration tests. Kitchen.ci +namespace :integration do + begin + require 'kitchen/rake_tasks' + + desc 'Run kitchen integration tests' + Kitchen::RakeTasks.new + rescue LoadError + puts '>>>>> Kitchen gem not loaded, omitting tasks' unless ENV['CI'] + end +end + +desc 'Run all tests on Travis' +task travis: ['style'] + +# Default +task default: ['style', 'integration:kitchen:all'] diff --git a/chef/cookbooks/ceph/attributes/cephfs.rb b/chef/cookbooks/ceph/attributes/cephfs.rb new file mode 100644 index 0000000..981b338 --- /dev/null +++ b/chef/cookbooks/ceph/attributes/cephfs.rb @@ -0,0 +1,10 @@ +default['ceph']['cephfs_mount'] = '/ceph' + +case node['platform_family'] +when 'debian' + packages = ['ceph-fs-common'] + packages += debug_packages(packages) if node['ceph']['install_debug'] + default['ceph']['cephfs']['packages'] = packages +else + default['ceph']['cephfs']['packages'] = [] +end diff --git a/chef/cookbooks/ceph/attributes/conf.rb b/chef/cookbooks/ceph/attributes/conf.rb new file mode 100644 index 0000000..ce12c63 --- /dev/null +++ b/chef/cookbooks/ceph/attributes/conf.rb @@ -0,0 +1,7 @@ +default['ceph']['config'] = {} +default['ceph']['config-sections'] = {} +default['ceph']['config']['keystone']['rgw keystone accepted roles'] = 'admin, _member_' +default['ceph']['config']['keystone']['rgw keystone token cache size'] = 500 +default['ceph']['config']['keystone']['rgw keystone revocation interval'] = 600 +default['ceph']['config']['keystone']['nss db path'] = '/var/ceph/nss' +default['ceph']['config']['keystone']['rgw keystone admin token'] = 'openstack_identity_bootstrap_token' \ No newline at end of file diff --git a/chef/cookbooks/ceph/attributes/default.rb b/chef/cookbooks/ceph/attributes/default.rb new file mode 100644 index 0000000..e05bec6 --- /dev/null +++ b/chef/cookbooks/ceph/attributes/default.rb @@ -0,0 +1,24 @@ +default['ceph']['install_debug'] = false +default['ceph']['encrypted_data_bags'] = false + +default['ceph']['install_repo'] = true + +case node['platform'] +when 'ubuntu' + default['ceph']['init_style'] = 'upstart' +else + default['ceph']['init_style'] = 'sysvinit' +end + +case node['platform_family'] +when 'debian' + packages = ['ceph-common'] + packages += debug_packages(packages) if node['ceph']['install_debug'] + default['ceph']['packages'] = packages +when 'rhel', 'fedora' + packages = ['ceph'] + packages += debug_packages(packages) if node['ceph']['install_debug'] + default['ceph']['packages'] = packages +else + default['ceph']['packages'] = [] +end diff --git a/chef/cookbooks/ceph/attributes/mds.rb b/chef/cookbooks/ceph/attributes/mds.rb new file mode 100644 index 0000000..54c21cd --- /dev/null +++ b/chef/cookbooks/ceph/attributes/mds.rb @@ -0,0 +1,12 @@ +include_attribute 'ceph' + +default['ceph']['mds']['init_style'] = node['init_style'] + +case node['platform_family'] +when 'debian' + packages = ['ceph-mds'] + packages += debug_packages(packages) if node['ceph']['install_debug'] + default['ceph']['mds']['packages'] = packages +else + default['ceph']['mds']['packages'] = [] +end diff --git a/chef/cookbooks/ceph/attributes/mon.rb b/chef/cookbooks/ceph/attributes/mon.rb new file mode 100644 index 0000000..662fdc7 --- /dev/null +++ b/chef/cookbooks/ceph/attributes/mon.rb @@ -0,0 +1,15 @@ +include_attribute 'ceph' + +default['ceph']['mon']['init_style'] = node['ceph']['init_style'] + +default['ceph']['mon']['secret_file'] = '/etc/chef/secrets/ceph_mon' +default['ceph']['default_pools'] = ['data', 'metadata', 'rbd'] + +case node['platform_family'] +when 'debian', 'rhel', 'fedora' + packages = ['ceph'] + packages += debug_packages(packages) if node['ceph']['install_debug'] + default['ceph']['mon']['packages'] = packages +else + default['ceph']['mon']['packages'] = [] +end diff --git a/chef/cookbooks/ceph/attributes/osd.rb b/chef/cookbooks/ceph/attributes/osd.rb new file mode 100644 index 0000000..addbcce --- /dev/null +++ b/chef/cookbooks/ceph/attributes/osd.rb @@ -0,0 +1,14 @@ +include_attribute 'ceph' + +default['ceph']['osd']['init_style'] = node['ceph']['init_style'] + +default['ceph']['osd']['secret_file'] = '/etc/chef/secrets/ceph_osd' + +case node['platform_family'] +when 'debian', 'rhel', 'fedora' + packages = ['ceph'] + packages += debug_packages(packages) if node['ceph']['install_debug'] + default['ceph']['osd']['packages'] = packages +else + default['ceph']['osd']['packages'] = [] +end diff --git a/chef/cookbooks/ceph/attributes/radosgw.rb b/chef/cookbooks/ceph/attributes/radosgw.rb new file mode 100644 index 0000000..a30d6db --- /dev/null +++ b/chef/cookbooks/ceph/attributes/radosgw.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: ceph +# Attributes:: radosgw +# +# Copyright 2011, DreamHost Web Hosting +# +# 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. +# + +include_attribute 'ceph' + +default['ceph']['radosgw']['api_fqdn'] = "#{node['hostname']}" +default['ceph']['radosgw']['admin_email'] = 'admin@example.com' +default['ceph']['radosgw']['rgw_addr'] = '*:80' +default['ceph']['radosgw']['rgw_port'] = false +default['ceph']['radosgw']['webserver_companion'] = 'apache2' # can be false +default['ceph']['radosgw']['use_apache_fork'] = true +default['ceph']['radosgw']['init_style'] = node['ceph']['init_style'] + +default['ceph']['radosgw']['signing']['certfile'] = '/tmp/signing_cert.pem' +default['ceph']['radosgw']['signing']['ca_certs'] = '/tmp/ca.pem' + + +case node['platform_family'] +when 'debian' + packages = ['radosgw'] + packages += debug_packages(packages) if node['ceph']['install_debug'] + default['ceph']['radosgw']['packages'] = packages +when 'rhel', 'fedora', 'suse' + default['ceph']['radosgw']['packages'] = ['ceph-radosgw'] +else + default['ceph']['radosgw']['packages'] = [] +end diff --git a/chef/cookbooks/ceph/attributes/radosgw_apache2.rb b/chef/cookbooks/ceph/attributes/radosgw_apache2.rb new file mode 100644 index 0000000..047c2a8 --- /dev/null +++ b/chef/cookbooks/ceph/attributes/radosgw_apache2.rb @@ -0,0 +1,6 @@ +case node['platform_family'] +when 'debian', 'suse' + default['ceph']['radosgw']['apache2']['packages'] = ['libapache2-mod-fastcgi'] +when 'rhel', 'fedora' + default['ceph']['radosgw']['apache2']['packages'] = ['mod_fastcgi'] +end diff --git a/chef/cookbooks/ceph/attributes/repo.rb b/chef/cookbooks/ceph/attributes/repo.rb new file mode 100644 index 0000000..b85047d --- /dev/null +++ b/chef/cookbooks/ceph/attributes/repo.rb @@ -0,0 +1,52 @@ +default['ceph']['branch'] = 'stable' # Can be stable, testing or dev. +# Major release version to install or gitbuilder branch +default['ceph']['version'] = 'firefly' +default['ceph']['el_add_epel'] = true +default['ceph']['repo_url'] = 'http://ceph.com' +default['ceph']['extras_repo_url'] = 'http://ceph.com/packages/ceph-extras' +default['ceph']['extras_repo'] = true + +case node['platform_family'] +when 'debian' + # Debian/Ubuntu default repositories + default['ceph']['debian']['stable']['repository'] = "#{node['ceph']['repo_url']}/debian-#{node['ceph']['version']}/" + default['ceph']['debian']['stable']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' + default['ceph']['debian']['testing']['repository'] = "#{node['ceph']['repo_url']}/debian-testing/" + default['ceph']['debian']['testing']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' + default['ceph']['debian']['dev']['repository'] = "http://gitbuilder.ceph.com/ceph-deb-#{node['lsb']['codename']}-x86_64-basic/ref/#{node['ceph']['version']}" + default['ceph']['debian']['dev']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc' + default['ceph']['debian']['extras']['repository'] = "#{node['ceph']['extras_repo_url']}/debian/" + default['ceph']['debian']['extras']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' +when 'rhel' + # Redhat/CentOS default repositories + default['ceph']['rhel']['stable']['repository'] = "#{node['ceph']['repo_url']}/rpm-#{node['ceph']['version']}/el6/x86_64/" + default['ceph']['rhel']['stable']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' + default['ceph']['rhel']['testing']['repository'] = "#{node['ceph']['repo_url']}/rpm-testing/el6/x86_64/" + default['ceph']['rhel']['testing']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' + default['ceph']['rhel']['dev']['repository'] = "http://gitbuilder.ceph.com/ceph-rpm-centos6-x86_64-basic/ref/#{node['ceph']['version']}/x86_64/" + default['ceph']['rhel']['dev']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc' + default['ceph']['rhel']['extras']['repository'] = "#{node['ceph']['extras_repo_url']}/rpm/centos6.4/x86_64/" + default['ceph']['rhel']['extras']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' +when 'fedora' + # Fedora default repositories + default['ceph']['fedora']['stable']['repository'] = "#{node['ceph']['repo_url']}/rpm-#{node['ceph']['version']}/fc#{node['platform_version']}/x86_64/" + default['ceph']['fedora']['stable']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' + default['ceph']['fedora']['testing']['repository'] = "#{node['ceph']['repo_url']}/rpm-testing/fc#{node['platform_version']}/x86_64/" + default['ceph']['fedora']['testing']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' + default['ceph']['fedora']['dev']['repository'] = "http://gitbuilder.ceph.com/ceph-rpm-fc#{node['platform_version']}-x86_64-basic/ref/#{node['ceph']['version']}/RPMS/x86_64/" + default['ceph']['fedora']['dev']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc' + default['ceph']['fedora']['extras']['repository'] = "#{node['ceph']['extras_repo_url']}/rpm/fedora#{node['platform_version']}/x86_64/" + default['ceph']['fedora']['extras']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' +when 'suse' + # (Open)SuSE default repositories + # Chef doesn't make a difference between suse and opensuse + suse = Mixlib::ShellOut.new("head -n1 /etc/SuSE-release| awk '{print $1}'").run_command.stdout.chomp.downcase + suse = 'sles' if suse == 'suse' + suse_version = suse << Mixlib::ShellOut.new("grep VERSION /etc/SuSE-release | awk -F'= ' '{print $2}'").run_command.stdout.chomp + + default['ceph']['suse']['stable']['repository'] = "#{node['ceph']['repo_url']}/rpm-#{node['ceph']['version']}/#{suse_version}/x86_64/ceph-release-1-0.#{suse_version}.noarch.rpm" + default['ceph']['suse']['testing']['repository'] = "#{node['ceph']['repo_url']}/rpm-testing/#{suse_version}/x86_64/ceph-release-1-0.#{suse_version}.noarch.rpm" + default['ceph']['suse']['extras']['repository'] = "#{node['ceph']['extras_repo_url']}/rpm/#{suse_version}/x86_64/" +else + fail "#{node['platform_family']} is not supported" +end diff --git a/chef/cookbooks/ceph/conf.rb b/chef/cookbooks/ceph/conf.rb new file mode 100644 index 0000000..43d6a54 --- /dev/null +++ b/chef/cookbooks/ceph/conf.rb @@ -0,0 +1,26 @@ +# fail 'mon_initial_members must be set in config' if node['ceph']['config']['mon_initial_members'].nil? + +unless node['ceph']['config']['fsid'] + Chef::Log.warn('We are genereting a new uuid for fsid') + require 'securerandom' + node.set['ceph']['config']['fsid'] = SecureRandom.uuid + node.save +end + +directory '/etc/ceph' do + owner 'root' + group 'root' + mode '0755' + action :create +end + +template '/etc/ceph/ceph.conf' do + source 'ceph.conf.erb' + variables lazy { + { + :mon_addresses => mon_addresses, + :is_rgw => node['ceph']['is_radosgw'] + } + } + mode '0644' +end diff --git a/chef/cookbooks/ceph/infrastructure.yml b/chef/cookbooks/ceph/infrastructure.yml new file mode 100644 index 0000000..de8655b --- /dev/null +++ b/chef/cookbooks/ceph/infrastructure.yml @@ -0,0 +1,6 @@ +roles: +- ceph-mds: +- ceph-mon: +- ceph-osd: +- ceph-radosgw: +- ceph-tgt: diff --git a/chef/cookbooks/ceph/libraries/default.rb b/chef/cookbooks/ceph/libraries/default.rb new file mode 100644 index 0000000..e0a9013 --- /dev/null +++ b/chef/cookbooks/ceph/libraries/default.rb @@ -0,0 +1,336 @@ +require 'ipaddr' +require 'json' +require 'chef/mixin/shell_out' +include Chef::Mixin::ShellOut + +def crowbar? + !defined?(Chef::Recipe::Barclamp).nil? +end + +def mon_nodes + if crowbar? + mon_roles = search(:role, 'name:crowbar-* AND run_list:role\[ceph*-mon\]') + unless mon_roles.empty? + search_string = mon_roles.map { |role_object| 'roles:' + role_object.name }.join(' OR ') + search_string = "(#{search_string}) AND ceph_config_environment:#{node['ceph']['config']['environment']}" + end + else + #search_string = "ceph_is_mon:true AND chef_environment:#{node.chef_environment}" + search_string = "ceph_is_mon:true AND ceph_config_fsid:#{node["ceph"]["config"]["fsid"]}" + end + + if use_cephx? && !node['ceph']['encrypted_data_bags'] + search_string = "(#{search_string}) AND (ceph_bootstrap_osd_key:*)" + end + search(:node, search_string) +end + +def osd_secret + if node['ceph']['encrypted_data_bags'] + secret = Chef::EncryptedDataBagItem.load_secret(node['ceph']['osd']['secret_file']) + return Chef::EncryptedDataBagItem.load('ceph', 'osd', secret)['secret'] + else + return mon_nodes[0]['ceph']['bootstrap_osd_key'] + end +end + +# If public_network is specified +# we need to search for the monitor IP +# in the node environment. +# 1. We look if the network is IPv6 or IPv4 +# 2. We look for a route matching the network +# 3. We grab the IP and return it with the port +def find_node_ip_in_network(network, nodeish = nil) + nodeish = node unless nodeish + net = IPAddr.new(network) + nodeish['network']['interfaces'].each do |_iface, addrs| + addresses = addrs['addresses'] || [] + addresses.each do |ip, params| + return ip_address_to_ceph_address(ip, params) if ip_address_in_network?(ip, params, net) + end + end + nil +end + +def ip_address_in_network?(ip, params, net) + if params['family'] == 'inet' + net.include?(ip) && params.key?('broadcast') # is primary ip on iface + elsif params['family'] == 'inet6' + net.include?(ip) + else + false + end +end + +def ip_address_to_ceph_address(ip, params) + if params['family'].eql?('inet6') + return "[#{ip}]:6789" + elsif params['family'].eql?('inet') + return "#{ip}:6789" + end +end + +def mon_addresses + mon_ips = [] + + if File.exist?("/var/run/ceph/ceph-mon.#{node['hostname']}.asok") + mon_ips = quorum_members_ips + else + mons = [] + # make sure if this node runs ceph-mon, it's always included even if + # search is laggy; put it first in the hopes that clients will talk + # primarily to local node + mons << node if node['ceph']['is_mon'] + + mons += mon_nodes + if crowbar? + mon_ips = mons.map { |node| Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, 'admin').address } + else + if node['ceph']['config']['global'] && node['ceph']['config']['global']['public network'] + mon_ips = mons.map { |nodeish| find_node_ip_in_network(node['ceph']['config']['global']['public network'], nodeish) } + else + mon_ips = mons.map { |node| node['ipaddress'] + ':6789' } + end + end + end + mon_ips.reject { |m| m.nil? }.uniq +end + +def mon_secret + if node['ceph']['encrypted_data_bags'] + secret = Chef::EncryptedDataBagItem.load_secret(node['ceph']['mon']['secret_file']) + Chef::EncryptedDataBagItem.load('ceph', 'mon', secret)['secret'] + elsif !mon_nodes.empty? + mon_nodes[0]['ceph']['monitor-secret'] + elsif node['ceph']['monitor-secret'] + node['ceph']['monitor-secret'] + elsif mon_master['hostname'] != node['hostname'] + mon_nodes[0]['ceph']['monitor-secret'] + else + Chef::Log.info('No monitor secret found') + nil + end +end + +def pg_creating? + pg_creating_flag = '' + if pg_creating_flag.empty? + pg_creating_flag = Mixlib::ShellOut.new('ceph -s | grep "creating"').run_command.stdout.strip + end + pg_creating_flag.include?('creating') +end + +def mon_master + search_string2 = "run_list:role\\[ceph*-mon\\] AND chef_environment:#{node.chef_environment} AND tags:mon_master" + mon_master_node = search(:node, search_string2) + if mon_master_node.empty? + search_string2 = "run_list:role\\[ceph*-mon\\] AND chef_environment:#{node.chef_environment}" + all_mons = search(:node, search_string2) + + # TODO(weidong): should add empty? checking later. + mons_sort = all_mons.sort_by { |a| a.name } + if mons_sort[0].name == node.name + node.tags << 'mon_master' unless node.tags.include?("mon_master") + node.save + end + mons_sort[0] + else + mon_master_node[0] + end +end + +def mon_init_member + search_string3 = "run_list:role\\[ceph*-mon\\] AND chef_environment:#{node.chef_environment}" + all_mons = search(:node, search_string3) + mons_sort = all_mons.sort_by { |a| a.name } +end + +def mon_init_member_name + mon_list = mon_init_member + mon_list.map { |a| a.name }.sort unless mon_list.nil? +end + +#search SSD device +def ssd_device + ssd_device = [] + node['block_device'].each do |device| + device_name = device[0] + if device_name.include?"sd" + device_ssd_flag = Mixlib::ShellOut.new("cat /sys/block/#{device_name}/queue/rotational").run_command.stdout.strip + if device_ssd_flag == "0" + ssd_device << "/dev/#{device_name}" + end + else + next + end + end + ssd_device +end + +def quorum_members_ips + mon_ips = [] + cmd = Mixlib::ShellOut.new("ceph --admin-daemon /var/run/ceph/ceph-mon.#{node['hostname']}.asok mon_status") + cmd.run_command + cmd.error! + + mons = JSON.parse(cmd.stdout)['monmap']['mons'] + mons.each do |k| + mon_ips.push(k['addr'][0..-3]) + end + mon_ips +end + +QUORUM_STATES = %w(leader, peon) +def quorum? + # "ceph auth get-or-create-key" would hang if the monitor wasn't + # in quorum yet, which is highly likely on the first run. This + # helper lets us delay the key generation into the next + # chef-client run, instead of hanging. + # + # Also, as the UNIX domain socket connection has no timeout logic + # in the ceph tool, this exits immediately if the ceph-mon is not + # running for any reason; trying to connect via TCP/IP would wait + # for a relatively long timeout. + + cmd = Mixlib::ShellOut.new("ceph --admin-daemon /var/run/ceph/ceph-mon.#{node['hostname']}.asok mon_status") + cmd.run_command + cmd.error! + + state = JSON.parse(cmd.stdout)['state'] + QUORUM_STATES.include?(state) +end + +# Cephx is on by default, but users can disable it. +# type can be one of 3 values: cluster, service, or client. If the value is none of the above, set it to cluster +def use_cephx?(type = nil) + # Verify type is valid + type = 'cluster' if %w(cluster service client).index(type).nil? + + # CephX is enabled if it's not configured at all, or explicity enabled + node['ceph']['config'].nil? || + node['ceph']['config']['global'].nil? || + node['ceph']['config']['global']["auth #{type} required"] == 'cephx' +end + +#current partion number of a given device +def partition_num(device) + cmd = "parted #{device} --script -- p | awk '{print $1}'" + rc = shell_out(cmd) + p_num = rc.stdout.split.select{|e| e[/\d/]} + if p_num.include? "Number" + last_num = 0 + Chef::Log.info("There is not any partition created at #{resource.device} yet.") + end + p_num +end + +#partion start size of a given device +def partition_start_size(device) + cmd = "parted #{device} --script -- p | awk '{print $3}' | tail -n 2" + rc = shell_out(cmd) + device_start_size = rc.stdout.split[0] + if device_start_size.include? "End" + device_start_size = 0 + end + if device_start_size == 0 + device_start_size + elsif device_start_size.include?('KB') + device_start_size = eval(device_start_size.gsub(/[A-Z]/,''))/1000 + elsif device_start_size.include?('GB') + device_start_size = eval(device_start_size.gsub(/[A-Z]/,''))*1000 + elsif device_start_size.include?('MB') + device_start_size = eval(device_start_size.gsub(/[A-Z]/,'')) + elsif device_start_size.include?('TB') + device_start_size = eval(device_start_size.gsub(/[A-Z]/,''))*1000000 + end + device_start_size +end + +def disk_total_size(device) + cmd = "parted #{device} --script -- p | grep #{device} | cut -f 2 -d ':'" + rc = shell_out(cmd) + device_total_size = rc.stdout.split[0] + if device_total_size.include?('GB') + device_total_size = eval(device_total_size.gsub(/[A-Z]/,''))*1000 + elsif device_total_size.include?('MB') + device_total_size = eval(device_total_size.gsub(/[A-Z]/,'')) + elsif device_total_size.include?('TB') + device_total_size = eval(device_total_size.gsub(/[A-Z]/,''))*1000000 + end + device_total_size +end + +def mklabel(device) + queryresult = %x{parted #{device} --script -- print |grep 'Partition Table: gpt'} + if not queryresult.include?('gpt') + cmd = "parted #{device} --script -- mklabel gpt" + rc = shell_out(cmd) + if not rc.exitstatus.eql?(0) + Chef::Log.error("Creating disk label was failed.") + end + end +end + +def mkpart(device) + device_total_size = disk_total_size(device) + device_start_size = partition_start_size(device) + 100 + if node['ceph']['config']['osd']['osd journal size'] + osd_journal_size = node['ceph']['config']['osd']['osd journal size'].to_i + elsif node['ceph']['config']['global']['osd journal size'] + osd_journal_size = node['ceph']['config']['global']['osd journal size'].to_i + else + osd_journal_size = 5120 + end + device_end_size = device_start_size + osd_journal_size + if device_start_size < device_total_size + p_num_old = partition_num(device) + if device_total_size > device_end_size + output = %x{parted #{device} --script -- mkpart osd_journal #{device_start_size.to_s} #{device_end_size.to_s}} + else + output = %x{parted #{device} --script -- mkpart osd_journal #{device_start_size.to_s} 100%} + end + output = %x{partx -a #{device} > /dev/null 2>&1} + p_num_new = partition_num(device) + p_num = (p_num_new - p_num_old)[0] + if p_num.nil? + Chef::Log.error("Making partition was failed.") + else + device_return = device+p_num + %x{mkfs.xfs #{device_return}} + device_return + end + end +end + +def create_disk_partion(device) + mklabel(device) + mkpart(device) +end + +def selinux_disabled? + selinux_status = Mixlib::ShellOut.new('sestatus').run_command.stdout.strip + selinux_status.include?('disabled') +end + +def node_election(role, tag, chef_environment = nil) + chef_environment = chef_environment || node.chef_environment + master = search(:node, "run_list:role\\[#{role}\\] AND \ + chef_environment:#{chef_environment} AND \ + tags:#{tag}") || [] + if master.empty? + nodes = search(:node, "run_list:role\\[#{role}\\] AND \ + chef_environment:#{chef_environment}") || [] + if !nodes.empty? + nodes = nodes.sort_by { |node| node.name } unless nodes.empty? + if node['hostname'].eql?(nodes[0]['hostname']) + node.tags << tag unless node.tags.include?(tag) + node.save + end + return nodes[0] + else + node + end + else + return master[0] + end +end diff --git a/chef/cookbooks/ceph/libraries/utils.rb b/chef/cookbooks/ceph/libraries/utils.rb new file mode 100644 index 0000000..257472f --- /dev/null +++ b/chef/cookbooks/ceph/libraries/utils.rb @@ -0,0 +1,14 @@ +def debug_packages(packages) + packages.map { |x| x + debug_ext } +end + +def debug_ext + case node['platform_family'] + when 'debian' + '-dbg' + when 'rhel', 'fedora' + '-debug' + else + '' + end +end diff --git a/chef/cookbooks/ceph/metadata.rb b/chef/cookbooks/ceph/metadata.rb new file mode 100644 index 0000000..01e49dc --- /dev/null +++ b/chef/cookbooks/ceph/metadata.rb @@ -0,0 +1,12 @@ +name 'ceph' +maintainer 'Kyle Bader' +maintainer_email 'kyle.bader@dreamhost.com' +license 'Apache 2.0' +description 'Installs/Configures the Ceph distributed filesystem' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '0.2.1' + +depends 'apache2', '>= 1.1.12' +depends 'apt' +depends 'yum', '>= 3.0' +depends 'yum-epel' diff --git a/chef/cookbooks/ceph/providers/client.rb b/chef/cookbooks/ceph/providers/client.rb new file mode 100644 index 0000000..1b05c49 --- /dev/null +++ b/chef/cookbooks/ceph/providers/client.rb @@ -0,0 +1,80 @@ +use_inline_resources + +def whyrun_supported? + true +end + +action :add do + current_resource = @current_resource + filename = @current_resource.filename + keyname = @current_resource.keyname + caps = @new_resource.caps.map { |k, v| "#{k} '#{v}'" }.join(' ') + owner = @new_resource.owner + group = @new_resource.group + mode = @new_resource.mode + unless @current_resource.caps_match + converge_by("Set caps for #{@new_resource}") do + auth_set_key(keyname, caps) + current_resource.key = get_key(keyname) + + end + end + # update the key in the file + file filename do + content file_content + owner owner + group group + mode mode + end + +end + +def load_current_resource + @current_resource = Chef::Resource::CephClient.new(@new_resource.name) + @current_resource.name(@new_resource.name) + @current_resource.as_keyring(@new_resource.as_keyring) + @current_resource.keyname(@new_resource.keyname || "client.#{current_resource.name}.#{node['hostname']}") + @current_resource.caps(get_caps(@current_resource.keyname)) + default_filename = "/etc/ceph/ceph.client.#{@new_resource.name}.#{node['hostname']}.#{@new_resource.as_keyring ? 'keyring' : 'secret'}" + @current_resource.filename(@new_resource.filename || default_filename) + @current_resource.key = get_key(@current_resource.keyname) + @current_resource.caps_match = true if @current_resource.caps == @new_resource.caps +end + +def file_content + @current_resource.as_keyring ? "[#{@current_resource.keyname}]\n\tkey = #{@current_resource.key}\n" : @current_resource.key +end + +def get_key(keyname) + cmd = "ceph auth print_key #{keyname} --name mon. --key='#{mon_secret}'" + #cmd = "ceph auth print_key #{keyname} --key='#{mon_secret}'" + Mixlib::ShellOut.new(cmd).run_command.stdout +end + +def get_caps(keyname) + caps = {} + cmd = "ceph auth get #{keyname} --name mon. --key='#{mon_secret}'" + #cmd = "ceph auth get #{keyname} --key='#{mon_secret}'" + output = Mixlib::ShellOut.new(cmd).run_command.stdout + output.scan(/caps\s*(\S+)\s*=\s*"([^"]*)"/) { |k, v| caps[k] = v } + caps +end + +def auth_set_key(keyname, caps) + secret = mon_secret + # try to add the key + cmd = "ceph auth get-or-create #{keyname} #{caps} --name mon. --key='#{secret}'" + #cmd = "ceph auth get-or-create #{keyname} #{caps} --key='#{secret}'" + get_or_create = Mixlib::ShellOut.new(cmd) + get_or_create.run_command + if get_or_create.stderr.scan(/EINVAL.*but cap.*does not match/) + Chef::Log.info('Deleting old key with incorrect caps') + # delete an old key if it exists and is wrong + Mixlib::ShellOut.new("ceph auth del #{keyname} --name mon. --key='#{secret}'").run_command + #Mixlib::ShellOut.new("ceph auth del #{keyname} --key='#{secret}'").run_command + # try to create again + get_or_create = Mixlib::ShellOut.new(cmd) + get_or_create.run_command + end + get_or_create.error! +end diff --git a/chef/cookbooks/git/.gitignore b/chef/cookbooks/ceph/recipes/.gitignore similarity index 64% rename from chef/cookbooks/git/.gitignore rename to chef/cookbooks/ceph/recipes/.gitignore index dd1e425..351201e 100644 --- a/chef/cookbooks/git/.gitignore +++ b/chef/cookbooks/ceph/recipes/.gitignore @@ -1,14 +1,17 @@ .vagrant Berksfile.lock -Gemfile.lock *~ *# .#* \#*# .*.sw[a-z] *.un~ -.bundle -.cache -.kitchen -bin +/cookbooks + +# Bundler +Gemfile.lock +bin/* +.bundle/* + +.kitchen/ .kitchen.local.yml diff --git a/chef/cookbooks/ceph/recipes/.kitchen.yml b/chef/cookbooks/ceph/recipes/.kitchen.yml new file mode 100644 index 0000000..41f8cf4 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/.kitchen.yml @@ -0,0 +1,63 @@ +--- +driver_plugin: vagrant +driver_config: + vagrantfile_erb: test/integration/Vagrantfile.erb + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.04 + run_list: + - recipe[apt] +- name: ubuntu-14.04 + run_list: + - recipe[apt] +- name: debian-7.4 + run_list: + - recipe[apt] +- name: centos-6.5 +- name: centos-5.10 +- name: fedora-18 + +provisioner: + name: chef_zero + +suites: +- name: default + run_list: + - "recipe[ceph::repo]" + - "recipe[ceph]" + attributes: &defaults + ceph: + config: + fsid: ae3f1d03-bacd-4a90-b869-1a4fabb107f2 + mon_initial_members: + - "127.0.0.1" +- name: osd + run_list: + - "role[ceph-osd]" + attributes: *defaults +- name: mon + run_list: + - "role[ceph-mon]" + attributes: *defaults +- name: mds + run_list: + - "role[ceph-mds]" + attributes: *defaults +- name: radosgw + run_list: + - "role[ceph-radosgw]" + attributes: *defaults +- name: aio + attributes: + ceph: + config-sections: + global: + "osd journal size" : 128 + "osd pool default size": 1 + osd_devices: + - { device: "/dev/sdb" } + - { device: "/dev/sdc" } + - { device: "/dev/sdd" } + run_list: + - recipe[ceph::all_in_one] diff --git a/chef/cookbooks/ceph/recipes/.rubocop.yml b/chef/cookbooks/ceph/recipes/.rubocop.yml new file mode 100644 index 0000000..0fde6a5 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/.rubocop.yml @@ -0,0 +1,28 @@ +AllCops: + Include: + - Berksfile + - Gemfile + - Rakefile + - Thorfile + - Guardfile + Exclude: + - vendor/** + +ClassLength: + Enabled: false +Documentation: + Enabled: false +Encoding: + Enabled: false +HashSyntax: + Enabled: false +LineLength: + Enabled: false +MethodLength: + Enabled: false +SignalException: + Enabled: false +TrailingComma: + Enabled: false +WordArray: + Enabled: false diff --git a/chef/cookbooks/ceph/recipes/.travis.yml b/chef/cookbooks/ceph/recipes/.travis.yml new file mode 100644 index 0000000..7d2bad2 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/.travis.yml @@ -0,0 +1,7 @@ +language: ruby +rvm: + - 1.9.3 + - 2.0.0 +bundler_args: --without integration +script: + - bundle exec rake travis diff --git a/chef/cookbooks/ceph/recipes/_common.rb b/chef/cookbooks/ceph/recipes/_common.rb new file mode 100644 index 0000000..8dbebfd --- /dev/null +++ b/chef/cookbooks/ceph/recipes/_common.rb @@ -0,0 +1,6 @@ +include_recipe 'ceph::_common_install' + +# Tools needed by cookbook +node['ceph']['packages'].each do |pck| + package pck +end diff --git a/chef/cookbooks/ceph/recipes/_common_install.rb b/chef/cookbooks/ceph/recipes/_common_install.rb new file mode 100644 index 0000000..171a227 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/_common_install.rb @@ -0,0 +1 @@ +include_recipe 'ceph::repo' if node['ceph']['install_repo'] diff --git a/chef/cookbooks/ceph/recipes/all_in_one.rb b/chef/cookbooks/ceph/recipes/all_in_one.rb new file mode 100644 index 0000000..794f377 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/all_in_one.rb @@ -0,0 +1,6 @@ + +include_recipe 'ceph::mon' +include_recipe 'ceph::osd' +include_recipe 'ceph::mds' +include_recipe 'ceph::cephfs' +include_recipe 'ceph::radosgw' diff --git a/chef/cookbooks/ceph/recipes/apt.rb b/chef/cookbooks/ceph/recipes/apt.rb new file mode 100644 index 0000000..d101b00 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/apt.rb @@ -0,0 +1,27 @@ + +include_recipe 'apt' + +branch = node['ceph']['branch'] + +distribution_codename = +case node['lsb']['codename'] +when 'jessie' then 'sid' +else node['lsb']['codename'] +end + +apt_repository 'ceph' do + repo_name 'ceph' + uri node['ceph']['debian'][branch]['repository'] + distribution distribution_codename + components ['main'] + key node['ceph']['debian'][branch]['repository_key'] +end + +apt_repository 'ceph-extras' do + repo_name 'ceph-extras' + uri node['ceph']['debian']['extras']['repository'] + distribution distribution_codename + components ['main'] + key node['ceph']['debian']['extras']['repository_key'] + only_if { node['ceph']['extras_repo'] } +end diff --git a/chef/cookbooks/ceph/recipes/cephfs.rb b/chef/cookbooks/ceph/recipes/cephfs.rb new file mode 100644 index 0000000..64dbaf9 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/cephfs.rb @@ -0,0 +1,46 @@ +# +# Author:: Kyle Bader +# Cookbook Name:: ceph +# Recipe:: cephfs +# +# Copyright 2011, DreamHost Web Hosting +# +# 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. + +include_recipe 'ceph::_common' +include_recipe 'ceph::cephfs_install' +include_recipe 'ceph::conf' + +name = 'cephfs' +client_name = "cephfs.#{node['hostname']}" +filename = "/etc/ceph/ceph.client.#{client_name}.secret" + +ceph_client name do + filename filename + caps('mon' => 'allow r', 'osd' => 'allow rw', 'mds' => 'allow') + as_keyring false +end + +mons = mon_addresses.join(',') + ':/' + +directory node['ceph']['cephfs_mount'] + +mount node['ceph']['cephfs_mount'] do + fstype 'ceph' + device mons + options "_netdev,name=#{client_name},secretfile=#{filename}" + dump 0 + pass 0 + action [:mount, :enable] + not_if { mons.empty? } +end diff --git a/chef/cookbooks/ceph/recipes/cephfs_install.rb b/chef/cookbooks/ceph/recipes/cephfs_install.rb new file mode 100644 index 0000000..826997d --- /dev/null +++ b/chef/cookbooks/ceph/recipes/cephfs_install.rb @@ -0,0 +1,5 @@ +include_recipe 'ceph::_common_install' + +node['ceph']['cephfs']['packages'].each do |pck| + package pck +end diff --git a/chef/cookbooks/ceph/recipes/conf.rb b/chef/cookbooks/ceph/recipes/conf.rb new file mode 100644 index 0000000..3477244 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/conf.rb @@ -0,0 +1,27 @@ +# fail 'mon_initial_members must be set in config' if node['ceph']['config']['mon_initial_members'].nil? + +unless node['ceph']['config']['fsid'] + Chef::Log.warn('We are genereting a new uuid for fsid') + require 'securerandom' + node.set['ceph']['config']['fsid'] = SecureRandom.uuid + node.save +end + +directory '/etc/ceph' do + owner 'root' + group 'root' + mode '0755' + action :create +end + +template '/etc/ceph/ceph.conf' do + source 'ceph.conf.erb' + variables lazy { + { + :mon_addresses => mon_addresses, + :is_rgw => node['ceph']['is_radosgw'], + :is_keystone_integration => node['ceph']['is_keystone_integration'] + } + } + mode '0644' +end diff --git a/chef/cookbooks/ceph/recipes/install.rb b/chef/cookbooks/ceph/recipes/install.rb new file mode 100644 index 0000000..7d1eb57 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/install.rb @@ -0,0 +1,53 @@ +# +# Author:: Kyle Bader +# Cookbook Name:: ceph +# Recipe:: default +# +# Copyright 2011, DreamHost Web Hosting +# +# 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. + +packages = [] + +case node['platform_family'] +when 'debian' + packages = %w( + ceph + ceph-common + ) + + if node['ceph']['install_debug'] + packages_dbg = %w( + ceph-dbg + ceph-common-dbg + ) + packages += packages_dbg + end +when 'rhel', 'fedora' + packages = %w( + ceph + ) + + if node['ceph']['install_debug'] + packages_dbg = %w( + ceph-debug + ) + packages += packages_dbg + end +end + +packages.each do |pkg| + package pkg do + action :install + end +end diff --git a/chef/cookbooks/ceph/recipes/mds.rb b/chef/cookbooks/ceph/recipes/mds.rb new file mode 100644 index 0000000..69ca3a5 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/mds.rb @@ -0,0 +1,70 @@ +# +# Author:: Kyle Bader +# Cookbook Name:: ceph +# Recipe:: mds +# +# Copyright 2011, DreamHost Web Hosting +# +# 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. + +include_recipe 'ceph::_common' +include_recipe 'ceph::mds_install' +include_recipe 'ceph::conf' + +cluster = 'ceph' + +directory "/var/lib/ceph/mds/#{cluster}-#{node['hostname']}" do + owner 'root' + group 'root' + mode 00755 + recursive true + action :create +end + +ceph_client 'mds' do + caps('osd' => 'allow *', 'mon' => 'allow rwx') + keyname "mds.#{node['hostname']}" + filename "/var/lib/ceph/mds/#{cluster}-#{node['hostname']}/keyring" +end + +file "/var/lib/ceph/mds/#{cluster}-#{node['hostname']}/done" do + owner 'root' + group 'root' + mode 00644 +end + +service_type = node['ceph']['osd']['init_style'] + +case service_type +when 'upstart' + filename = 'upstart' +else + filename = 'sysvinit' +end +file "/var/lib/ceph/mds/#{cluster}-#{node['hostname']}/#{filename}" do + owner 'root' + group 'root' + mode 00644 +end + +service 'ceph_mds' do + case service_type + when 'upstart' + service_name 'ceph-mds-all-starter' + provider Chef::Provider::Service::Upstart + else + service_name 'ceph' + end + action [:enable, :start] + supports :restart => true +end diff --git a/chef/cookbooks/ceph/recipes/mds_install.rb b/chef/cookbooks/ceph/recipes/mds_install.rb new file mode 100644 index 0000000..3ce533a --- /dev/null +++ b/chef/cookbooks/ceph/recipes/mds_install.rb @@ -0,0 +1,5 @@ +include_recipe 'ceph::_common_install' + +node['ceph']['mds']['packages'].each do |pck| + package pck +end diff --git a/chef/cookbooks/ceph/recipes/mon.rb b/chef/cookbooks/ceph/recipes/mon.rb new file mode 100644 index 0000000..7b760a8 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/mon.rb @@ -0,0 +1,180 @@ +# This recipe creates a monitor cluster +# +# You should never change the mon default path or +# the keyring path. +# Don't change the cluster name either +# Default path for mon data: /var/lib/ceph/mon/$cluster-$id/ +# which will be /var/lib/ceph/mon/ceph-`hostname`/ +# This path is used by upstart. If changed, upstart won't +# start the monitor +# The keyring files are created using the following pattern: +# /etc/ceph/$cluster.client.$name.keyring +# e.g. /etc/ceph/ceph.client.admin.keyring +# The bootstrap-osd and bootstrap-mds keyring are a bit +# different and are created in +# /var/lib/ceph/bootstrap-{osd,mds}/ceph.keyring + +node.default['ceph']['is_mon'] = true + +include_recipe 'ceph::conf' +include_recipe 'ceph::_common' +include_recipe 'ceph::mon_install' + + +service_type = node['ceph']['mon']['init_style'] + +directory '/var/run/ceph' do + owner 'root' + group 'root' + mode 00755 + recursive true + action :create +end + +directory "/var/lib/ceph/mon/ceph-#{node['hostname']}" do + owner 'root' + group 'root' + mode 00755 + recursive true + action :create +end + +# TODO: cluster name +cluster = 'ceph' + +if mon_master.name != node.name + admin_keyring = mon_master['ceph']['admin-secret'] + if admin_keyring.nil? + Chef::Application.fatal!("wait for mon master node update.") + end + if mon_secret.nil? + Chef::Application.fatal!("wait for mon master node update.") + end + admin_user = "admin" + template "/etc/ceph/ceph.client.#{admin_user}.keyring" do + source 'ceph.client.keyring.erb' + mode 00600 + variables( + name: admin_user, + key: admin_keyring + ) + end +end + +unless File.exist?("/var/lib/ceph/mon/ceph-#{node['hostname']}/done") + keyring = "#{Chef::Config[:file_cache_path]}/#{cluster}-#{node['hostname']}.mon.keyring" + + execute 'format mon-secret as keyring' do + command lazy { "ceph-authtool '#{keyring}' --create-keyring --name=mon. --add-key='#{mon_secret}' --cap mon 'allow *'" } + creates "#{Chef::Config[:file_cache_path]}/#{cluster}-#{node['hostname']}.mon.keyring" + only_if { mon_secret } + notifies :create, 'ruby_block[save mon_secret]', :immediately + end + + execute 'generate mon-secret as keyring' do + command "ceph-authtool '#{keyring}' --create-keyring --name=mon. --gen-key --cap mon 'allow *'" + creates "#{Chef::Config[:file_cache_path]}/#{cluster}-#{node['hostname']}.mon.keyring" + not_if { mon_secret } + notifies :create, 'ruby_block[save mon_secret]', :immediately + end + + ruby_block 'save mon_secret' do + block do + fetch = Mixlib::ShellOut.new("ceph-authtool '#{keyring}' --print-key --name=mon.") + fetch.run_command + key = fetch.stdout + node.set['ceph']['monitor-secret'] = key + node.save + end + action :nothing + end + + execute 'ceph-mon mkfs' do + command "ceph-mon --mkfs -i #{node['hostname']} --keyring '#{keyring}'" + end + + ruby_block 'finalise' do + block do + ['done', service_type].each do |ack| + ::File.open("/var/lib/ceph/mon/ceph-#{node['hostname']}/#{ack}", 'w').close + end + end + end +end + +if service_type == 'upstart' + service 'ceph-mon' do + provider Chef::Provider::Service::Upstart + action :enable + end + service 'ceph-mon-all' do + provider Chef::Provider::Service::Upstart + supports :status => true + action [:enable, :start] + end +end + +service 'ceph_mon' do + case service_type + when 'upstart' + service_name 'ceph-mon-all-starter' + provider Chef::Provider::Service::Upstart + else + service_name 'ceph' + end + supports :restart => true, :status => true + subscribes :restart, resources('template[/etc/ceph/ceph.conf]') + action [:enable, :start] +end + +mon_addresses.each do |addr| + execute "peer #{addr}" do + command "ceph --admin-daemon '/var/run/ceph/ceph-mon.#{node['hostname']}.asok' add_bootstrap_peer_hint #{addr}" + ignore_failure true + end +end + +# The key is going to be automatically created, We store it when it is created +# If we're storing keys in encrypted data bags, then they've already been generated above +#if use_cephx? && !node['ceph']['encrypted_data_bags'] +unless node['ceph']['encrypted_data_bags'] + ruby_block 'get osd-bootstrap keyring' do + block do + run_out = '' + while run_out.empty? + run_out = Mixlib::ShellOut.new('ceph auth get-key client.bootstrap-osd').run_command.stdout.strip + sleep 2 + end + node.set['ceph']['bootstrap_osd_key'] = run_out + node.save + end + not_if { node['ceph']['bootstrap_osd_key'] } + end +end + +ruby_block 'save admin_secret' do + block do + fetch = Mixlib::ShellOut.new("ceph-authtool /etc/ceph/ceph.client.admin.keyring --print-key --name=client.admin") + fetch.run_command + key = fetch.stdout + node.set['ceph']['admin-secret'] = key + node.save + end +end + + +default_pools = node['ceph']['default_pools'] +#set default pg num +if node['ceph']['config']['global']['osd pool default pg num'] + + default_pools.each do |default_pool| + run_out = Mixlib::ShellOut.new("ceph osd pool get #{default_pool} pg_num| awk -F \": \" '{print $2}'").run_command.stdout.strip + if run_out.to_i < node['ceph']['config']['global']['osd pool default pgp num'].to_i + execute 'set default pg num' do + command "ceph osd pool delete #{default_pool} #{default_pool} --yes-i-really-really-mean-it;ceph osd pool create #{default_pool} #{node['ceph']['config']['global']['osd pool default pg num']}" + ignore_failure true + not_if {pg_creating?} + end + end + end +end diff --git a/chef/cookbooks/ceph/recipes/mon_install.rb b/chef/cookbooks/ceph/recipes/mon_install.rb new file mode 100644 index 0000000..557ad4c --- /dev/null +++ b/chef/cookbooks/ceph/recipes/mon_install.rb @@ -0,0 +1,5 @@ +include_recipe 'ceph::_common_install' + +node['ceph']['mon']['packages'].each do |pck| + package pck +end diff --git a/chef/cookbooks/ceph/recipes/openstack_config_mon.rb b/chef/cookbooks/ceph/recipes/openstack_config_mon.rb new file mode 100644 index 0000000..808b89f --- /dev/null +++ b/chef/cookbooks/ceph/recipes/openstack_config_mon.rb @@ -0,0 +1,76 @@ +# attention: +# this recipe should run after the openstack and ceph are running correctly! +# + +cluster = 'ceph' + +if node['ceph']['openstack_pools'].nil? + node.normal['ceph']['openstack_pools'] = [{'pool_name'=>'images'},{'pool_name'=>'volumes'},{'pool_name'=>'vms'}] +end + +#create pools for openstack volumes and images +if node['ceph']['openstack_pools'] + pools = node['ceph']['openstack_pools'] + + pools = Hash[(0...pools.size).zip pools] unless pools.kind_of? Hash + + pools.each do |index, ceph_pools| + unless ceph_pools['status'].nil? + Chef::Log.info("osd pools: ceph_pools #{ceph_pools['pool_name']} has already been create.") + next + end + + execute "create #{ceph_pools['pool_name']} pool" do + command "ceph osd pool create #{ceph_pools['pool_name']} #{node['ceph']['config']['global']['osd pool default pg num']}" + notifies :create, "ruby_block[save osd pools status #{index}]", :immediately + end + + ruby_block "save osd pools status #{index}" do + block do + node.normal['ceph']['openstack_pools'][index]['status'] = 'created' + node.save + end + action :nothing + end + end +end + +#generate the openstack cinder secret +if node['ceph']['cinder-secret'].nil? + keyring1 = "client.cinder" + execute 'generate cinder-secret as keyring' do + command "ceph auth get-or-create #{keyring1} mon 'allow r' osd 'allow class-read object_prefix rbd_children, allow rwx pool=volumes, allow rwx pool=vms, allow rx pool=images'" + notifies :create, 'ruby_block[save cinder-secret]', :immediately + end + + ruby_block 'save cinder-secret' do + block do + fetch1 = Mixlib::ShellOut.new("ceph auth print_key '#{keyring1}'") + fetch1.run_command + key1 = fetch1.stdout + node.set['ceph']['cinder-secret'] = key1 + node.save + end + action :nothing + end +end + +#generate the openstack glance secret +if node['ceph']['glance-secret'].nil? + keyring2 = "client.glance" + execute 'generate cinder-secret as keyring' do + command "ceph auth get-or-create #{keyring2} mon 'allow r' osd 'allow class-read object_prefix rbd_children, allow rwx pool=images'" + notifies :create, 'ruby_block[save glance-secret]', :immediately + end + + ruby_block 'save glance-secret' do + block do + fetch2 = Mixlib::ShellOut.new("ceph auth print_key '#{keyring2}'") + fetch2.run_command + key2 = fetch2.stdout + node.set['ceph']['glance-secret'] = key2 + node.save + end + action :nothing + end +end \ No newline at end of file diff --git a/chef/cookbooks/ceph/recipes/openstack_config_radosgw.rb b/chef/cookbooks/ceph/recipes/openstack_config_radosgw.rb new file mode 100644 index 0000000..68a8ee3 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/openstack_config_radosgw.rb @@ -0,0 +1,100 @@ +# +# Author:: Kyle Bader +# Cookbook Name:: ceph +# Recipe:: radosgw +# +# Copyright 2011, Liucheng +# +# 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. +node.default['ceph']['is_keystone_integration'] = false + +if node['ceph']['is_keystone_integration'] + keystone_master = node_election('os-identity', 'keystone_keygen', node['ceph']['keystone environment']) + puts "****************keystone_master:#{keystone_master}" + if keystone_master['openstack']['endpoints']['identity-bind']['host'].nil? + Chef::Log.debug \ + "Chef-client exit for keystone endpoint bind host on #{keystone_master.name})" + exit 1 + end + node.default['ceph']['config']['keystone']['rgw keystone url'] = "#{keystone_master['openstack']['endpoints']['identity-bind']['host']}:35357" + + template '/etc/ceph/ceph.conf' do + source 'ceph.conf.erb' + variables lazy { + { + :mon_addresses => mon_addresses, + :is_rgw => node['ceph']['is_radosgw'], + :is_keystone_integration => node['ceph']['is_keystone_integration'] + } + } + mode '0644' + end + + %w{certfile ca_certs}.each do |name| + if !keystone_master['openstack']['identity']['signing'].attribute?("#{name}_data") + Chef::Log.debug \ + "Chef-client exit for PKI files from node #{keystone_master.name})" + exit 1 + end + file node['ceph']['radosgw']['signing']["#{name}"] do + content keystone_master['openstack']['identity']['signing']["#{name}_data"] + owner 'root' + group 'root' + mode 00640 + end + end + + directory node['ceph']['config']['keystone']['nss db path'] do + owner 'apache' + group 'apache' + mode 00755 + recursive true + action :create + end + + if !::File.exist?("#{node['ceph']['config']['keystone']['nss db path']}/done") + execute 'config ca.pem' do + command "openssl x509 -in #{node['ceph']['radosgw']['signing']['ca_certs']} -pubkey | certutil -d /var/ceph/nss -A -n ca -t \"TCu,Cu,Tuw\"" + end + + execute 'config signing_cert.pem' do + command "openssl x509 -in #{node['ceph']['radosgw']['signing']['certfile']} -pubkey | certutil -A -d /var/ceph/nss -n signing_cert -t \"P,P,P\"" + end + + execute 'change owner of nss' do + command "chown apache:apache -R #{node['ceph']['config']['keystone']['nss db path']}" + end + + file "#{node['ceph']['config']['keystone']['nss db path']}/done" do + action :create + end + + end + + service 'ceph-radosgw' do + case node['ceph']['radosgw']['init_style'] + when 'upstart' + service_name 'radosgw-all-starter' + provider Chef::Provider::Service::Upstart + else + if node['platform'] == 'debian' + service_name 'radosgw' + else + service_name 'ceph-radosgw' + end + end + supports :restart => true + action [:enable, :start] + subscribes :restart, resources('template[/etc/ceph/ceph.conf]') + end +end diff --git a/chef/cookbooks/ceph/recipes/osd.rb b/chef/cookbooks/ceph/recipes/osd.rb new file mode 100644 index 0000000..3c1a3ff --- /dev/null +++ b/chef/cookbooks/ceph/recipes/osd.rb @@ -0,0 +1,193 @@ +# +# Author:: Kyle Bader +# Cookbook Name:: ceph +# Recipe:: osd +# +# Copyright 2011, DreamHost Web Hosting +# +# 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. + +# this recipe allows bootstrapping new osds, with help from mon +# Sample environment: +# #knife node edit ceph1 +# "osd_devices": [ +# { +# "device": "/dev/sdc" +# }, +# { +# "device": "/dev/sdd", +# "dmcrypt": true, +# "journal": "/dev/sdd" +# } +# ] + +include_recipe 'ceph::_common' +include_recipe 'ceph::osd_install' +include_recipe 'ceph::conf' + +package 'gdisk' do + action :upgrade +end + +package 'cryptsetup' do + action :upgrade + only_if { node['dmcrypt'] } +end + +service_type = node['ceph']['osd']['init_style'] + +directory '/var/lib/ceph/bootstrap-osd' do + owner 'root' + group 'root' + mode '0755' +end + +# TODO: cluster name +cluster = 'ceph' + +execute 'format bootstrap-osd as keyring' do + command lazy { "ceph-authtool '/var/lib/ceph/bootstrap-osd/#{cluster}.keyring' --create-keyring --name=client.bootstrap-osd --add-key='#{osd_secret}'" } + creates "/var/lib/ceph/bootstrap-osd/#{cluster}.keyring" + only_if { osd_secret } +end + +node_osds = node['ceph']['osd_devices'] + +if node_osds.nil? or node_osds.empty? + osd_device = [] + ssd_disk = ssd_device + ssd_index = 0 + # search normal osd device + node['block_device'].each do |device| + device_hash = Hash.new + device_name = device[0] + if device_name.include?"sd" + # whether the storage device is in use + device_ssd_flag = Mixlib::ShellOut.new("cat /sys/block/#{device_name}/queue/rotational").run_command.stdout.strip + device_partion_num = Mixlib::ShellOut.new("cat /proc/partitions | grep #{device_name} -c").run_command.stdout.strip + if device_partion_num == "1" and device_ssd_flag == "1" + device_hash['device'] = "/dev/#{device_name}" + unless ssd_disk.empty? + ssd_index = (ssd_index >= ssd_disk.length ? 0 : ssd_index) + ssd_partion = nil + while ssd_partion.nil? + if ssd_index >= ssd_disk.length + break + end + ssd_partion = create_disk_partion(ssd_disk[ssd_index]) + ssd_index = ssd_index + 1 + end + ssd_index = ssd_index + 1 + end + device_hash['journal'] = ssd_partion unless ssd_partion.nil? + end + osd_device << device_hash unless device_hash.empty? + else + next + end + node.normal['ceph']['osd_devices'] = osd_device + node.save + node_osds = osd_device + Log.info("osd_devices are #{node['ceph']['osd_devices']}") + end +end + +if crowbar? + node['crowbar']['disks'].each do |disk, _data| + execute "ceph-disk-prepare #{disk}" do + command "ceph-disk-prepare /dev/#{disk}" + only_if { node['crowbar']['disks'][disk]['usage'] == 'Storage' } + notifies :run, 'execute[udev trigger]', :immediately + end + + ruby_block "set disk usage for #{disk}" do + block do + node.set['crowbar']['disks'][disk]['usage'] = 'ceph-osd' + node.save + end + end + end + + execute 'udev trigger' do + command 'udevadm trigger --subsystem-match=block --action=add' + action :nothing + end +else + # Calling ceph-disk-prepare is sufficient for deploying an OSD + # After ceph-disk-prepare finishes, the new device will be caught + # by udev which will run ceph-disk-activate on it (udev will map + # the devices if dm-crypt is used). + # IMPORTANT: + # - Always use the default path for OSD (i.e. /var/lib/ceph/ + # osd/$cluster-$id) + # - $cluster should always be ceph + # - The --dmcrypt option will be available starting w/ Cuttlefish + if node_osds + devices = node_osds + + devices = Hash[(0...devices.size).zip devices] unless devices.kind_of? Hash + + devices.each do |index, osd_device| + unless osd_device['status'].nil? + Log.info("osd: osd_device #{osd_device} has already been setup.") + next + end + + directory osd_device['device'] do # ~FC022 + owner 'root' + group 'root' + recursive true + only_if { osd_device['type'] == 'directory' } + end + + dmcrypt = osd_device['encrypted'] == true ? '--dmcrypt' : '' + + execute "ceph-disk-prepare on #{osd_device['device']}" do + command "ceph-disk-prepare #{dmcrypt} #{osd_device['device']} #{osd_device['journal']}" + action :run + notifies :create, "ruby_block[save osd_device status #{index}]", :immediately + end + + execute "ceph-disk-activate #{osd_device['device']}" do + only_if { osd_device['type'] == 'directory' } + end + + # we add this status to the node env + # so that we can implement recreate + # and/or delete functionalities in the + # future. + ruby_block "save osd_device status #{index}" do + block do + node.normal['ceph']['osd_devices'][index]['status'] = 'deployed' + node.save + end + action :nothing + end + end + else + Log.info('node["ceph"]["osd_devices"] empty') + end +end + +service 'ceph_osd' do + case service_type + when 'upstart' + service_name 'ceph-osd-all-starter' + provider Chef::Provider::Service::Upstart + else + service_name 'ceph' + end + action [:enable, :start] + supports :restart => true + subscribes :restart, resources('template[/etc/ceph/ceph.conf]') +end diff --git a/chef/cookbooks/ceph/recipes/osd_install.rb b/chef/cookbooks/ceph/recipes/osd_install.rb new file mode 100644 index 0000000..21991ad --- /dev/null +++ b/chef/cookbooks/ceph/recipes/osd_install.rb @@ -0,0 +1,5 @@ +include_recipe 'ceph::_common_install' + +node['ceph']['osd']['packages'].each do |pck| + package pck +end diff --git a/chef/cookbooks/ceph/recipes/radosgw.rb b/chef/cookbooks/ceph/recipes/radosgw.rb new file mode 100644 index 0000000..127d78f --- /dev/null +++ b/chef/cookbooks/ceph/recipes/radosgw.rb @@ -0,0 +1,103 @@ +# +# Author:: Kyle Bader +# Cookbook Name:: ceph +# Recipe:: radosgw +# +# Copyright 2011, DreamHost Web Hosting +# +# 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. + +node.default['ceph']['is_radosgw'] = true + +include_recipe 'ceph::_common' +include_recipe 'ceph::radosgw_install' +include_recipe 'ceph::conf' + +directory '/var/run/ceph' do + owner 'apache' + group 'apache' + mode 00755 + recursive true + action :create +end + +if !::File.exist?("/var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']}/done") + if node['ceph']['radosgw']['webserver_companion'] + include_recipe "ceph::radosgw_#{node['ceph']['radosgw']['webserver_companion']}" + end + + ceph_client 'radosgw' do + caps('mon' => 'allow rw', 'osd' => 'allow rwx') + end + + d_owner = d_group = 'apache' + %W( + /etc/ceph/ceph.client.radosgw.#{node['hostname']}.keyring + /var/log/ceph/radosgw.log + ).each do |f| + file f do + owner d_owner + group d_group + action :create + end + end + + directory "/var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']}" do + recursive true + end + + file "/var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']}/done" do + action :create + end + + service 'radosgw' do + case node['ceph']['radosgw']['init_style'] + when 'upstart' + service_name 'radosgw-all-starter' + provider Chef::Provider::Service::Upstart + else + if node['platform'] == 'debian' + service_name 'radosgw' + else + service_name 'ceph-radosgw' + end + end + supports :restart => true + action [:enable, :start] + end + + execute 'set selinux permissive' do + command "setenforce 0" + not_if {selinux_disabled?} + end + +else + Log.info('Rados Gateway already deployed') +end + +service 'radosgw' do + case node['ceph']['radosgw']['init_style'] + when 'upstart' + service_name 'radosgw-all-starter' + provider Chef::Provider::Service::Upstart + else + if node['platform'] == 'debian' + service_name 'radosgw' + else + service_name 'ceph-radosgw' + end + end + supports :restart => true + action [:enable, :start] + subscribes :restart, resources('template[/etc/ceph/ceph.conf]') +end diff --git a/chef/cookbooks/ceph/recipes/radosgw_apache2.rb b/chef/cookbooks/ceph/recipes/radosgw_apache2.rb new file mode 100644 index 0000000..1cfad8a --- /dev/null +++ b/chef/cookbooks/ceph/recipes/radosgw_apache2.rb @@ -0,0 +1,104 @@ +# +# Author:: Kyle Bader +# Cookbook Name:: ceph +# Recipe:: radosgw_apache2 +# +# Copyright 2011, DreamHost Web Hosting +# +# 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. + +# For EL, delete the current fastcgi configuration +# and set the correct owners for dirs and logs +# d_owner = d_group = 'root' +# if node['platform_family'] == 'rhel' +# file "#{node['apache']['dir']}/conf.d/fastcgi.conf" do +# action :delete +# backup false +# end +# d_owner = d_group = 'apache' +# end + +# %W(/var/run/ceph +# /var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']} +# /var/lib/apache2/ +# ).each do |dir| +# directory dir do +# owner d_owner +# group d_group +# mode '0755' +# recursive true +# action :create +# end +# end + +include_recipe 'ceph::_common' +include_recipe 'ceph::_common_install' +include_recipe 'ceph::radosgw_apache2_repo' + +node['ceph']['radosgw']['apache2']['packages'].each do |pck| + package pck +end + +include_recipe 'apache2' + +d_owner = d_group = 'root' +puts "**************************rgw_platform_family: #{node['platform_family']}" +if node['platform_family'] == 'rhel' + file "#{node['apache']['dir']}/conf.d/fastcgi.conf" do + action :delete + backup false + end + d_owner = d_group = 'apache' +end + +%W(/var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']} + /var/lib/apache2/ + /var/log/ceph/ +).each do |dir| + directory dir do + owner d_owner + group d_group + mode '0755' + recursive true + action :create + end +end + +apache_module 'fastcgi' do + conf true +end + +apache_module 'rewrite' do + conf false +end + +web_app 'rgw' do + template 'rgw.conf.erb' + server_name node['ceph']['radosgw']['api_fqdn'] + admin_email node['ceph']['radosgw']['admin_email'] + ceph_rgw_addr node['ceph']['radosgw']['rgw_addr'] +end + +service 'apache2' do + action :restart +end + +template '/var/www/s3gw.fcgi' do + source 's3gw.fcgi.erb' + owner 'root' + group 'root' + mode '0755' + variables( + :ceph_rgw_client => "client.radosgw.#{node['hostname']}" + ) +end diff --git a/chef/cookbooks/ceph/recipes/radosgw_apache2_repo.rb b/chef/cookbooks/ceph/recipes/radosgw_apache2_repo.rb new file mode 100644 index 0000000..86164b6 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/radosgw_apache2_repo.rb @@ -0,0 +1,33 @@ +if node['ceph']['radosgw']['use_apache_fork'] == true + if node.platform_family?('debian') && + %w(precise quantal raring saucy trusty squeeze wheezy).include?(node['lsb']['codename']) + apt_repository 'ceph-apache2' do + repo_name 'ceph-apache2' + uri "http://gitbuilder.ceph.com/apache2-deb-#{node['lsb']['codename']}-x86_64-basic/ref/master" + distribution node['lsb']['codename'] + components ['main'] + key 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc' + end + apt_repository 'ceph-modfastcgi' do + repo_name 'ceph-modfastcgi' + uri "http://gitbuilder.ceph.com/libapache-mod-fastcgi-deb-#{node['lsb']['codename']}-x86_64-basic/ref/master" + distribution node['lsb']['codename'] + components ['main'] + key 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc' + end + elsif (node.platform_family?('fedora') && [18, 19].include?(node['platform_version'].to_i)) || + (node.platform_family?('rhel') && [6].include?(node['platform_version'].to_i)) + platform_family = node['platform_family'] + platform_version = node['platform_version'].to_i + yum_repository 'ceph-apache2' do + baseurl "http://gitbuilder.ceph.com/apache2-rpm-#{node['platform']}#{platform_version}-x86_64-basic/ref/master" + gpgkey node['ceph'][platform_family]['dev']['repository_key'] + end + yum_repository 'ceph-modfastcgi' do + baseurl "http://gitbuilder.ceph.com/mod_fastcgi-rpm-#{node['platform']}#{platform_version}-x86_64-basic/ref/master" + gpgkey node['ceph'][platform_family]['dev']['repository_key'] + end + else + Log.info("Ceph's Apache and Apache FastCGI forks not available for this distribution") + end +end \ No newline at end of file diff --git a/chef/cookbooks/ceph/recipes/radosgw_install.rb b/chef/cookbooks/ceph/recipes/radosgw_install.rb new file mode 100644 index 0000000..7944d49 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/radosgw_install.rb @@ -0,0 +1,5 @@ +include_recipe 'ceph::_common_install' + +node['ceph']['radosgw']['packages'].each do |pck| + package pck +end diff --git a/chef/cookbooks/ceph/recipes/repo.rb b/chef/cookbooks/ceph/recipes/repo.rb new file mode 100644 index 0000000..6ef37de --- /dev/null +++ b/chef/cookbooks/ceph/recipes/repo.rb @@ -0,0 +1,8 @@ +case node['platform_family'] +when 'debian' + include_recipe 'ceph::apt' +when 'rhel', 'suse', 'fedora' + include_recipe 'ceph::rpm' +else + fail 'not supported' +end diff --git a/chef/cookbooks/ceph/recipes/rpm.rb b/chef/cookbooks/ceph/recipes/rpm.rb new file mode 100644 index 0000000..5c93fd2 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/rpm.rb @@ -0,0 +1,37 @@ +platform_family = node['platform_family'] + +case platform_family +when 'rhel' + include_recipe 'yum-epel' if node['ceph']['el_add_epel'] +end + +branch = node['ceph']['branch'] +if branch == 'dev' && platform_family != 'centos' && platform_family != 'fedora' + fail "Dev branch for #{platform_family} is not yet supported" +end + +package 'yum-plugin-priorities' + +yum_repository 'ceph' do + baseurl node['ceph'][platform_family][branch]['repository'] + gpgkey node['ceph'][platform_family][branch]['repository_key'] + priority '1' +end + +yum_repository 'ceph-extra' do + baseurl node['ceph'][platform_family]['extras']['repository'] + gpgkey node['ceph'][platform_family]['extras']['repository_key'] + priority '1' + only_if { node['ceph']['extras_repo'] } +end + + +package 'parted' # needed by ceph-disk-prepare to run partprobe +package 'hdparm' # used by ceph-disk activate +package 'xfsprogs' # needed by ceph-disk-prepare to format as xfs +if node['platform_family'] == 'rhel' && node['platform_version'].to_f > 6 + package 'btrfs-progs' # needed to format as btrfs, in the future +end +if node['platform_family'] == 'rhel' && node['platform_version'].to_f < 7 + package 'python-argparse' +end \ No newline at end of file diff --git a/chef/cookbooks/ceph/recipes/tgt.rb b/chef/cookbooks/ceph/recipes/tgt.rb new file mode 100644 index 0000000..eb353e9 --- /dev/null +++ b/chef/cookbooks/ceph/recipes/tgt.rb @@ -0,0 +1,51 @@ +# +# Author:: Kyle Bader +# Cookbook Name:: ceph +# Recipe:: radosgw +# +# Copyright 2011, DreamHost Web Hosting +# +# 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. + +node.default['ceph']['extras_repo'] = true + +case node['platform_family'] +when 'debian' + packages = %w( + tgt + ) +when 'rhel', 'fedora' + packages = %w( + scsi-target-utils + ) +end + +packages.each do |pkg| + package pkg do + action :upgrade + end +end + +include_recipe 'ceph::conf' +# probably needs the key +service 'tgt' do + if node['platform'] == 'ubuntu' + # The ceph version of tgt does not provide an Upstart script + provider Chef::Provider::Service::Init::Debian + service_name 'tgt' + else + service_name 'tgt' + end + supports :restart => true + action [:enable, :start] +end diff --git a/chef/cookbooks/ceph/resources/client.rb b/chef/cookbooks/ceph/resources/client.rb new file mode 100644 index 0000000..2428b34 --- /dev/null +++ b/chef/cookbooks/ceph/resources/client.rb @@ -0,0 +1,24 @@ +actions :add +default_action :add + +attribute :name, :kind_of => String, :name_attribute => true +attribute :caps, :kind_of => Hash, :default => { 'mon' => 'allow r', 'osd' => 'allow r' } + +# Whether to store the secret in a keyring file or a plain secret file +attribute :as_keyring, :kind_of => [TrueClass, FalseClass], :default => true + +# what the key should be called in the ceph cluster +# defaults to client.#{name}.#{hostname} +attribute :keyname, :kind_of => String + +# where the key should be saved +# defaults to /etc/ceph/ceph.client.#{name}.#{hostname}.keyring if as_keyring +# defaults to /etc/ceph/ceph.client.#{name}.#{hostname}.secret if not as_keyring +attribute :filename, :kind_of => String + +# key file access creds +attribute :owner, :kind_of => String, :default => 'root' +attribute :group, :kind_of => String, :default => 'root' +attribute :mode, :kind_of => [Integer, String], :default => '00640' + +attr_accessor :key, :caps_match diff --git a/chef/cookbooks/ceph/templates/default/ceph.client.keyring.erb b/chef/cookbooks/ceph/templates/default/ceph.client.keyring.erb new file mode 100644 index 0000000..a1edaa4 --- /dev/null +++ b/chef/cookbooks/ceph/templates/default/ceph.client.keyring.erb @@ -0,0 +1,2 @@ +[client.<%= @name -%>] + key = <%= @key %> diff --git a/chef/cookbooks/ceph/templates/default/ceph.conf.erb b/chef/cookbooks/ceph/templates/default/ceph.conf.erb new file mode 100644 index 0000000..4088219 --- /dev/null +++ b/chef/cookbooks/ceph/templates/default/ceph.conf.erb @@ -0,0 +1,59 @@ +[global] + fsid = <%= node["ceph"]["config"]["fsid"] %> + mon initial members = <%= node["ceph"]["config"]["mon_initial_members"] %> + mon host = <%= @mon_addresses.sort.join(', ') %> +<% if (! node['ceph']['config']['global'].nil?) -%> + <% node['ceph']['config']['global'].sort.each do |k, v| %> + <%= k %> = <%= v %> + <% end %> +<% end -%> + +<% if (! node['ceph']['config']['osd'].nil?) -%> +[osd] + <% node['ceph']['config']['osd'].sort.each do |k, v| %> + <% if ssd_device%> + <% end %> + <%= k %> = <%= v %> + <% end %> +<% end -%> + +<% if (! node['ceph']['config']['mon'].nil?) -%> +[mon] + <% node['ceph']['config']['mon'].sort.each do |k, v| %> + <%= k %> = <%= v %> + <% end %> +<% end -%> + +<% if (! node['ceph']['config']['mds'].nil?) -%> +[mds] + <% node['ceph']['config']['mds'].sort.each do |key, value| -%> + <%= key %> = <%= value %> + <% end -%> +<% end -%> + +<% if (@is_rgw) -%> +[client.radosgw.<%= node['hostname'] %>] + host = <%= node['hostname'] %> + rgw socket path = /var/run/ceph/radosgw.<%= node['hostname'] %> + keyring = /etc/ceph/ceph.client.radosgw.<%= node['hostname'] %>.keyring + log file = /var/log/ceph/radosgw.log +<% if (! node['ceph']['config']['rgw'].nil?) -%> + <% node['ceph']['config']['rgw'].sort.each do |k, v| %> + <%= k %> = <%= v %> + <% end %> +<% end -%> +<% if (@is_keystone_integration) -%> +<% if (! node['ceph']['config']['keystone'].nil?) -%> + <% node['ceph']['config']['keystone'].sort.each do |k, v| %> + <%= k %> = <%= v %> + <% end %> +<% end -%> +<% end -%> +<% end -%> + +<% node['ceph']['config-sections'].sort.each do |name, sect| %> +[<%= name %>] + <% sect.sort.each do |k, v| %> + <%= k %> = <%= v %> + <% end %> +<% end %> diff --git a/chef/cookbooks/ceph/templates/default/mods/fastcgi.conf.erb b/chef/cookbooks/ceph/templates/default/mods/fastcgi.conf.erb new file mode 100644 index 0000000..e46d1b7 --- /dev/null +++ b/chef/cookbooks/ceph/templates/default/mods/fastcgi.conf.erb @@ -0,0 +1,6 @@ + + AddHandler fastcgi-script .fcgi + #FastCgiWrapper /usr/lib/apache2/suexec + FastCgiIpcDir /var/lib/apache2/fastcgi + FastCgiWrapper off + diff --git a/chef/cookbooks/ceph/templates/default/rgw.conf.erb b/chef/cookbooks/ceph/templates/default/rgw.conf.erb new file mode 100644 index 0000000..a9431cd --- /dev/null +++ b/chef/cookbooks/ceph/templates/default/rgw.conf.erb @@ -0,0 +1,39 @@ +<% if node['ceph']['radosgw']['rgw_port'] -%> +FastCgiExternalServer /var/www/s3gw.fcgi -host 127.0.0.1:<%= node['ceph']['radosgw']['rgw_port'] %> +<% else -%> +FastCgiExternalServer /var/www/s3gw.fcgi -socket /var/run/ceph/radosgw.<%= node['hostname'] %> +<% end -%> + +LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" \"%{Host}i\"" proxy_combined +LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" \"%{Host}i\"" proxy_debug + +> + ServerName <%= @params[:server_name] %> +<% if node['ceph']['radosgw']['api_aliases'] -%> +<% node['ceph']['radosgw']['api_aliases'].each do |api_alias| -%> + ServerAlias <%= api_alias %> +<% end -%> +<% end -%> + ServerAdmin <%= node["ceph"]["radosgw"]["admin_email"] %> + DocumentRoot /var/www/ + + RewriteEngine On + RewriteRule ^/(.*) /s3gw.fcgi?%{QUERY_STRING} [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] + + + + Options +ExecCGI + AllowOverride All + SetHandler fastcgi-script + Order allow,deny + Allow from all + AuthBasicAuthoritative Off + + + + AllowEncodedSlashes On + + ErrorLog /var/log/<%= node['apache']['package'] %>/error.log + CustomLog /var/log/<%= node['apache']['package'] %>/rgw-access.log proxy_combined + ServerSignature Off + diff --git a/chef/cookbooks/ceph/templates/default/s3gw.fcgi.erb b/chef/cookbooks/ceph/templates/default/s3gw.fcgi.erb new file mode 100644 index 0000000..c6b684f --- /dev/null +++ b/chef/cookbooks/ceph/templates/default/s3gw.fcgi.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/radosgw -c /etc/ceph/ceph.conf -n <%= @ceph_rgw_client %> diff --git a/chef/cookbooks/ceph/test/integration/Vagrantfile.erb b/chef/cookbooks/ceph/test/integration/Vagrantfile.erb new file mode 100644 index 0000000..8266d08 --- /dev/null +++ b/chef/cookbooks/ceph/test/integration/Vagrantfile.erb @@ -0,0 +1,10 @@ +Vagrant.configure("2") do |config| + config.vm.box = "<%= config[:box] %>" + config.vm.box_url = "<%= config[:box_url ]%>" + (0..2).each do |d| + config.vm.provider :virtualbox do |vb| + vb.customize [ "createhd", "--filename", "disk-#{d}", "--size", "1000" ] + vb.customize [ "storageattach", :id, "--storagectl", "IDE Controller", "--device", (1+d)/2, "--port", (1+d)%2, "--type", "hdd", "--medium", "disk-#{d}.vdi" ] + end + end +end diff --git a/chef/cookbooks/ceph/test/integration/aio/bats/ceph-running.bats b/chef/cookbooks/ceph/test/integration/aio/bats/ceph-running.bats new file mode 100644 index 0000000..578d7e4 --- /dev/null +++ b/chef/cookbooks/ceph/test/integration/aio/bats/ceph-running.bats @@ -0,0 +1,19 @@ +@test "ceph is running" { + ceph -s | grep HEALTH +} + +@test "ceph is healthy" { + ceph -s | grep HEALTH_OK +} + +@test "cephfs is mounted" { + mount | grep 'type ceph' +} + +@test "radosgw is running" { + ps auxwww | grep radosg[w] +} + +@test "apache is running and listening" { + netstat -ln | grep -E '^\S+\s+\S+\s+\S+\s+\S+:80\s+' +} diff --git a/chef/cookbooks/chef_handler/CHANGELOG.md b/chef/cookbooks/chef_handler/CHANGELOG.md index a249ec3..ed380f4 100644 --- a/chef/cookbooks/chef_handler/CHANGELOG.md +++ b/chef/cookbooks/chef_handler/CHANGELOG.md @@ -1,28 +1,44 @@ -## v1.1.4: +chef_handler cookbook CHANGELOG +=============================== -* [COOK-2146] - style updates +v1.1.6 (2014-04-09) +------------------- +[COOK-4494] - Add ChefSpec matchers -## v1.1.2: -* [COOK-1989] - fix scope for handler local variable to the enable block +v1.1.5 (2014-02-25) +------------------- +- [COOK-4117] - use the correct scope when searching the children class name -## v1.1.0: -* [COOK-1645] - properly delete old handlers -* [COOK-1322] - support platforms that use 'wheel' as root group' +v1.1.4 +------ +- [COOK-2146] - style updates -## v1.0.8: +v1.1.2 +--------- +- [COOK-1989] - fix scope for handler local variable to the enable block -* [COOK-1177] - doesn't work on windows due to use of unix specific attributes -## v1.0.6: +v1.1.0 +------ -* [COOK-1069] - typo in chef_handler readme +- [COOK-1645] - properly delete old handlers +- [COOK-1322] - support platforms that use 'wheel' as root group' -## v1.0.4: +v1.0.8 +------ +- [COOK-1177] - doesn't work on windows due to use of unix specific attributes -* [COOK-654] dont try and access a class before it has been loaded -* fix bad boolean check (if vs unless) +v1.0.6 +------ +- [COOK-1069] - typo in chef_handler readme -## v1.0.2: +v1.0.4 +------ +- [COOK-654] dont try and access a class before it has been loaded +- fix bad boolean check (if vs unless) -* [COOK-620] ensure handler code is reloaded during daemonized chef runs +v1.0.2 +------ +- [COOK-620] ensure handler code is reloaded during daemonized chef runs + diff --git a/chef/cookbooks/chef_handler/CONTRIBUTING b/chef/cookbooks/chef_handler/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/chef_handler/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/chef_handler/LICENSE b/chef/cookbooks/chef_handler/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/chef_handler/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/chef_handler/README.md b/chef/cookbooks/chef_handler/README.md index ab99822..06f9347 100644 --- a/chef/cookbooks/chef_handler/README.md +++ b/chef/cookbooks/chef_handler/README.md @@ -1,7 +1,7 @@ Description =========== -Creates a configured handler path for distributing [Chef report and exception handlers](http://wiki.opscode.com/display/chef/Exception+and+Report+Handlers). Also exposes an LWRP for enabling Chef handlers from within recipe code (as opposed to hard coding in the client.rb file). This is useful for cookbook authors who may want to ship a product specific handler (see the `cloudkick` cookbook for an example) with their cookbook. +Creates a configured handler path for distributing [Chef report and exception handlers](http://docs.opscode.com/handlers.html). Also exposes an LWRP for enabling Chef handlers from within recipe code (as opposed to hard coding in the client.rb file). This is useful for cookbook authors who may want to ship a product specific handler (see the `cloudkick` cookbook for an example) with their cookbook. Attributes ========== diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cluster_test.rb b/chef/cookbooks/chef_handler/libraries/matchers.rb similarity index 57% rename from chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cluster_test.rb rename to chef/cookbooks/chef_handler/libraries/matchers.rb index 0ffb839..bfebc33 100644 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cluster_test.rb +++ b/chef/cookbooks/chef_handler/libraries/matchers.rb @@ -1,5 +1,9 @@ # -# Copyright 2012, Opscode, Inc. +# Author:: Douglas Thrift () +# Cookbook Name:: chef_handler +# Library:: matchers +# +# Copyright 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,16 +18,12 @@ # limitations under the License. # -describe "rabbitmq_test::cluster" do - - it 'writes the erlang cookie file' do - file("/var/lib/rabbitmq/.erlang.cookie").must_exist +if defined?(ChefSpec) + def enable_chef_handler(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:chef_handler, :enable, resource_name) end - it 'writes cluster configuration to the config file' do - file("/etc/rabbitmq/rabbitmq.conf").must_match( - /^ {cluster_nodes, [.*]},$/ - ) + def disable_chef_handler(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:chef_handler, :disable, resource_name) end - end diff --git a/chef/cookbooks/chef_handler/metadata.json b/chef/cookbooks/chef_handler/metadata.json new file mode 100644 index 0000000..274c87f --- /dev/null +++ b/chef/cookbooks/chef_handler/metadata.json @@ -0,0 +1,29 @@ +{ + "name": "chef_handler", + "version": "1.1.6", + "description": "Distribute and enable Chef Exception and Report handlers", + "long_description": "Description\n===========\n\nCreates a configured handler path for distributing [Chef report and exception handlers](http://docs.opscode.com/handlers.html). Also exposes an LWRP for enabling Chef handlers from within recipe code (as opposed to hard coding in the client.rb file). This is useful for cookbook authors who may want to ship a product specific handler (see the `cloudkick` cookbook for an example) with their cookbook.\n\nAttributes\n==========\n\n`node[\"chef_handler\"][\"handler_path\"]` - location to drop off handlers directory, default is `/var/chef/handlers`.\n\nResource/Provider\n=================\n\n`chef_handler`\n--------------\n\nRequires, configures and enables handlers on the node for the current Chef run. Also has the ability to pass arguments to the handlers initializer. This allows initialization data to be pulled from a node's attribute data.\n\nIt is best to declare `chef_handler` resources early on in the compile phase so they are available to fire for any exceptions during the Chef run. If you have a base role you would want any recipes that register Chef handlers to come first in the run_list.\n\n### Actions\n\n- :enable: Enables the Chef handler for the current Chef run on the current node\n- :disable: Disables the Chef handler for the current Chef run on the current node\n\n### Attribute Parameters\n\n- class_name: name attribute. The name of the handler class (can be module name-spaced).\n- source: full path to the handler file. can also be a gem path if the handler ships as part of a Ruby gem.\n- arguments: an array of arguments to pass the handler's class initializer\n- supports: type of Chef Handler to register as, ie :report, :exception or both. default is `:report => true, :exception => true`\n\n### Example\n\n # register the Chef::Handler::JsonFile handler\n # that ships with the Chef gem\n chef_handler \"Chef::Handler::JsonFile\" do\n source \"chef/handler/json_file\"\n arguments :path => '/var/chef/reports'\n action :enable\n end\n\n # do the same but during the compile phase\n chef_handler \"Chef::Handler::JsonFile\" do\n source \"chef/handler/json_file\"\n arguments :path => '/var/chef/reports'\n action :nothing\n end.run_action(:enable)\n\n # handle exceptions only\n chef_handler \"Chef::Handler::JsonFile\" do\n source \"chef/handler/json_file\"\n arguments :path => '/var/chef/reports'\n supports :exception => true\n action :enable\n end\n\n\n # enable the CloudkickHandler which was\n # dropped off in the default handler path.\n # passes the oauth key/secret to the handler's\n # intializer.\n chef_handler \"CloudkickHandler\" do\n source \"#{node['chef_handler']['handler_path']}/cloudkick_handler.rb\"\n arguments [node['cloudkick']['oauth_key'], node['cloudkick']['oauth_secret']]\n action :enable\n end\n\n\nUsage\n=====\n\ndefault\n-------\n\nPut the recipe `chef_handler` at the start of the node's run list to make sure that custom handlers are dropped off early on in the Chef run and available for later recipes.\n\nFor information on how to write report and exception handlers for Chef, please see the Chef wiki pages:\nhttp://wiki.opscode.com/display/chef/Exception+and+Report+Handlers\n\njson_file\n---------\n\nLeverages the `chef_handler` LWRP to automatically register the `Chef::Handler::JsonFile` handler that ships as part of Chef. This handler serializes the run status data to a JSON file located at `/var/chef/reports`.\n\nLicense and Author\n==================\n\nAuthor:: Seth Chisamore ()\n\nCopyright:: 2011, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + }, + "dependencies": { + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + } +} \ No newline at end of file diff --git a/chef/cookbooks/chef_handler/metadata.rb b/chef/cookbooks/chef_handler/metadata.rb index 703ae11..92f9527 100644 --- a/chef/cookbooks/chef_handler/metadata.rb +++ b/chef/cookbooks/chef_handler/metadata.rb @@ -4,4 +4,4 @@ maintainer_email "cookbooks@opscode.com" license "Apache 2.0" description "Distribute and enable Chef Exception and Report handlers" long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "1.1.4" +version "1.1.6" diff --git a/chef/cookbooks/chef_handler/providers/default.rb b/chef/cookbooks/chef_handler/providers/default.rb index 2eb2be7..aa2ee55 100644 --- a/chef/cookbooks/chef_handler/providers/default.rb +++ b/chef/cookbooks/chef_handler/providers/default.rb @@ -88,6 +88,10 @@ end def klass @klass ||= begin - @new_resource.class_name.split('::').inject(Kernel) { |scope, const_name| scope.const_get(const_name) } + # we need to search the ancestors only for the + # first/uppermost namespace of the class, so we need + # to enable the #const_get inherit paramenter only when + # we are searching in Kernel scope (see COOK-4117). + @new_resource.class_name.split('::').inject(Kernel) { |scope, const_name| scope.const_get(const_name, scope === Kernel) } end end diff --git a/chef/cookbooks/collectd/attributes/default.rb b/chef/cookbooks/collectd/attributes/default.rb index 4e65421..efe30e4 100644 --- a/chef/cookbooks/collectd/attributes/default.rb +++ b/chef/cookbooks/collectd/attributes/default.rb @@ -18,7 +18,29 @@ # default[:collectd][:base_dir] = "/var/lib/collectd" if platform_family?("rhel") - default[:collectd][:package_name] = ["collectd"] + default[:collectd][:package_name] = ["collectd", + "collectd-amqp", + "collectd-apache", + "collectd-dbi", + "collectd-email", + "collectd-gmond", + "collectd-java", + "collectd-libnotify", + "collectd-liboping", +# "collectd-libvirt", + "collectd-memcache", + "collectd-mysql", + "collectd-nginx", + "collectd-OpenIPMI", + "collectd-perl", + "collectd-postgresql", + "collectd-python", + "collectd-rrdtool", + "collectd-sensors", + "collectd-snmp", + "collectd-varnish" + ] + default[:collectd][:yum][:uri] = "http://12.133.183.203/repos/collectd/epel-6" default[:collectd][:plugin_dir] = "/usr/lib64/collectd" default[:collectd][:config_file] = "/etc/collectd.conf" elsif platform_family?("debian") @@ -42,7 +64,7 @@ default[:collectd][:plugins] = {"cpu"=>{}, "match_regex"=>"" } default[:collectd][:included_plugins] = {"kairosdb"=>{}} -default[:collectd][:server][:host] = "10.145.81.250" +default[:collectd][:server][:host] = "metrics" default[:collectd][:server][:port] = "4242" -default[:collectd][:server][:protocol] = "udp" +default[:collectd][:server][:protocol] = "tcp" default[:collectd][:mq][:vhost] = "/" diff --git a/chef/cookbooks/collectd/files/default/kairosdb_writer.py b/chef/cookbooks/collectd/files/default/kairosdb_writer.py index 3c19d9b..2e08fae 100644 --- a/chef/cookbooks/collectd/files/default/kairosdb_writer.py +++ b/chef/cookbooks/collectd/files/default/kairosdb_writer.py @@ -21,6 +21,7 @@ from traceback import format_exc host = None port = None +lowercase_metric_names = False prefix = None types = {} postfix = None diff --git a/chef/cookbooks/collectd/files/default/rabbitmq_info.py b/chef/cookbooks/collectd/files/default/rabbitmq_info.py index d492d48..fd9137d 100644 --- a/chef/cookbooks/collectd/files/default/rabbitmq_info.py +++ b/chef/cookbooks/collectd/files/default/rabbitmq_info.py @@ -53,7 +53,7 @@ def get_stats(): # call http api instead of rabbitmqctl to collect statistics due to issue: # https://github.com/phrawzty/rabbitmq-collectd-plugin/issues/5 try: - r = requests.get('%s/%s' % (RABBITMQ_API, VHOST), + r = requests.get('%s' % RABBITMQ_API, auth=('%s' % USER, '%s' % PASS)) # p = subprocess.Popen([RABBITMQCTL_BIN, '-q', '-p', VHOST, # 'list_queues', 'name', 'messages', 'memory', 'consumers'], @@ -83,13 +83,16 @@ def get_stats(): logger('err', 'No result found for this vhost') return None for i in resp: - if "messages" in i: - stats['ctl_messages'] += i['messages'] - stats['ctl_memory'] += i['memory'] - stats['ctl_consumers'] += i['consumers'] - stats['ctl_messages_%s' % i['name']] = i['messages'] - stats['ctl_memory_%s' % i['name']] = i['memory'] - stats['ctl_consumers_%s' % i['name']] = i['consumers'] + if i['vhost'] == VHOST: + if "messages" in i: + stats['ctl_messages'] += i['messages'] + stats['ctl_messages_%s' % i['name']] = i['messages'] + if "memory" in i: + stats['ctl_memory'] += i['memory'] + stats['ctl_memory_%s' % i['name']] = i['memory'] + if "consumers" in i: + stats['ctl_consumers'] += i['consumers'] + stats['ctl_consumers_%s' % i['name']] = i['consumers'] if not stats['ctl_memory'] > 0: logger('warn', '%s reports 0 memory usage. This is probably incorrect.' % RABBITMQ_API) diff --git a/chef/cookbooks/collectd/recipes/client.rb b/chef/cookbooks/collectd/recipes/client.rb index 3ab309c..2445f52 100644 --- a/chef/cookbooks/collectd/recipes/client.rb +++ b/chef/cookbooks/collectd/recipes/client.rb @@ -18,13 +18,15 @@ # include_recipe "collectd" -if node["collectd"].attribute?("rhel") or node["collectd"].attribute?("debian") - case node["platform_family"] - when "rhel" +case node["platform_family"] +when "rhel" + if node["collectd"].attribute?("rhel") if not node["collectd"]["rhel"]["plugins"].nil? node.override["collectd"]["plugins"]=node["collectd"]["rhel"]["plugins"].to_hash end - when "debian" + end +when "debian" + if node["collectd"].attribute?("debian") if not node["collectd"]["debian"]["plugins"].nil? node.override["collectd"]["plugins"]=node["collectd"]["debian"]["plugins"].to_hash end diff --git a/chef/cookbooks/collectd/recipes/default.rb b/chef/cookbooks/collectd/recipes/default.rb index f18dbda..b547875 100644 --- a/chef/cookbooks/collectd/recipes/default.rb +++ b/chef/cookbooks/collectd/recipes/default.rb @@ -18,12 +18,25 @@ # case node["platform_family"] when "rhel" - include_recipe "yum::epel" + include_recipe "yum-epel" + yum_repository "collectd" do + description "collectd and its plugins" + gpgcheck false + baseurl node["collectd"]["yum"]["uri"] + enabled true + action :add + end execute "yum-update" do user "root" command "yum -y update" action :run end +when "debian" + execute "apt-update" do + command "apt-get update" + ignore_failure true + action :run + end end node[:collectd][:package_name].each do |pkg| diff --git a/chef/cookbooks/collectd/recipes/kairosdb.rb b/chef/cookbooks/collectd/recipes/kairosdb.rb index c935f25..c2b9fcd 100644 --- a/chef/cookbooks/collectd/recipes/kairosdb.rb +++ b/chef/cookbooks/collectd/recipes/kairosdb.rb @@ -32,9 +32,9 @@ collectd_python_plugin "kairosdb_writer" do opts = {"KairosDBHost"=>node['collectd']['server']['host'], "KairosDBPort"=>node['collectd']['server']['port'], "KairosDBProtocol"=>node['collectd']['server']['protocol'], - "LowercaseMetricNames"=>"true", "Tags" => "host=#{node['fqdn']}\" \"role=OSROLE\" \"location=China.Beijing.TsingHua\" \"cluster=#{node['cluster']}", - "TypesDB" => node['collectd']['types_db'] + "TypesDB" => node['collectd']['types_db'], + "LowercaseMetricNames"=>"true" } options(opts) end diff --git a/chef/cookbooks/collectd/recipes/rabbitmq.rb b/chef/cookbooks/collectd/recipes/rabbitmq.rb index 854d739..2acdfc7 100644 --- a/chef/cookbooks/collectd/recipes/rabbitmq.rb +++ b/chef/cookbooks/collectd/recipes/rabbitmq.rb @@ -17,25 +17,10 @@ # limitations under the License. # -defaultbag = "openstack" -if !Chef::DataBag.list.key?(defaultbag) - Chef::Application.fatal!("databag '#{defaultbag}' doesn't exist.") - return -end - -myitem = node.attribute?('cluster')? node['cluster']:"env_default" - -if !search(defaultbag, "id:#{myitem}") - Chef::Application.fatal!("databagitem '#{myitem}' doesn't exist.") - return -end - package "python-requests" do action :install end -mydata = data_bag_item(defaultbag, myitem) - cookbook_file File.join(node['collectd']['plugin_dir'], "rabbitmq_info.py") do source "rabbitmq_info.py" owner "root" @@ -44,13 +29,13 @@ cookbook_file File.join(node['collectd']['plugin_dir'], "rabbitmq_info.py") do notifies :restart, resources(:service => "collectd") end -node.override["collectd"]["mq"]["vhost"] = mydata["mq"]["rabbitmq"]["vhost"] +node.override["collectd"]["mq"]["vhost"] = node["openstack"]["mq"]["vhost"] collectd_python_plugin "rabbitmq_info" do opts = { "Vhost" => node["collectd"]["mq"]["vhost"], "Api" => "http://localhost:15672/api/queues", - "User" => "#{mydata["credential"]["mq"]["rabbitmq"]["username"]}", - "Pass" => "#{mydata["credential"]["mq"]["rabbitmq"]["password"]}" + "User" => "#{node["openstack"]["mq"]["user"]}", + "Pass" => "#{node["openstack"]["mq"]["password"]}" } options(opts) end diff --git a/chef/cookbooks/database/CHANGELOG.md b/chef/cookbooks/database/CHANGELOG.md index d76e795..8da1f33 100644 --- a/chef/cookbooks/database/CHANGELOG.md +++ b/chef/cookbooks/database/CHANGELOG.md @@ -1,82 +1,106 @@ -## v1.4.0: +database Cookbook CHANGELOG +======================= +This file is used to list changes made in each version of the database cookbook. + +v2.0.0 (2014-02-25) +------------------- +[COOK-3441] database_user password argument should not be required + + +v1.6.0 +------ +### New Feature +- **[COOK-4009](https://tickets.opscode.com/browse/COOK-4009)** - Add PostgreSQL SCHEMA management capability + +### Improvement +- **[COOK-3862](https://tickets.opscode.com/browse/COOK-3862)** - Improve database cookbook documentation + + +v1.5.2 +------ +### Improvement +- **[COOK-3716](https://tickets.opscode.com/browse/COOK-3716)** - Add ALTER SQL Server user roles + + +v1.5.0 +------ +### Improvement +- **[COOK-3546](https://tickets.opscode.com/browse/COOK-3546)** - Add connection parameters `:socket` +- **[COOK-1709](https://tickets.opscode.com/browse/COOK-1709)** - Add 'grant_option' parameter + +v1.4.0 +------- ### Bug - -- [COOK-2074]: Regex in exists? check in `sql_server_database` resource - should match for start and end of line +- [COOK-2074]: Regex in exists? check in `sql_server_database` resource should match for start and end of line - [COOK-2561]: `mysql_database_user` can't set global grants ### Improvement -- [COOK-2075]: Support the collation attribute in the - `database_sql_server` provider +- [COOK-2075]: Support the collation attribute in the `database_sql_server` provider -## v1.3.12: +v1.3.12 +------- +- [COOK-850] - `postgresql_database_user` doesn't have example -* [COOK-850] - `postgresql_database_user` doesn't have example +v1.3.10 +------- +- [COOK-2117] - undefined variable `grant_statement` in mysql user provider -## v1.3.10: +v1.3.8 +------ +- [COOK-1896] - Escape command +- [COOK-2047] - Chef::Provider::Database::MysqlUser action :grant improperly quotes `username`@`host` string +- [COOK-2060] - Mysql::Error: Table '*.*' doesn't exist when privileges include SELECT and database/table attributes are nil +- [COOK-2062] - Remove backticks from database name when using wildcard -* [COOK-2117] - undefined variable `grant_statement` in mysql user - provider +v1.3.6 +------ +- [COOK-1688] - fix typo in readme and add amazon linux to supported platforms -## v1.3.8: +v1.3.4 +------ +- [COOK-1561] - depend on mysql 1.3.0+ explicitly +- depend on postgresql 1.0.0 explicitly -* [COOK-1896] - Escape command -* [COOK-2047] - Chef::Provider::Database::MysqlUser action :grant - improperly quotes `username`@`host` string -* [COOK-2060] - Mysql::Error: Table '*.*' doesn't exist when privileges - include SELECT and database/table attributes are nil -* [COOK-2062] - Remove backticks from database name when using wildcard +v1.3.2 +------ +- Update the version for release (oops) -## v1.3.6: +v1.3.0 +------ +- [COOK-932] - Add mysql recipe to conveniently include mysql::ruby +- [COOK-1228] - database resource should be able to execute scripts on disk +- [COOK-1291] - make the snapshot retention policy less confusing +- [COOK-1401] - Allow to specify the collation of new databases +- [COOK-1534] - Add postgresql recipe to conveniently include postgresql::ruby -* [COOK-1688] - fix typo in readme and add amazon linux to supported - platforms +v1.2.0 +------ +- [COOK-970] - workaround for disk [re]naming on ubuntu 11.04+ +- [COOK-1085] - check RUBY_VERSION and act accordingly for role +- [COOK-749] - localhost should be a string in snapshot recipe -## v1.3.4: +v1.1.4 +------ +- [COOK-1062] - Databases: Postgres exists should close connection -* [COOK-1561] - depend on mysql 1.3.0+ explicitly -* depend on postgresql 1.0.0 explicitly +v1.1.2 +------ +- [COOK-975] - Change arg='DEFAULT' to arg=nil, :default => 'DEFAULT' +- [COOK-964] - Add parentheses around connection hash in example -## v1.3.2: +v1.1.0 +------ +- [COOK-716] - providers for PostgreSQL -* Update the version for release (oops) +v1.0.0 +------ +- [COOK-683] - added `database` and `database_user` resources +- [COOK-684] - MySQL providers +- [COOK-685] - SQL Server providers +- refactored - `database::master` and `database::snapshot` recipes to leverage new resources -## v1.3.0: - -* [COOK-932] - Add mysql recipe to conveniently include mysql::ruby -* [COOK-1228] - database resource should be able to execute scripts on disk -* [COOK-1291] - make the snapshot retention policy less confusing -* [COOK-1401] - Allow to specify the collation of new databases -* [COOK-1534] - Add postgresql recipe to conveniently include postgresql::ruby - -## v1.2.0: - -* [COOK-970] - workaround for disk [re]naming on ubuntu 11.04+ -* [COOK-1085] - check RUBY_VERSION and act accordingly for role -* [COOK-749] - localhost should be a string in snapshot recipe - -## v1.1.4: - -* [COOK-1062] - Databases: Postgres exists should close connection - -## v1.1.2: - -* [COOK-975] - Change arg='DEFAULT' to arg=nil, :default => 'DEFAULT' -* [COOK-964] - Add parentheses around connection hash in example - -## v1.1.0 - -* [COOK-716] - providers for PostgreSQL - -## v1.0.0 - -* [COOK-683] - added `database` and `database_user` resources -* [COOK-684] - MySQL providers -* [COOK-685] - SQL Server providers -* refactored - `database::master` and `database::snapshot` recipes to leverage new resources - -## v0.99.1 - -* Use Chef 0.10's `node.chef_environment` instead of `node['app_environment']`. +v0.99.1 +------- +- Use Chef 0.10's `node.chef_environment` instead of `node['app_environment']`. diff --git a/chef/cookbooks/database/CONTRIBUTING b/chef/cookbooks/database/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/database/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/database/LICENSE b/chef/cookbooks/database/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/database/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/database/README.md b/chef/cookbooks/database/README.md index a7d209e..67ab62c 100644 --- a/chef/cookbooks/database/README.md +++ b/chef/cookbooks/database/README.md @@ -1,33 +1,19 @@ Database Cookbook ================= +The main highlight of this cookbook is the `database` and `database_user` resources for managing databases and database users in a RDBMS. Providers for MySQL, PostgreSQL and SQL Server are also provided, see usage documentation below. -The main highlight of this cookbook is the `database` and -`database_user` resources for managing databases and database users in -a RDBMS. Providers for MySQL, PostgreSQL and SQL Server are also -provided, see usage documentation below. +This cookbook also contains recipes to configure mysql database masters and slaves and uses EBS for storage, integrating together with the application cookbook utilizing data bags for application related information. These recipes are written primarily to use MySQL and the Opscode mysql cookbook. Other RDBMS may be supported at a later date. This cookbook does not automatically restore database dumps, but does install tools to help with that. -This cookbook also contains recipes to configure mysql database -masters and slaves and uses EBS for storage, integrating together with -the application cookbook utilizing data bags for application related -information. These recipes are written primarily to use MySQL and the -Opscode mysql cookbook. Other RDBMS may be supported at a later date. -This cookbook does not automatically restore database dumps, but does -install tools to help with that. Requirements -============ - +------------ Chef version 0.10.10+. -Platform --------- - +### Platforms * Debian, Ubuntu * Red Hat, CentOS, Scientific, Fedora, Amazon -Cookbooks ---------- - +### Cookbooks The following Opscode cookbooks are dependencies: * mysql @@ -35,398 +21,410 @@ The following Opscode cookbooks are dependencies: * xfs * aws + Resources/Providers -=================== +------------------- +These resources aim to expose an abstraction layer for interacting with different RDBMS in a general way. Currently the cookbook ships with providers for MySQL, PostgreSQL and SQL Server. Please see specific usage in the __Example__ sections below. The providers use specific Ruby gems installed under Chef's Ruby environment to execute commands and carry out actions. These gems will need to be installed before the providers can operate correctly. Specific notes for each RDBS flavor: -These resources aim to expose an abstraction layer for interacting -with different RDBMS in a general way. Currently the cookbook ships -with providers for MySQL, PostgreSQL and SQL Server. Please see -specific usage in the __Example__ sections below. The providers use -specific Ruby gems installed under Chef's Ruby environment to execute -commands and carry out actions. These gems will need to be installed -before the providers can operate correctly. Specific notes for each -RDBS flavor: +- MySQL: leverages the `mysql` gem which is installed as part of the `mysql::ruby` recipe. You must declare `include_recipe "database::mysql"` to include this in your recipe. +- PostgreSQL: leverages the `pg` gem which is installed as part of the `postgresql::ruby` recipe. You must declare `include_recipe "database::postgresql"` to include this. +- SQL Server: leverages the `tiny_tds` gem which is installed as part of the `sql_server::client` recipe. -- MySQL: leverages the `mysql` gem which is installed as part of the - `mysql::ruby` recipe. You can use `database::mysql` to include this, - too. -- PostgreSQL: leverages the `pg` gem which is installed as part of the - `postgresql::ruby` recipe. You can use `database::postgresql` to - include this, too. - Currently does not work in Chef "omnibus" full stack installs, see COOK-1406. -- SQL Server: leverages the `tiny_tds` gem which is installed as part - of the `sql_server::client` recipe. +This cookbook is not in charge of installing the Database Management System itself. Therefore, if you want to install MySQL, for instance, you should add `include_recipe "mysql::server"` in your recipe, or include `mysql::server` in the node run_list. -`database` ----------- - -Manage databases in a RDBMS. Use the proper shortcut resource -depending on your RDBMS: `mysql_database`, `postgresql_database` or -`sql_server_database`. - -### Actions +### database +Manage databases in a RDBMS. Use the proper shortcut resource depending on your RDBMS: `mysql_database`, `postgresql_database` or `sql_server_database`. +#### Actions - :create: create a named database - :drop: drop a named database - :query: execute an arbitrary query against a named database -### Attribute Parameters - +#### Attribute Parameters - database_name: name attribute. Name of the database to interact with -- connection: hash of connection info. valid keys include :host, - :port, :username, :password -- sql: string of sql or a block that executes to a string of sql, - which will be executed against the database. used by :query action - only +- connection: hash of connection info. valid keys include :host, :port, :username, :password and :socket (only for MySQL DB*) +- sql: string of sql or a block that executes to a string of sql, which will be executed against the database. used by :query action only -### Providers +\* The database cookbook uses the `mysql` gem, which uses the `real_connect()` function from mysql API to connect to the server. -- **Chef::Provider::Database::Mysql**: shortcut resource `mysql_database` -- **Chef::Provider::Database::Postgresql**: shortcut resource `postgresql_database` -- **Chef::Provider::Database::SqlServer**: shortcut resource `sql_server_database` +> "The value of host may be either a host name or an IP address. If host is NULL or the string "localhost", a connection to the local host is assumed. For Windows, the client connects using a shared-memory connection, if the server has shared-memory connections enabled. Otherwise, TCP/IP is used. For Unix, the client connects using a Unix socket file. For local connections, you can also influence the type of connection to use with the MYSQL_OPT_PROTOCOL or MYSQL_OPT_NAMED_PIPE options to mysql_options(). The type of connection must be supported by the server. For a host value of "." on Windows, the client connects using a named pipe, if the server has named-pipe connections enabled. If named-pipe connections are not enabled, an error occurs." -### Examples +If you set the `:host` key to "localhost" or if you leave it blank, a socket will be used. By default `real_connect()` function will look for socket in `/var/lib/mysql/mysql.sock`. If your socket file in non-default location - you can use :socket key to specify that location. - # create a mysql database - mysql_database 'oracle_rules' do - connection ({:host => "localhost", :username => 'root', :password => node['mysql']['server_root_password']}) - action :create - end +#### Providers +- `Chef::Provider::Database::Mysql`: shortcut resource `mysql_database` +- `Chef::Provider::Database::Postgresql`: shortcut resource `postgresql_database` +- `Chef::Provider::Database::SqlServer`: shortcut resource `sql_server_database` - # create a sql server database - sql_server_database 'mr_softie' do - connection ({:host => "127.0.0.1", :port => node['sql_server']['port'], :username => 'sa', :password => node['sql_server']['server_sa_password']}) - action :create - end +#### Examples +```ruby +# Create a mysql database +mysql_database 'oracle_rules' do + connection( + :host => 'localhost', + :username => 'root', + :password => node['mysql']['server_root_password'] + ) + action :create +end +``` - # create a postgresql database - postgresql_database 'mr_softie' do - connection ({:host => "127.0.0.1", :port => 5432, :username => 'postgres', :password => node['postgresql']['password']['postgres']}) - action :create - end +```ruby +# Create a sql server database +sql_server_database 'mr_softie' do + connection( + :host => '127.0.0.1', + :port => node['sql_server']['port'], + :username => 'sa', + :password => node['sql_server']['server_sa_password'] + ) + action :create +end +``` - # create a postgresql database with additional parameters - postgresql_database 'mr_softie' do - connection ({:host => "127.0.0.1", :port => 5432, :username => 'postgres', :password => node['postgresql']['password']['postgres']}) - template 'DEFAULT' - encoding 'DEFAULT' - tablespace 'DEFAULT' - connection_limit '-1' - owner 'postgres' - action :create - end +```ruby +# create a postgresql database +postgresql_database 'mr_softie' do + connection( + :host => '127.0.0.1' + :port => 5432, + :username => 'postgres', + :password => node['postgresql']['password']['postgres'] + ) + action :create +end +``` - # externalize conection info in a ruby hash - mysql_connection_info = {:host => "localhost", - :username => 'root', - :password => node['mysql']['server_root_password']} - sql_server_connection_info = {:host => "localhost", - :port => node['sql_server']['port'], - :username => 'sa', - :password => node['sql_server']['server_sa_password']} - postgresql_connection_info = {:host => "127.0.0.1", - :port => node['postgresql']['config']['port'], - :username => 'postgres', - :password => node['postgresql']['password']['postgres']} +```ruby +# create a postgresql database with additional parameters +postgresql_database 'mr_softie' do + connection( + :host => '127.0.0.1', + :port => 5432, + :username => 'postgres', + :password => node['postgresql']['password']['postgres'] + ) + template 'DEFAULT' + encoding 'DEFAULT' + tablespace 'DEFAULT' + connection_limit '-1' + owner 'postgres' + action :create +end +``` - # same create commands, connection info as an external hash - mysql_database 'foo' do - connection mysql_connection_info - action :create - end - sql_server_database 'foo' do - connection sql_server_connection_info - action :create - end - postgresql_database 'foo' do - connection postgresql_connection_info - action :create - end +```ruby +# Externalize conection info in a ruby hash +mysql_connection_info = { + :host => 'localhost', + :username => 'root', + :password => node['mysql']['server_root_password'] +} - # create database, set provider in resource parameter - database 'bar' do - connection mysql_connection_info - provider Chef::Provider::Database::Mysql - action :create - end - database 'bar' do - connection sql_server_connection_info - provider Chef::Provider::Database::SqlServer - action :create - end - database 'bar' do - connection postgresql_connection_info - provider Chef::Provider::Database::Postgresql - action :create - end +sql_server_connection_info = { + :host => 'localhost', + :port => node['sql_server']['port'], + :username => 'sa', + :password => node['sql_server']['server_sa_password'] +} - # drop a database - mysql_database "baz" do - connection mysql_connection_info - action :drop - end +postgresql_connection_info = { + :host => '127.0.0.1', + :port => node['postgresql']['config']['port'], + :username => 'postgres', + :password => node['postgresql']['password']['postgres'] +} - # query a database - mysql_database "flush the privileges" do - connection mysql_connection_info - sql "flush privileges" - action :query - end - # query a database from a sql script on disk - mysql_database "run script" do - connection mysql_connection_info - sql { ::File.open("/path/to/sql_script.sql").read } - action :query - end - # vacuum a postgres database - postgres_database "vacuum databases" do - connection postgresql_connection_info - database_table "template1" - sql "VACUUM FULL VERBOSE ANALYZE" - action :query - end +# Same create commands, connection info as an external hash +mysql_database 'foo' do + connection mysql_connection_info + action :create +end -`database_user` ---------------- +sql_server_database 'foo' do + connection sql_server_connection_info + action :create +end -Manage users and user privileges in a RDBMS. Use the proper shortcut -resource depending on your RDBMS: `mysql_database_user`, -`postgresql_database_user`, or `sql_server_database_user`. +postgresql_database 'foo' do + connection postgresql_connection_info + action :create +end -### Actions + +# Create database, set provider in resource parameter +database 'bar' do + connection mysql_connection_info + provider Chef::Provider::Database::Mysql + action :create +end + +database 'bar' do + connection sql_server_connection_info + provider Chef::Provider::Database::SqlServer + action :create +end + +database 'bar' do + connection postgresql_connection_info + provider Chef::Provider::Database::Postgresql + action :create +end + + + +# Drop a database +mysql_database 'baz' do + connection mysql_connection_info + action :drop +end + + + +# Query a database +mysql_database 'flush the privileges' do + connection mysql_connection_info + sql 'flush privileges' + action :query +end + + + +# Query a database from a sql script on disk +mysql_database 'run script' do + connection mysql_connection_info + sql { ::File.open('/path/to/sql_script.sql').read } + action :query +end + + + +# Vacuum a postgres database +postgresql_database 'vacuum databases' do + connection postgresql_connection_info + database_table 'template1' + sql 'VACUUM FULL VERBOSE ANALYZE' + action :query +end +``` + +### database_user +Manage users and user privileges in a RDBMS. Use the proper shortcut resource depending on your RDBMS: `mysql_database_user`, `postgresql_database_user`, or `sql_server_database_user`. + +#### Actions - :create: create a user - :drop: drop a user - :grant: manipulate user privileges on database objects -### Attribute Parameters - +#### Attribute Parameters - username: name attribute. Name of the database user - password: password for the user account - database_name: Name of the database to interact with -- connection: hash of connection info. valid keys include :host, - :port, :username, :password -- privileges: array of database privileges to grant user. used by the - :grant action. default is :all -- host: host where user connections are allowed from. used by MySQL - provider only. default is 'localhost' -- table: table to grant privileges on. used by :grant action and MySQL - provider only. default is '*' (all tables) +- connection: hash of connection info. valid keys include :host, :port, :username, :password +- privileges: array of database privileges to grant user. used by the :grant action. default is :all +- grant_option: appends 'WITH GRANT OPTION' to grant statement. used by MySQL provider only. default is 'false' +- host: host where user connections are allowed from. used by MySQL provider only. default is 'localhost' +- table: table to grant privileges on. used by :grant action and MySQL provider only. default is '*' (all tables) -### Providers +#### Providers +- `Chef::Provider::Database::MysqlUser`: shortcut resource `mysql_database_user` +- `Chef::Provider::Database::PostgresqlUser`: shortcut resource `postgresql_database_user` +- `Chef::Provider::Database::SqlServerUser`: shortcut resource`sql_server_database_user` -- **Chef::Provider::Database::MysqlUser**: shortcut resource - `mysql_database_user` -- **Chef::Provider::Database::PostgresqlUser**: shortcut - resource `postgresql_database_user` -- **Chef::Provider::Database::SqlServerUser**: shortcut resource - `sql_server_database_user` +#### Examples -### Examples +```ruby +# create connection info as an external ruby hash +mysql_connection_info = { + :host => 'localhost', + :username => 'root', + :password => node['mysql']['server_root_password'] +} - # create connection info as an external ruby hash - mysql_connection_info = {:host => "localhost", - :username => 'root', - :password => node['mysql']['server_root_password']} - postgresql_connection_info = {:host => "localhost", - :port => node['postgresql']['config']['port'], - :username => 'postgres', - :password => node['postgresql']['password']['postgres']} - sql_server_connection_info = {:host => "localhost", - :port => node['sql_server']['port'], - :username => 'sa', - :password => node['sql_server']['server_sa_password']} +postgresql_connection_info = { + :host => 'localhost', + :port => node['postgresql']['config']['port'], + :username => 'postgres', + :password => node['postgresql']['password']['postgres'] +} - # create a mysql user but grant no privileges - mysql_database_user 'disenfranchised' do - connection mysql_connection_info - password 'super_secret' - action :create - end +sql_server_connection_info = { + :host => 'localhost', + :port => node['sql_server']['port'], + :username => 'sa', + :password => node['sql_server']['server_sa_password'] +} - # do the same but pass the provider to the database resource - database_user 'disenfranchised' do - connection mysql_connection_info - password 'super_secret' - provider Chef::Provider::Database::MysqlUser - action :create - end - # create a postgresql user but grant no privileges - postgresql_database_user 'disenfranchised' do - connection postgresql_connection_info - password 'super_secret' - action :create - end - # do the same but pass the provider to the database resource - database_user 'disenfranchised' do - connection postgresql_connection_info - password 'super_secret' - provider Chef::Provider::Database::PostgresqlUser - action :create - end +# Create a mysql user but grant no privileges +mysql_database_user 'disenfranchised' do + connection mysql_connection_info + password 'super_secret' + action :create +end - # create a sql server user but grant no privileges - sql_server_database_user 'disenfranchised' do - connection sql_server_connection_info - password 'super_secret' - action :create - end - # drop a mysql user - mysql_database_user "foo_user" do - connection mysql_connection_info - action :drop - end - # bulk drop sql server users - %w{ disenfranchised foo_user }.each do |user| - sql_server_database_user user do - connection sql_server_connection_info - action :drop - end - end +# Do the same but pass the provider to the database resource +database_user 'disenfranchised' do + connection mysql_connection_info + password 'super_secret' + provider Chef::Provider::Database::MysqlUser + action :create +end - # grant select,update,insert privileges to all tables in foo db from all hosts - mysql_database_user 'foo_user' do - connection mysql_connection_info - password 'super_secret' - database_name 'foo' - host '%' - privileges [:select,:update,:insert] - action :grant - end - # grant all privileges on all databases/tables from localhost - mysql_database_user 'super_user' do - connection mysql_connection_info - password 'super_secret' - action :grant - end - # grant all privileges on all tables in foo db - postgresql_database_user 'foo_user' do - connection postgresql_connection_info - database_name 'foo' - privileges [:all] - action :grant - end +# Create a postgresql user but grant no privileges +postgresql_database_user 'disenfranchised' do + connection postgresql_connection_info + password 'super_secret' + action :create +end + + + +# Do the same but pass the provider to the database resource +database_user 'disenfranchised' do + connection postgresql_connection_info + password 'super_secret' + provider Chef::Provider::Database::PostgresqlUser + action :create +end + + + +# Create a sql server user but grant no privileges +sql_server_database_user 'disenfranchised' do + connection sql_server_connection_info + password 'super_secret' + action :create +end + + + +# Drop a mysql user +mysql_database_user 'foo_user' do + connection mysql_connection_info + action :drop +end + + + +# Bulk drop sql server users +%w(disenfranchised foo_user).each do |user| + sql_server_database_user user do + connection sql_server_connection_info + action :drop + end +end + + + +# Grant SELECT, UPDATE, and INSERT privileges to all tables in foo db from all hosts +mysql_database_user 'foo_user' do + connection mysql_connection_info + password 'super_secret' + database_name 'foo' + host '%' + privileges [:select,:update,:insert] + action :grant +end + + + +# Grant all privileges on all databases/tables from localhost +mysql_database_user 'super_user' do + connection mysql_connection_info + password 'super_secret' + action :grant +end + + + +# Grant all privileges on all tables in foo db +postgresql_database_user 'foo_user' do + connection postgresql_connection_info + database_name 'foo' + privileges [:all] + action :grant +end + +# grant select,update,insert privileges to all tables in foo db +sql_server_database_user 'foo_user' do + connection sql_server_connection_info + password 'super_secret' + database_name 'foo' + privileges [:select,:update,:insert] + action :grant +end +``` - # grant select,update,insert privileges to all tables in foo db - sql_server_database_user 'foo_user' do - connection sql_server_connection_info - password 'super_secret' - database_name 'foo' - privileges [:select,:update,:insert] - action :grant - end Recipes -======= +------- +### ebs_volume +*Note*: This recipe does not currently work on RHEL platforms due to the xfs cookbook not supporting RHEL yet. -ebs\_volume ------------ - -*Note*: This recipe does not currently work on RHEL platforms due to - the xfs cookbook not supporting RHEL yet. - -Loads the aws information from the data bag. Searches the applications -data bag for the database master or slave role and checks that role is -applied to the node. Loads the EBS information and the master -information from data bags. Uses the aws cookbook LWRP, -`aws_ebs_volume` to manage the volume. +Loads the aws information from the data bag. Searches the applications data bag for the database master or slave role and checks that role is applied to the node. Loads the EBS information and the master information from data bags. Uses the aws cookbook LWRP, `aws_ebs_volume` to manage the volume. On a master node: -* if we have an ebs volume already as stored in a data bag, attach it. -* if we don't have the ebs information then create a new one and - attach it. -* store the volume information in a data bag via a ruby block. +- if we have an ebs volume already as stored in a data bag, attach it +- if we don't have the ebs information then create a new one and attach it +- store the volume information in a data bag via a ruby block On a slave node: -* use the master volume information to generate a snapshot. -* create the new volume from the snapshot and attach it. +- use the master volume information to generate a snapshot +- create the new volume from the snapshot and attach it -Also on a master node, generate some configuration for running a -snapshot via `chef-solo` from cron. +Also on a master node, generate some configuration for running a snapshot via `chef-solo` from cron. -On a new filesystem volume, create as XFS, then mount it in /mnt, and -also bind-mount it to the mysql data directory (default -/var/lib/mysql). +On a new filesystem volume, create as XFS, then mount it in `/mnt`, and also bind-mount it to the mysql data directory (default `/var/lib/mysql`). -master ------- +### master +This recipe no longer loads AWS specific information, and the database position for replication is no longer stored in a databag because the client might not have permission to write to the databag item. This may be handled in a different way at a future date. -This recipe no longer loads AWS specific information, and the database -position for replication is no longer stored in a databag because the -client might not have permission to write to the databag item. This -may be handled in a different way at a future date. +Searches the apps databag for applications, and for each one it will check that the specified database master role is set in both the databag and applied to the node's run list. Then, retrieves the passwords for `root`, `repl` and `debian` users and saves them to the node attributes. If the passwords are not found in the databag, it prints a message that they'll be generated by the mysql cookbook. -Searches the apps databag for applications, and for each one it will -check that the specified database master role is set in both the -databag and applied to the node's run list. Then, retrieves the -passwords for `root`, `repl` and `debian` users and saves them to the -node attributes. If the passwords are not found in the databag, it -prints a message that they'll be generated by the mysql cookbook. +Then it adds the application databag database settings to a hash, to use later. -Then it adds the application databag database settings to a hash, to -use later. +Then it will iterate over the databases and create them with the `mysql_database` resource while adding privileges for application specific database users using the `mysql_database_user` resource. -Then it will iterate over the databases and create them with the -`mysql_database` resource while adding privileges for application -specific database users using the `mysql_database_user` resource. +### slave +_TODO_: Retrieve the master status from a data bag, then start replication using a ruby block. The replication status needs to be handled in some other way for now since the master recipe above doesn't actually set it in the databag anymore. -slave ------ +### snapshot +Run via Chef Solo. Retrieves the db snapshot configuration from the specified JSON file. Uses the `mysql_database` resource to lock and unlock tables, and does a filesystem freeze and EBS snapshot. -_TODO_: Retrieve the master status from a data bag, then start -replication using a ruby block. The replication status needs to be -handled in some other way for now since the master recipe above -doesn't actually set it in the databag anymore. - -snapshot --------- - -Run via Chef Solo. Retrieves the db snapshot configuration from the -specified JSON file. Uses the `mysql_database` resource to lock and -unlock tables, and does a filesystem freeze and EBS snapshot. Deprecated Recipes -================== +------------------ +The following recipe is considered deprecated. It is kept for reference purposes. -The following recipe is considered deprecated. It is kept for -reference purposes. +### ebs_backup +Older style of doing mysql snapshot and replication using Adam Jacob's [ec2_mysql](http://github.com/adamhjk/ec2_mysql) script and library. -ebs\_backup ------------ - -Older style of doing mysql snapshot and replication using Adam Jacob's -[ec2_mysql](http://github.com/adamhjk/ec2_mysql) script and library. Data Bags -========= +--------- +This cookbook uses the apps data bag item for the specified application; see the `application` cookbook's README.md. It also creates data bag items in a bag named 'aws' for storing volume information. In order to interact with EC2, it expects aws to have a main item: -This cookbook uses the apps data bag item for the specified -application; see the `application` cookbook's README.md. It also -creates data bag items in a bag named 'aws' for storing volume -information. In order to interact with EC2, it expects aws to have a -main item: +```javascript +{ + "id": "main", + "ec2_private_key": "private key as a string", + "ec2_cert": "certificate as a string", + "aws_account_id": "", + "aws_secret_access_key": "", + "aws_access_key_id": "" +} +``` - { - "id": "main", - "ec2_private_key": "private key as a string", - "ec2_cert": "certificate as a string", - "aws_account_id": "", - "aws_secret_access_key": "", - "aws_access_key_id": "" - } - -Note: with the Open Source Chef Server, the server using the database -recipes must be an admin client or it will not be able to create data -bag items. You can modify whether the client is admin by editing it -with knife. +Note: with the Open Source Chef Server, the server using the database recipes must be an admin client or it will not be able to create data bag items. You can modify whether the client is admin by editing it with knife. knife client edit { @@ -435,67 +433,54 @@ with knife. ... } -This is not required if the Chef Server is Opscode Hosted Chef, -instead use the ACL feature to modify access for the node to be able -to update the data bag. +This is not required if the Chef Server is Opscode Hosted Chef, instead use the ACL feature to modify access for the node to be able to update the data bag. + Usage -===== +----- +Aside from the application data bag (see the README in the application cookbook), create a role for the database master. Use a `role.rb` in your chef-repo, or create the role directly with knife. -Aside from the application data bag (see the README in the application -cookbook), create a role for the database master. Use a role.rb in -your chef-repo, or create the role directly with knife. +```javascript +{ + "name": "my_app_database_master", + "chef_type": "role", + "json_class": "Chef::Role", + "default_attributes": {}, + "description": "", + "run_list": [ + "recipe[mysql::server]", + "recipe[database::master]" + ], + "override_attributes": {} +} +``` - % knife role show my_app_database_master -Fj - { - "name": "my_app_database_master", - "chef_type": "role", - "json_class": "Chef::Role", - "default_attributes": { - }, - "description": "", - "run_list": [ - "recipe[mysql::server]", - "recipe[database::master]" - ], - "override_attributes": { - } - } +Create a `production` environment. This is also used in the `application` cookbook. -Create a `production` environment. This is also used in the -`application` cookbook. +```javascript +{ + "name": "production", + "description": "", + "cookbook_versions": {}, + "json_class": "Chef::Environment", + "chef_type": "environment", + "default_attributes": {}, + "override_attributes": {} +} +``` - % knife environment show production -Fj - { - "name": "production", - "description": "", - "cookbook_versions": { - }, - "json_class": "Chef::Environment", - "chef_type": "environment", - "default_attributes": { - }, - "override_attributes": { - } - } - - -The cookbook `my_app_database` is recommended to set up any -application specific database resources such as configuration -templates, trending monitors, etc. It is not required, but you would -need to create it separately in `site-cookbooks`. Add it to the -`my_app_database_master` role. - -License and Author -================== +The cookbook `my_app_database` is recommended to set up any application specific database resources such as configuration templates, trending monitors, etc. It is not required, but you would need to create it separately in `site-cookbooks`. Add it to the `my_app_database_master` role. +License & Authors +----------------- - Author:: Adam Jacob () - Author:: Joshua Timberman () - Author:: AJ Christensen () - Author:: Seth Chisamore () - Author:: Lamont Granquist () -Copyright 2009-2012, Opscode, Inc. +```text +Copyright 2009-2013, Opscode, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -508,3 +493,4 @@ 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. +``` diff --git a/chef/cookbooks/database/libraries/provider_database_mysql.rb b/chef/cookbooks/database/libraries/provider_database_mysql.rb index b8954a9..ef0c482 100644 --- a/chef/cookbooks/database/libraries/provider_database_mysql.rb +++ b/chef/cookbooks/database/libraries/provider_database_mysql.rb @@ -85,7 +85,8 @@ class Chef @new_resource.connection[:username], @new_resource.connection[:password], nil, - @new_resource.connection[:port] || 3306 + @new_resource.connection[:port] || 3306, + @new_resource.connection[:socket] || nil ) connection.set_server_option ::Mysql::OPTION_MULTI_STATEMENTS_ON connection diff --git a/chef/cookbooks/database/libraries/provider_database_mysql_user.rb b/chef/cookbooks/database/libraries/provider_database_mysql_user.rb index 56c47c4..e09c9d7 100644 --- a/chef/cookbooks/database/libraries/provider_database_mysql_user.rb +++ b/chef/cookbooks/database/libraries/provider_database_mysql_user.rb @@ -35,7 +35,9 @@ class Chef def action_create unless exists? begin - db.query("CREATE USER `#{@new_resource.username}`@`#{@new_resource.host}` IDENTIFIED BY '#{@new_resource.password}'") + statement = "CREATE USER `#{@new_resource.username}`@`#{@new_resource.host}`" + statement += " IDENTIFIED BY '#{@new_resource.password}'" if @new_resource.password + db.query(statement) @new_resource.updated_by_last_action(true) ensure close @@ -48,8 +50,6 @@ class Chef begin db.query("DROP USER `#{@new_resource.username}`@`#{@new_resource.host}`") @new_resource.updated_by_last_action(true) - rescue - Chef::Log.warn("The action_drop drop user failed: drop user '#{@new_resource.username}'@'#{@new_resource.host}'") ensure close end @@ -67,8 +67,9 @@ class Chef filtered = '[FILTERED]' end grant_statement = "GRANT #{@new_resource.privileges.join(', ')} ON #{@new_resource.database_name && @new_resource.database_name != '*' ? "`#{@new_resource.database_name}`" : '*'}.#{@new_resource.table && @new_resource.table != '*' ? "`#{@new_resource.table}`" : '*'} TO `#{@new_resource.username}`@`#{@new_resource.host}` IDENTIFIED BY " + with_grant_option = @new_resource.grant_option == true ? ' WITH GRANT OPTION ' : '' Chef::Log.info("#{@new_resource}: granting access with statement [#{grant_statement}#{filtered}]") - db.query(grant_statement + password) + db.query(grant_statement + password + with_grant_option) @new_resource.updated_by_last_action(true) ensure close diff --git a/chef/cookbooks/database/libraries/provider_database_postgresql_schema.rb b/chef/cookbooks/database/libraries/provider_database_postgresql_schema.rb new file mode 100644 index 0000000..acb84e3 --- /dev/null +++ b/chef/cookbooks/database/libraries/provider_database_postgresql_schema.rb @@ -0,0 +1,73 @@ +# +# Author:: Marco Betti () +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# 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. +# + +require File.join(File.dirname(__FILE__), 'provider_database_postgresql') + +class Chef + class Provider + class Database + class PostgresqlSchema < Chef::Provider::Database::Postgresql + include Chef::Mixin::ShellOut + + def load_current_resource + Gem.clear_paths + require 'pg' + @current_resource = Chef::Resource::PostgresqlDatabaseSchema.new(@new_resource.name) + @current_resource.schema_name(@new_resource.schema_name) + @current_resource + end + + def action_create + unless exists? + begin + if new_resource.owner + db(@new_resource.database_name).query("CREATE SCHEMA \"#{@new_resource.schema_name}\" AUTHORIZATION \"#{@new_resource.owner}\"") + else + db(@new_resource.database_name).query("CREATE SCHEMA \"#{@new_resource.schema_name}\"") + end + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_drop + if exists? + begin + db(@new_resource.database_name).query("DROP SCHEMA \"#{@new_resource.schema_name}\"") + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + private + def exists? + begin + exists = db(@new_resource.database_name).query("SELECT schema_name FROM information_schema.schemata WHERE schema_name='#{@new_resource.schema_name}'").num_tuples != 0 + ensure + close + end + exists + end + end + end + end +end diff --git a/chef/cookbooks/database/libraries/provider_database_postgresql_user.rb b/chef/cookbooks/database/libraries/provider_database_postgresql_user.rb index 5959086..96657d0 100644 --- a/chef/cookbooks/database/libraries/provider_database_postgresql_user.rb +++ b/chef/cookbooks/database/libraries/provider_database_postgresql_user.rb @@ -1,6 +1,7 @@ # # Author:: Seth Chisamore () # Author:: Lamont Granquist () +# Author:: Marco Betti () # Copyright:: Copyright (c) 2011 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -36,7 +37,9 @@ class Chef def action_create unless exists? begin - db("template1").query("CREATE USER \"#{@new_resource.username}\" WITH PASSWORD '#{@new_resource.password}'") + statement = "CREATE USER \"#{@new_resource.username}\"" + statement += " WITH PASSWORD '#{@new_resource.password}'" if @new_resource.password + db("template1").query(statement) @new_resource.updated_by_last_action(true) ensure close @@ -67,6 +70,17 @@ class Chef end end + def action_grant_schema + begin + grant_statement = "GRANT #{@new_resource.privileges.join(', ')} ON SCHEMA \"#{@new_resource.schema_name}\" TO \"#{@new_resource.username}\"" + Chef::Log.info("#{@new_resource}: granting access with statement [#{grant_statement}]") + db(@new_resource.database_name).query(grant_statement) + @new_resource.updated_by_last_action(true) + ensure + close + end + end + private def exists? begin diff --git a/chef/cookbooks/database/libraries/provider_database_sql_server_user.rb b/chef/cookbooks/database/libraries/provider_database_sql_server_user.rb index 596892e..158abe1 100644 --- a/chef/cookbooks/database/libraries/provider_database_sql_server_user.rb +++ b/chef/cookbooks/database/libraries/provider_database_sql_server_user.rb @@ -84,6 +84,24 @@ class Chef end end + def action_alter_roles + begin + if @new_resource.password + action_create + end + Chef::Application.fatal!('Please provide a database_name, SQL Server does not support global GRANT statements.') unless @new_resource.database_name + db.execute("USE [#{@new_resource.database_name}]").do + @new_resource.sql_roles.each do | sql_role, role_action | + alter_statement = "ALTER ROLE [#{sql_role}] #{role_action} MEMBER [#{@new_resource.username}]" + Chef::Log.info("#{@new_resource} granting access with statement [#{alter_statement}]") + db.execute(alter_statement).do + end + @new_resource.updated_by_last_action(true) + ensure + close + end + end + private def exists?(type=:users) case type diff --git a/chef/cookbooks/database/libraries/resource_database.rb b/chef/cookbooks/database/libraries/resource_database.rb index 103a999..3824c73 100644 --- a/chef/cookbooks/database/libraries/resource_database.rb +++ b/chef/cookbooks/database/libraries/resource_database.rb @@ -6,9 +6,9 @@ # 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. diff --git a/chef/cookbooks/database/libraries/resource_database_user.rb b/chef/cookbooks/database/libraries/resource_database_user.rb index 043721a..f074ab8 100644 --- a/chef/cookbooks/database/libraries/resource_database_user.rb +++ b/chef/cookbooks/database/libraries/resource_database_user.rb @@ -6,9 +6,9 @@ # 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. @@ -31,6 +31,7 @@ class Chef @table = nil @host = 'localhost' @privileges = [:all] + @grant_option = false @allowed_actions.push(:create, :drop, :grant) @action = :create @@ -56,8 +57,7 @@ class Chef set_or_return( :password, arg, - :kind_of => String, - :required => true + :kind_of => String ) end @@ -85,6 +85,14 @@ class Chef ) end + def grant_option(arg=nil) + set_or_return( + :grant_option, + arg, + :kind_of => [ TrueClass, FalseClass ], :default => false + ) + end + end end end diff --git a/chef/cookbooks/database/libraries/resource_mysql_database.rb b/chef/cookbooks/database/libraries/resource_mysql_database.rb index 726619e..e904220 100644 --- a/chef/cookbooks/database/libraries/resource_mysql_database.rb +++ b/chef/cookbooks/database/libraries/resource_mysql_database.rb @@ -6,9 +6,9 @@ # 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. @@ -31,4 +31,4 @@ class Chef end end -end \ No newline at end of file +end diff --git a/chef/cookbooks/database/libraries/resource_mysql_database_user.rb b/chef/cookbooks/database/libraries/resource_mysql_database_user.rb index 6e11ebe..f653e6b 100644 --- a/chef/cookbooks/database/libraries/resource_mysql_database_user.rb +++ b/chef/cookbooks/database/libraries/resource_mysql_database_user.rb @@ -6,9 +6,9 @@ # 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. @@ -31,4 +31,4 @@ class Chef end end -end \ No newline at end of file +end diff --git a/chef/cookbooks/database/libraries/resource_postgresql_database.rb b/chef/cookbooks/database/libraries/resource_postgresql_database.rb index d8afb8c..548d599 100644 --- a/chef/cookbooks/database/libraries/resource_postgresql_database.rb +++ b/chef/cookbooks/database/libraries/resource_postgresql_database.rb @@ -7,9 +7,9 @@ # 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. diff --git a/chef/cookbooks/database/libraries/resource_postgresql_database_schema.rb b/chef/cookbooks/database/libraries/resource_postgresql_database_schema.rb new file mode 100644 index 0000000..b388193 --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_postgresql_database_schema.rb @@ -0,0 +1,44 @@ +# +# Author:: Marco Betti () +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# 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. +# + +require File.join(File.dirname(__FILE__), 'resource_database') +require File.join(File.dirname(__FILE__), 'provider_database_postgresql_schema') + +class Chef + class Resource + class PostgresqlDatabaseSchema < Chef::Resource::Database + + def initialize(name, run_context=nil) + super + @resource_name = :postgresql_database_schema + @schema_name = name + @allowed_actions.push(:create, :drop) + @action = :create + @provider = Chef::Provider::Database::PostgresqlSchema + end + + def schema_name(arg=nil) + set_or_return( + :schema_name, + arg, + :kind_of => String + ) + end + end + end +end diff --git a/chef/cookbooks/database/libraries/resource_postgresql_database_user.rb b/chef/cookbooks/database/libraries/resource_postgresql_database_user.rb index b3bf9d2..cfaf503 100644 --- a/chef/cookbooks/database/libraries/resource_postgresql_database_user.rb +++ b/chef/cookbooks/database/libraries/resource_postgresql_database_user.rb @@ -1,15 +1,16 @@ # # Author:: Seth Chisamore () # Author:: Lamont Granquist () +# Author:: Marco Betti () # Copyright:: Copyright (c) 2011 Opscode, Inc. # License:: Apache License, Version 2.0 # # 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. @@ -28,6 +29,16 @@ class Chef super @resource_name = :postgresql_database_user @provider = Chef::Provider::Database::PostgresqlUser + @schema_name = nil + @allowed_actions.push(:create, :drop, :grant, :grant_schema) + end + + def schema_name(arg=nil) + set_or_return( + :schema_name, + arg, + :kind_of => String + ) end end diff --git a/chef/cookbooks/database/libraries/resource_sql_server_database.rb b/chef/cookbooks/database/libraries/resource_sql_server_database.rb index cdb17cb..bdd3248 100644 --- a/chef/cookbooks/database/libraries/resource_sql_server_database.rb +++ b/chef/cookbooks/database/libraries/resource_sql_server_database.rb @@ -6,9 +6,9 @@ # 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. @@ -31,4 +31,4 @@ class Chef end end -end \ No newline at end of file +end diff --git a/chef/cookbooks/database/libraries/resource_sql_server_database_user.rb b/chef/cookbooks/database/libraries/resource_sql_server_database_user.rb index 56a3e03..378e27b 100644 --- a/chef/cookbooks/database/libraries/resource_sql_server_database_user.rb +++ b/chef/cookbooks/database/libraries/resource_sql_server_database_user.rb @@ -6,9 +6,9 @@ # 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. @@ -25,10 +25,20 @@ class Chef def initialize(name, run_context=nil) super + @sql_roles = {} @resource_name = :sql_server_database_user @provider = Chef::Provider::Database::SqlServerUser + @allowed_actions.push(:alter_roles) end + end + def sql_roles(arg=nil) + Chef::Log.debug("Received roles: #{arg.inspect}") + set_or_return( + :sql_roles, + arg, + :kind_of => Hash + ) end end -end \ No newline at end of file +end diff --git a/chef/cookbooks/database/metadata.json b/chef/cookbooks/database/metadata.json new file mode 100644 index 0000000..41a4179 --- /dev/null +++ b/chef/cookbooks/database/metadata.json @@ -0,0 +1,46 @@ +{ + "name": "database", + "version": "2.0.0", + "description": "Sets up the database master or slave", + "long_description": "Database Cookbook\n=================\nThe main highlight of this cookbook is the `database` and `database_user` resources for managing databases and database users in a RDBMS. Providers for MySQL, PostgreSQL and SQL Server are also provided, see usage documentation below.\n\nThis cookbook also contains recipes to configure mysql database masters and slaves and uses EBS for storage, integrating together with the application cookbook utilizing data bags for application related information. These recipes are written primarily to use MySQL and the Opscode mysql cookbook. Other RDBMS may be supported at a later date. This cookbook does not automatically restore database dumps, but does install tools to help with that.\n\n\nRequirements\n------------\nChef version 0.10.10+.\n\n### Platforms\n* Debian, Ubuntu\n* Red Hat, CentOS, Scientific, Fedora, Amazon\n\n### Cookbooks\nThe following Opscode cookbooks are dependencies:\n\n* mysql\n* postgresql\n* xfs\n* aws\n\n\nResources/Providers\n-------------------\nThese resources aim to expose an abstraction layer for interacting with different RDBMS in a general way. Currently the cookbook ships with providers for MySQL, PostgreSQL and SQL Server. Please see specific usage in the __Example__ sections below. The providers use specific Ruby gems installed under Chef's Ruby environment to execute commands and carry out actions. These gems will need to be installed before the providers can operate correctly. Specific notes for each RDBS flavor:\n\n- MySQL: leverages the `mysql` gem which is installed as part of the `mysql::ruby` recipe. You must declare `include_recipe \"database::mysql\"` to include this in your recipe.\n- PostgreSQL: leverages the `pg` gem which is installed as part of the `postgresql::ruby` recipe. You must declare `include_recipe \"database::postgresql\"` to include this. \n- SQL Server: leverages the `tiny_tds` gem which is installed as part of the `sql_server::client` recipe.\n\nThis cookbook is not in charge of installing the Database Management System itself. Therefore, if you want to install MySQL, for instance, you should add `include_recipe \"mysql::server\"` in your recipe, or include `mysql::server` in the node run_list.\n\n### database\nManage databases in a RDBMS. Use the proper shortcut resource depending on your RDBMS: `mysql_database`, `postgresql_database` or `sql_server_database`.\n\n#### Actions\n- :create: create a named database\n- :drop: drop a named database\n- :query: execute an arbitrary query against a named database\n\n#### Attribute Parameters\n- database_name: name attribute. Name of the database to interact with\n- connection: hash of connection info. valid keys include :host, :port, :username, :password and :socket (only for MySQL DB*)\n- sql: string of sql or a block that executes to a string of sql, which will be executed against the database. used by :query action only\n\n\\* The database cookbook uses the `mysql` gem, which uses the `real_connect()` function from mysql API to connect to the server.\n\n> \"The value of host may be either a host name or an IP address. If host is NULL or the string \"localhost\", a connection to the local host is assumed. For Windows, the client connects using a shared-memory connection, if the server has shared-memory connections enabled. Otherwise, TCP/IP is used. For Unix, the client connects using a Unix socket file. For local connections, you can also influence the type of connection to use with the MYSQL_OPT_PROTOCOL or MYSQL_OPT_NAMED_PIPE options to mysql_options(). The type of connection must be supported by the server. For a host value of \".\" on Windows, the client connects using a named pipe, if the server has named-pipe connections enabled. If named-pipe connections are not enabled, an error occurs.\"\n\nIf you set the `:host` key to \"localhost\" or if you leave it blank, a socket will be used. By default `real_connect()` function will look for socket in `/var/lib/mysql/mysql.sock`. If your socket file in non-default location - you can use :socket key to specify that location.\n\n#### Providers\n- `Chef::Provider::Database::Mysql`: shortcut resource `mysql_database`\n- `Chef::Provider::Database::Postgresql`: shortcut resource `postgresql_database`\n- `Chef::Provider::Database::SqlServer`: shortcut resource `sql_server_database`\n\n#### Examples\n```ruby\n# Create a mysql database\nmysql_database 'oracle_rules' do\n connection(\n :host => 'localhost',\n :username => 'root',\n :password => node['mysql']['server_root_password']\n )\n action :create\nend\n```\n\n```ruby\n# Create a sql server database\nsql_server_database 'mr_softie' do\n connection(\n :host => '127.0.0.1',\n :port => node['sql_server']['port'],\n :username => 'sa',\n :password => node['sql_server']['server_sa_password']\n )\n action :create\nend\n```\n\n```ruby\n# create a postgresql database\npostgresql_database 'mr_softie' do\n connection(\n :host => '127.0.0.1'\n :port => 5432,\n :username => 'postgres',\n :password => node['postgresql']['password']['postgres']\n )\n action :create\nend\n```\n\n```ruby\n# create a postgresql database with additional parameters\npostgresql_database 'mr_softie' do\n connection(\n :host => '127.0.0.1',\n :port => 5432,\n :username => 'postgres',\n :password => node['postgresql']['password']['postgres']\n )\n template 'DEFAULT'\n encoding 'DEFAULT'\n tablespace 'DEFAULT'\n connection_limit '-1'\n owner 'postgres'\n action :create\nend\n```\n\n```ruby\n# Externalize conection info in a ruby hash\nmysql_connection_info = {\n :host => 'localhost',\n :username => 'root',\n :password => node['mysql']['server_root_password']\n}\n\nsql_server_connection_info = {\n :host => 'localhost',\n :port => node['sql_server']['port'],\n :username => 'sa',\n :password => node['sql_server']['server_sa_password']\n}\n\npostgresql_connection_info = {\n :host => '127.0.0.1',\n :port => node['postgresql']['config']['port'],\n :username => 'postgres',\n :password => node['postgresql']['password']['postgres']\n}\n\n\n\n# Same create commands, connection info as an external hash\nmysql_database 'foo' do\n connection mysql_connection_info\n action :create\nend\n\nsql_server_database 'foo' do\n connection sql_server_connection_info\n action :create\nend\n\npostgresql_database 'foo' do\n connection postgresql_connection_info\n action :create\nend\n\n\n\n# Create database, set provider in resource parameter\ndatabase 'bar' do\n connection mysql_connection_info\n provider Chef::Provider::Database::Mysql\n action :create\nend\n\ndatabase 'bar' do\n connection sql_server_connection_info\n provider Chef::Provider::Database::SqlServer\n action :create\nend\n\ndatabase 'bar' do\n connection postgresql_connection_info\n provider Chef::Provider::Database::Postgresql\n action :create\nend\n\n\n\n# Drop a database\nmysql_database 'baz' do\n connection mysql_connection_info\n action :drop\nend\n\n\n\n# Query a database\nmysql_database 'flush the privileges' do\n connection mysql_connection_info\n sql 'flush privileges'\n action :query\nend\n\n\n\n# Query a database from a sql script on disk\nmysql_database 'run script' do\n connection mysql_connection_info\n sql { ::File.open('/path/to/sql_script.sql').read }\n action :query\nend\n\n\n\n# Vacuum a postgres database\npostgresql_database 'vacuum databases' do\n connection postgresql_connection_info\n database_table 'template1'\n sql 'VACUUM FULL VERBOSE ANALYZE'\n action :query\nend\n```\n\n### database_user\nManage users and user privileges in a RDBMS. Use the proper shortcut resource depending on your RDBMS: `mysql_database_user`, `postgresql_database_user`, or `sql_server_database_user`.\n\n#### Actions\n- :create: create a user\n- :drop: drop a user\n- :grant: manipulate user privileges on database objects\n\n#### Attribute Parameters\n- username: name attribute. Name of the database user\n- password: password for the user account\n- database_name: Name of the database to interact with\n- connection: hash of connection info. valid keys include :host, :port, :username, :password\n- privileges: array of database privileges to grant user. used by the :grant action. default is :all\n- grant_option: appends 'WITH GRANT OPTION' to grant statement. used by MySQL provider only. default is 'false'\n- host: host where user connections are allowed from. used by MySQL provider only. default is 'localhost'\n- table: table to grant privileges on. used by :grant action and MySQL provider only. default is '*' (all tables)\n\n#### Providers\n- `Chef::Provider::Database::MysqlUser`: shortcut resource `mysql_database_user`\n- `Chef::Provider::Database::PostgresqlUser`: shortcut resource `postgresql_database_user`\n- `Chef::Provider::Database::SqlServerUser`: shortcut resource`sql_server_database_user`\n\n#### Examples\n\n```ruby\n# create connection info as an external ruby hash\nmysql_connection_info = {\n :host => 'localhost',\n :username => 'root',\n :password => node['mysql']['server_root_password']\n}\n\npostgresql_connection_info = {\n :host => 'localhost',\n :port => node['postgresql']['config']['port'],\n :username => 'postgres',\n :password => node['postgresql']['password']['postgres']\n}\n\nsql_server_connection_info = {\n :host => 'localhost',\n :port => node['sql_server']['port'],\n :username => 'sa',\n :password => node['sql_server']['server_sa_password']\n}\n\n\n\n# Create a mysql user but grant no privileges\nmysql_database_user 'disenfranchised' do\n connection mysql_connection_info\n password 'super_secret'\n action :create\nend\n\n\n\n# Do the same but pass the provider to the database resource\ndatabase_user 'disenfranchised' do\n connection mysql_connection_info\n password 'super_secret'\n provider Chef::Provider::Database::MysqlUser\n action :create\nend\n\n\n\n# Create a postgresql user but grant no privileges\npostgresql_database_user 'disenfranchised' do\n connection postgresql_connection_info\n password 'super_secret'\n action :create\nend\n\n\n\n# Do the same but pass the provider to the database resource\ndatabase_user 'disenfranchised' do\n connection postgresql_connection_info\n password 'super_secret'\n provider Chef::Provider::Database::PostgresqlUser\n action :create\nend\n\n\n\n# Create a sql server user but grant no privileges\nsql_server_database_user 'disenfranchised' do\n connection sql_server_connection_info\n password 'super_secret'\n action :create\nend\n\n\n\n# Drop a mysql user\nmysql_database_user 'foo_user' do\n connection mysql_connection_info\n action :drop\nend\n\n\n\n# Bulk drop sql server users\n%w(disenfranchised foo_user).each do |user|\n sql_server_database_user user do\n connection sql_server_connection_info\n action :drop\n end\nend\n\n\n\n# Grant SELECT, UPDATE, and INSERT privileges to all tables in foo db from all hosts\nmysql_database_user 'foo_user' do\n connection mysql_connection_info\n password 'super_secret'\n database_name 'foo'\n host '%'\n privileges [:select,:update,:insert]\n action :grant\nend\n\n\n\n# Grant all privileges on all databases/tables from localhost\nmysql_database_user 'super_user' do\n connection mysql_connection_info\n password 'super_secret'\n action :grant\nend\n\n\n\n# Grant all privileges on all tables in foo db\npostgresql_database_user 'foo_user' do\n connection postgresql_connection_info\n database_name 'foo'\n privileges [:all]\n action :grant\nend\n\n# grant select,update,insert privileges to all tables in foo db\nsql_server_database_user 'foo_user' do\n connection sql_server_connection_info\n password 'super_secret'\n database_name 'foo'\n privileges [:select,:update,:insert]\n action :grant\nend\n```\n\n\nRecipes\n-------\n### ebs_volume\n*Note*: This recipe does not currently work on RHEL platforms due to the xfs cookbook not supporting RHEL yet.\n\nLoads the aws information from the data bag. Searches the applications data bag for the database master or slave role and checks that role is applied to the node. Loads the EBS information and the master information from data bags. Uses the aws cookbook LWRP, `aws_ebs_volume` to manage the volume.\n\nOn a master node:\n- if we have an ebs volume already as stored in a data bag, attach it\n- if we don't have the ebs information then create a new one and attach it\n- store the volume information in a data bag via a ruby block\n\nOn a slave node:\n- use the master volume information to generate a snapshot\n- create the new volume from the snapshot and attach it\n\nAlso on a master node, generate some configuration for running a snapshot via `chef-solo` from cron.\n\nOn a new filesystem volume, create as XFS, then mount it in `/mnt`, and also bind-mount it to the mysql data directory (default `/var/lib/mysql`).\n\n### master\nThis recipe no longer loads AWS specific information, and the database position for replication is no longer stored in a databag because the client might not have permission to write to the databag item. This may be handled in a different way at a future date.\n\nSearches the apps databag for applications, and for each one it will check that the specified database master role is set in both the databag and applied to the node's run list. Then, retrieves the passwords for `root`, `repl` and `debian` users and saves them to the node attributes. If the passwords are not found in the databag, it prints a message that they'll be generated by the mysql cookbook.\n\nThen it adds the application databag database settings to a hash, to use later.\n\nThen it will iterate over the databases and create them with the `mysql_database` resource while adding privileges for application specific database users using the `mysql_database_user` resource.\n\n### slave\n_TODO_: Retrieve the master status from a data bag, then start replication using a ruby block. The replication status needs to be handled in some other way for now since the master recipe above doesn't actually set it in the databag anymore.\n\n### snapshot\nRun via Chef Solo. Retrieves the db snapshot configuration from the specified JSON file. Uses the `mysql_database` resource to lock and unlock tables, and does a filesystem freeze and EBS snapshot.\n\n\nDeprecated Recipes\n------------------\nThe following recipe is considered deprecated. It is kept for reference purposes.\n\n### ebs_backup\nOlder style of doing mysql snapshot and replication using Adam Jacob's [ec2_mysql](http://github.com/adamhjk/ec2_mysql) script and library.\n\n\nData Bags\n---------\nThis cookbook uses the apps data bag item for the specified application; see the `application` cookbook's README.md. It also creates data bag items in a bag named 'aws' for storing volume information. In order to interact with EC2, it expects aws to have a main item:\n\n```javascript\n{\n \"id\": \"main\",\n \"ec2_private_key\": \"private key as a string\",\n \"ec2_cert\": \"certificate as a string\",\n \"aws_account_id\": \"\",\n \"aws_secret_access_key\": \"\",\n \"aws_access_key_id\": \"\"\n}\n```\n\nNote: with the Open Source Chef Server, the server using the database recipes must be an admin client or it will not be able to create data bag items. You can modify whether the client is admin by editing it with knife.\n\n knife client edit \n {\n ...\n \"admin\": true\n ...\n }\n\nThis is not required if the Chef Server is Opscode Hosted Chef, instead use the ACL feature to modify access for the node to be able to update the data bag.\n\n\nUsage\n-----\nAside from the application data bag (see the README in the application cookbook), create a role for the database master. Use a `role.rb` in your chef-repo, or create the role directly with knife.\n\n```javascript\n{\n \"name\": \"my_app_database_master\",\n \"chef_type\": \"role\",\n \"json_class\": \"Chef::Role\",\n \"default_attributes\": {},\n \"description\": \"\",\n \"run_list\": [\n \"recipe[mysql::server]\",\n \"recipe[database::master]\"\n ],\n \"override_attributes\": {}\n}\n```\n\nCreate a `production` environment. This is also used in the `application` cookbook.\n\n```javascript\n{\n \"name\": \"production\",\n \"description\": \"\",\n \"cookbook_versions\": {},\n \"json_class\": \"Chef::Environment\",\n \"chef_type\": \"environment\",\n \"default_attributes\": {},\n \"override_attributes\": {}\n}\n```\n\nThe cookbook `my_app_database` is recommended to set up any application specific database resources such as configuration templates, trending monitors, etc. It is not required, but you would need to create it separately in `site-cookbooks`. Add it to the `my_app_database_master` role.\n\nLicense & Authors\n-----------------\n- Author:: Adam Jacob ()\n- Author:: Joshua Timberman ()\n- Author:: AJ Christensen ()\n- Author:: Seth Chisamore ()\n- Author:: Lamont Granquist ()\n\n```text\nCopyright 2009-2013, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "debian": ">= 0.0.0", + "ubuntu": ">= 0.0.0", + "centos": ">= 0.0.0", + "suse": ">= 0.0.0", + "fedora": ">= 0.0.0", + "redhat": ">= 0.0.0", + "scientific": ">= 0.0.0", + "amazon": ">= 0.0.0" + }, + "dependencies": { + "mysql": ">= 1.3.0", + "postgresql": ">= 1.0.0", + "aws": ">= 0.0.0", + "xfs": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "database": "Empty placeholder", + "database::ebs_backup": "Considered deprecated, older way of backing up EBS volumes", + "database::ebs_volume": "Sets up an EBS volume in EC2 for the database", + "database::master": "Creates application specific user and database", + "database::snapshot": "Locks tables and freezes XFS filesystem for replication, assumes EC2 + EBS" + } +} \ No newline at end of file diff --git a/chef/cookbooks/database/metadata.rb b/chef/cookbooks/database/metadata.rb index e579288..e0be9f0 100644 --- a/chef/cookbooks/database/metadata.rb +++ b/chef/cookbooks/database/metadata.rb @@ -4,7 +4,7 @@ maintainer_email "cookbooks@opscode.com" license "Apache 2.0" description "Sets up the database master or slave" long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "1.4.1" +version "2.0.0" recipe "database", "Empty placeholder" recipe "database::ebs_backup", "Considered deprecated, older way of backing up EBS volumes" diff --git a/chef/cookbooks/database/templates/default/s3cfg.erb b/chef/cookbooks/database/templates/default/s3cfg.erb index c2f818c..f193656 100644 --- a/chef/cookbooks/database/templates/default/s3cfg.erb +++ b/chef/cookbooks/database/templates/default/s3cfg.erb @@ -11,13 +11,13 @@ force = False gpg_command = /usr/bin/gpg gpg_decrypt = %(gpg_command)s -d --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s gpg_encrypt = %(gpg_command)s -c --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s -gpg_passphrase = +gpg_passphrase = guess_mime_type = False host_base = s3.amazonaws.com host_bucket = %(bucket)s.s3.amazonaws.com human_readable_sizes = False preserve_attrs = True -proxy_host = +proxy_host = proxy_port = 0 recv_chunk = 4096 secret_key = <%= @aws['aws_secret_access_key'] %> diff --git a/chef/cookbooks/dmg/CHANGELOG.md b/chef/cookbooks/dmg/CHANGELOG.md index ecaba9d..4d5db2d 100644 --- a/chef/cookbooks/dmg/CHANGELOG.md +++ b/chef/cookbooks/dmg/CHANGELOG.md @@ -3,6 +3,54 @@ dmg Cookbook CHANGELOG This file is used to list changes made in each version of the dmg ookbook. +v2.2.0 (2014-02-25) +------------------- +- [COOK-4285] Accept long EULAs + + +v2.1.4 (2014-01-26) +------------------- +* [COOK-4157] - dmg_package LWRP broken due to "puts" instead of "system" +* [COOK-4065] - dmg cookbook outputs the name of packages when checking if they are installed + + +v2.1.2 +------ +Cleaning up merge errors + + +v2.1.0 +------ +### Bug +- **[COOK-3946](https://tickets.opscode.com/browse/COOK-3946)** - Syntax error in resources/package.rb +- **[COOK-2672](https://tickets.opscode.com/browse/COOK-2672)** - EULA for package is displayed instead accepted + + +v2.0.8 +------ +Adding a Chef 10 compatibility check in provider + + +v2.0.6 +------ +# BUG +- [COOK-3302] - Sometimes hdiutil detach fails due to cfprefsd running in background +# IMPROVEMENT +- Adding foodcritic and rubocop to .travis.yml + + +v2.0.4 +------ +### Bug +- **[COOK-3331](https://tickets.opscode.com/browse/COOK-3331)** - Fix an issue where `dmg_package` with no source raises an exception + + +v2.0.2 +------ +### Bug +- **[COOK-3578](https://tickets.opscode.com/browse/COOK-3578)** - Support `package_id`s with spaces +- **[COOK-3302](https://tickets.opscode.com/browse/COOK-3302)** - Fix an issue where `hdiutil detach` fails due to `cfprefsd` running in the background + v2.0.0 ------ ### Bug diff --git a/chef/cookbooks/dmg/CONTRIBUTING b/chef/cookbooks/dmg/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/dmg/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/dmg/LICENSE b/chef/cookbooks/dmg/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/dmg/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/dmg/README.md b/chef/cookbooks/dmg/README.md index cbf74db..305eb38 100644 --- a/chef/cookbooks/dmg/README.md +++ b/chef/cookbooks/dmg/README.md @@ -1,133 +1,133 @@ -Description -=========== - +dmg Cookbook +============ Lightweight resource and provider to install OS X applications (.app) from dmg files. + Requirements -============ - -## Platform: - -* Mac OS X - -Resources and Providers -======================= - -dmg\_package ------------ +### Platform +- Mac OS X + + +Resources/Providers +------------------- +### dmg_package This resource will install a DMG "Package". It will retrieve the DMG from a remote URL, mount it using OS X's `hdid`, copy the application (.app directory) to the specified destination (/Applications), and detach the image using `hdiutil`. The dmg file will be stored in the `Chef::Config[:file_cache_path]`. If you want to install an application that has already been downloaded (not using the `source` parameter), copy it to the appropriate location. You can find out what directory this is with the following command on the node to run chef: - knife exec -E 'p Chef::Config[:file_cache_path]' -c /etc/chef/client.rb +```bash +knife exec -E 'p Chef::Config[:file_cache_path]' -c /etc/chef/client.rb +``` Optionally, the LWRP can install an "mpkg" or "pkg" package using installer(8). -# Actions: +#### Actions +- :install - Installs the application. -* :install - Installs the application. - -# Parameter attributes: - -* `app` - This is the name of the application used by default for the /Volumes directory and the .app directory copied to /Applications. -* `source` - remote URL for the dmg to download if specified. Default is nil. -* `owner` - owner that should own the package installation. -* `destination` - directory to copy the .app into. Default is /Applications. -* `checksum` - sha256 checksum of the dmg to download. Default is nil. -* `type` - type of package, "app", "pkg" or "mpkg". Default is "app". When using "pkg" or "mpkg", the destination must be /Applications. -* `volumes_dir` - Directory under /Volumes where the dmg is mounted. Not all dmgs are mounted into a /Volumes location matching the name of the dmg. If not specified, this will use the name attribute. -* `package_id` - Package id registered with pkgutil when a pkg or mpkg is installed -* `dmg_name` - Specify the name of the dmg if it is not the same as `app`, or if the name has spaces. -* `dmg_passphrase` - Specify a passphrase to use to unencrypt the dmg while mounting. -* `accept_eula` - Specify whether to accept the EULA. Certain dmgs require acceptance of EULA before mounting. Can be true or false, defaults to false. - -Usage Examples -============== +#### Parameter attributes: +- `app` - This is the name of the application used by default for the /Volumes directory and the .app directory copied to /Applications. +- `source` - remote URL for the dmg to download if specified. Default is nil. +- `owner` - owner that should own the package installation. +- `destination` - directory to copy the .app into. Default is /Applications. +- `checksum` - sha256 checksum of the dmg to download. Default is nil. +- `type` - type of package, "app", "pkg" or "mpkg". Default is "app". When using "pkg" or "mpkg", the destination must be /Applications. +- `volumes_dir` - Directory under /Volumes where the dmg is mounted. Not all dmgs are mounted into a /Volumes location matching the name of the dmg. If not specified, this will use the name attribute. +- `package_id` - Package id registered with pkgutil when a pkg or mpkg is installed +- `dmg_name` - Specify the name of the dmg if it is not the same as `app`, or if the name has spaces. +- `dmg_passphrase` - Specify a passphrase to use to unencrypt the dmg while mounting. +- `accept_eula` - Specify whether to accept the EULA. Certain dmgs require acceptance of EULA before mounting. Can be true or false, defaults to false. +#### Examples Install `/Applications/Tunnelblick.app` from the primary download site. - dmg_package "Tunnelblick" do - source "http://tunnelblick.googlecode.com/files/Tunnelblick_3.1.2.dmg" - checksum "a3fae60b6833175f32df20c90cd3a3603a" - action :install - end +```ruby +dmg_package 'Tunnelblick' do + source 'http://tunnelblick.googlecode.com/files/Tunnelblick_3.1.2.dmg' + checksum 'a3fae60b6833175f32df20c90cd3a3603a' + action :install +end +``` Install Google Chrome. Uses the `dmg_name` because the application name has spaces. Installs in `/Applications/Google Chrome.app`. - dmg_package "Google Chrome" do - dmg_name "googlechrome" - source "https://dl-ssl.google.com/chrome/mac/stable/GGRM/googlechrome.dmg" - checksum "7daa2dc5c46d9bfb14f1d7ff4b33884325e5e63e694810adc58f14795165c91a" - action :install - end +```ruby +dmg_package 'Google Chrome' do + dmg_name 'googlechrome' + source 'https://dl-ssl.google.com/chrome/mac/stable/GGRM/googlechrome.dmg' + checksum '7daa2dc5c46d9bfb14f1d7ff4b33884325e5e63e694810adc58f14795165c91a' + action :install +end +``` Install Dropbox. Uses `volumes_dir` because the mounted directory is different than the name of the application directory. Installs in `/Applications/Dropbox.app`. - dmg_package "Dropbox" do - volumes_dir "Dropbox Installer" - source "http://www.dropbox.com/download?plat=mac" - checksum "b4ea620ca22b0517b75753283ceb82326aca8bc3c86212fbf725de6446a96a13" - action :install - end +```ruby +dmg_package 'Dropbox' do + volumes_dir 'Dropbox Installer' + source 'http://www.dropbox.com/download?plat=mac' + checksum 'b4ea620ca22b0517b75753283ceb82326aca8bc3c86212fbf725de6446a96a13' + action :install +end +``` Install MacIrssi to `~/Applications` from the local file downloaded to the cache path into an Applications directory in the current user's home directory. Chef should run as a non-root user for this. - directory "#{ENV['HOME']}/Applications" +```ruby +directory "#{ENV['HOME']}/Applications" - dmg_package "MacIrssi" do - destination "#{ENV['HOME']}/Applications" - action :install - end +dmg_package 'MacIrssi' do + destination "#{ENV['HOME']}/Applications" + action :install +end +``` Install Virtualbox to `/Applications` from the .mpkg: - dmg_package "Virtualbox" do - source "http://dlc.sun.com.edgesuite.net/virtualbox/4.0.8/VirtualBox-4.0.8-71778-OSX.dmg" - type "mpkg" - end +```ruby +dmg_package 'Virtualbox' do + source 'http://dlc.sun.com.edgesuite.net/virtualbox/4.0.8/VirtualBox-4.0.8-71778-OSX.dmg' + type 'mpkg' +end +``` Install pgAdmin to `/Applications` and automatically accept the EULA: - dmg_package "pgAdmin3" do - source "http://wwwmaster.postgresql.org/redir/198/h/pgadmin3/release/v1.12.3/osx/pgadmin3-1.12.3.dmg" - checksum "9435f79d5b52d0febeddfad392adf82db9df159196f496c1ab139a6957242ce9" - accept_eula true - end +```ruby +dmg_package 'pgAdmin3' do + source 'http://wwwmaster.postgresql.org/redir/198/h/pgadmin3/release/v1.12.3/osx/pgadmin3-1.12.3.dmg' + checksum '9435f79d5b52d0febeddfad392adf82db9df159196f496c1ab139a6957242ce9' + accept_eula true +end +``` Install Pivotal Tracker to `/Applications` using a password-protected dmg: - dmg_package "Pivotal Tracker" do - volumes_dir "tracker" - source "http://cheffiles.pivotallabs.com/fluid_tracker.dmg" - dmg_passphrase "xyz" - end +```ruby +dmg_package 'Pivotal Tracker' do + volumes_dir 'tracker' + source 'http://cheffiles.pivotallabs.com/fluid_tracker.dmg' + dmg_passphrase 'xyz' +end +``` Install Silverlight, with idempotence check based on pkgutil: - dmg_package "Silerlight" do - source "http://silverlight.dlservice.microsoft.com/download/D/C/2/DC2D5838-9138-4D25-AA92-52F61F7C51E6/runtime/Silverlight.dmg" - type "pkg" - checksum "6d4a0ad4552d9815531463eb3f467fb8cf4bffcc" - package_id "com.microsoft.installSilverlightPlugin" - end +```ruby +dmg_package 'Silerlight' do + source 'http://silverlight.dlservice.microsoft.com/download/D/C/2/DC2D5838-9138-4D25-AA92-52F61F7C51E6/runtime/Silverlight.dmg' + type 'pkg' + checksum '6d4a0ad4552d9815531463eb3f467fb8cf4bffcc' + package_id 'com.microsoft.installSilverlightPlugin' +end +``` -To do -===== -A few things remain outstanding to make this cookbook "1.0" quality. +License & Authors +----------------- +- Author:: Joshua Timberman (joshua@opscode.com) -* support downloading a .dmg.zip and unzipping it -* specify a local .dmg already downloaded in another location (like ~/Downloads) - -Some things that would be nice to have at some point. - -* use hdiutil to mount/attach the disk image -* automatically detect the `volumes_dir` where the image is attached -* be able to automatically accept license agreements - -License and Author -================== - -* Copyright 2011, Joshua Timberman +```text +Copyright 2011, Joshua Timberman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -140,3 +140,4 @@ 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. +``` diff --git a/chef/cookbooks/dmg/attributes/default.rb b/chef/cookbooks/dmg/attributes/default.rb index 4935ec8..29949c4 100644 --- a/chef/cookbooks/dmg/attributes/default.rb +++ b/chef/cookbooks/dmg/attributes/default.rb @@ -1,4 +1,4 @@ -# +# Encoding: utf-8 # Cookbook Name:: dmg # Attributes:: default # @@ -16,5 +16,5 @@ # See the License for the specific language governing permissions and # limitations under the License. # -default[:dmg][:base_dir] = "/Applications" -default[:dmg][:cache_dir] = Chef::Config[:file_cache_path] +default['dmg']['base_dir'] = '/Applications' +default['dmg']['cache_dir'] = Chef::Config[:file_cache_path] diff --git a/chef/cookbooks/dmg/metadata.json b/chef/cookbooks/dmg/metadata.json new file mode 100644 index 0000000..10608e6 --- /dev/null +++ b/chef/cookbooks/dmg/metadata.json @@ -0,0 +1,30 @@ +{ + "name": "dmg", + "version": "2.2.0", + "description": "LWRP to install OS X applications from dmgs", + "long_description": "dmg Cookbook\n============\nLightweight resource and provider to install OS X applications (.app) from dmg files.\n\n\nRequirements\n------------\n### Platform\n- Mac OS X\n\n\nResources/Providers\n-------------------\n### dmg_package\n\nThis resource will install a DMG \"Package\". It will retrieve the DMG from a remote URL, mount it using OS X's `hdid`, copy the application (.app directory) to the specified destination (/Applications), and detach the image using `hdiutil`. The dmg file will be stored in the `Chef::Config[:file_cache_path]`. If you want to install an application that has already been downloaded (not using the `source` parameter), copy it to the appropriate location. You can find out what directory this is with the following command on the node to run chef:\n\n```bash\nknife exec -E 'p Chef::Config[:file_cache_path]' -c /etc/chef/client.rb\n```\n\nOptionally, the LWRP can install an \"mpkg\" or \"pkg\" package using installer(8).\n\n#### Actions\n- :install - Installs the application.\n\n#### Parameter attributes:\n- `app` - This is the name of the application used by default for the /Volumes directory and the .app directory copied to /Applications.\n- `source` - remote URL for the dmg to download if specified. Default is nil.\n- `owner` - owner that should own the package installation.\n- `destination` - directory to copy the .app into. Default is /Applications.\n- `checksum` - sha256 checksum of the dmg to download. Default is nil.\n- `type` - type of package, \"app\", \"pkg\" or \"mpkg\". Default is \"app\". When using \"pkg\" or \"mpkg\", the destination must be /Applications.\n- `volumes_dir` - Directory under /Volumes where the dmg is mounted. Not all dmgs are mounted into a /Volumes location matching the name of the dmg. If not specified, this will use the name attribute.\n- `package_id` - Package id registered with pkgutil when a pkg or mpkg is installed\n- `dmg_name` - Specify the name of the dmg if it is not the same as `app`, or if the name has spaces.\n- `dmg_passphrase` - Specify a passphrase to use to unencrypt the dmg while mounting.\n- `accept_eula` - Specify whether to accept the EULA. Certain dmgs require acceptance of EULA before mounting. Can be true or false, defaults to false.\n\n#### Examples\nInstall `/Applications/Tunnelblick.app` from the primary download site.\n\n```ruby\ndmg_package 'Tunnelblick' do\n source 'http://tunnelblick.googlecode.com/files/Tunnelblick_3.1.2.dmg'\n checksum 'a3fae60b6833175f32df20c90cd3a3603a'\n action :install\nend\n```\n\nInstall Google Chrome. Uses the `dmg_name` because the application name has spaces. Installs in `/Applications/Google Chrome.app`.\n\n```ruby\ndmg_package 'Google Chrome' do\n dmg_name 'googlechrome'\n source 'https://dl-ssl.google.com/chrome/mac/stable/GGRM/googlechrome.dmg'\n checksum '7daa2dc5c46d9bfb14f1d7ff4b33884325e5e63e694810adc58f14795165c91a'\n action :install\nend\n```\n\nInstall Dropbox. Uses `volumes_dir` because the mounted directory is different than the name of the application directory. Installs in `/Applications/Dropbox.app`.\n\n```ruby\ndmg_package 'Dropbox' do\n volumes_dir 'Dropbox Installer'\n source 'http://www.dropbox.com/download?plat=mac'\n checksum 'b4ea620ca22b0517b75753283ceb82326aca8bc3c86212fbf725de6446a96a13'\n action :install\nend\n```\n\nInstall MacIrssi to `~/Applications` from the local file downloaded to the cache path into an Applications directory in the current user's home directory. Chef should run as a non-root user for this.\n\n```ruby\ndirectory \"#{ENV['HOME']}/Applications\"\n\ndmg_package 'MacIrssi' do\n destination \"#{ENV['HOME']}/Applications\"\n action :install\nend\n```\n\nInstall Virtualbox to `/Applications` from the .mpkg:\n\n```ruby\ndmg_package 'Virtualbox' do\n source 'http://dlc.sun.com.edgesuite.net/virtualbox/4.0.8/VirtualBox-4.0.8-71778-OSX.dmg'\n type 'mpkg'\nend\n```\n\nInstall pgAdmin to `/Applications` and automatically accept the EULA:\n\n```ruby\ndmg_package 'pgAdmin3' do\n source 'http://wwwmaster.postgresql.org/redir/198/h/pgadmin3/release/v1.12.3/osx/pgadmin3-1.12.3.dmg'\n checksum '9435f79d5b52d0febeddfad392adf82db9df159196f496c1ab139a6957242ce9'\n accept_eula true\nend\n```\n\nInstall Pivotal Tracker to `/Applications` using a password-protected dmg:\n\n```ruby\ndmg_package 'Pivotal Tracker' do\n volumes_dir 'tracker'\n source 'http://cheffiles.pivotallabs.com/fluid_tracker.dmg'\n dmg_passphrase 'xyz'\nend\n```\n\nInstall Silverlight, with idempotence check based on pkgutil:\n\n```ruby\ndmg_package 'Silerlight' do\n source 'http://silverlight.dlservice.microsoft.com/download/D/C/2/DC2D5838-9138-4D25-AA92-52F61F7C51E6/runtime/Silverlight.dmg'\n type 'pkg'\n checksum '6d4a0ad4552d9815531463eb3f467fb8cf4bffcc'\n package_id 'com.microsoft.installSilverlightPlugin'\nend\n```\n\n\nLicense & Authors\n-----------------\n- Author:: Joshua Timberman (joshua@opscode.com)\n\n```text\nCopyright 2011, Joshua Timberman \n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Opscode, Inc", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "mac_os_x": ">= 0.0.0" + }, + "dependencies": { + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + } +} \ No newline at end of file diff --git a/chef/cookbooks/dmg/metadata.rb b/chef/cookbooks/dmg/metadata.rb index 0738cf4..d7165ee 100644 --- a/chef/cookbooks/dmg/metadata.rb +++ b/chef/cookbooks/dmg/metadata.rb @@ -1,8 +1,9 @@ -name "dmg" -maintainer "Joshua Timberman" -maintainer_email "cookbooks@housepub.org" -license "Apache 2.0" -description "LWRP to install OS X applications from dmgs" +# Encoding: utf-8 +name 'dmg' +maintainer 'Opscode, Inc' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'LWRP to install OS X applications from dmgs' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "2.0.1" -supports "mac_os_x" +version '2.2.0' +supports 'mac_os_x' diff --git a/chef/cookbooks/dmg/providers/package.rb b/chef/cookbooks/dmg/providers/package.rb index 7b36d7b..a06e051 100644 --- a/chef/cookbooks/dmg/providers/package.rb +++ b/chef/cookbooks/dmg/providers/package.rb @@ -1,4 +1,4 @@ -# +# Encoding: utf-8 # Cookbook Name:: dmg # Provider:: package # @@ -17,6 +17,10 @@ # limitations under the License. # +include Chef::Mixin::ShellOut + +use_inline_resources if defined?(use_inline_resources) + def load_current_resource @dmgpkg = Chef::Resource::DmgPackage.new(new_resource.name) @dmgpkg.app(new_resource.app) @@ -31,26 +35,28 @@ action :install do dmg_name = new_resource.dmg_name ? new_resource.dmg_name : new_resource.app dmg_file = "#{Chef::Config[:file_cache_path]}/#{dmg_name}.dmg" - remote_file "#{dmg_file} - #{@dmgpkg.name}" do - path dmg_file - source new_resource.source - checksum new_resource.checksum if new_resource.checksum - only_if { new_resource.source } + if new_resource.source + remote_file "#{dmg_file} - #{@dmgpkg.name}" do + path dmg_file + source new_resource.source + checksum new_resource.checksum if new_resource.checksum + end end - passphrase_cmd = new_resource.dmg_passphrase ? "-passphrase #{new_resource.dmg_passphrase}" : "" + passphrase_cmd = new_resource.dmg_passphrase ? "-passphrase #{new_resource.dmg_passphrase}" : '' ruby_block "attach #{dmg_file}" do block do - software_license_agreement = system("hdiutil imageinfo #{passphrase_cmd} '#{dmg_file}' | grep -q 'Software License Agreement: true'") - raise "Requires EULA Acceptance; add 'accept_eula true' to package resource" if software_license_agreement && !new_resource.accept_eula - accept_eula_cmd = new_resource.accept_eula ? "echo Y |" : "" - system "#{accept_eula_cmd} hdiutil attach #{passphrase_cmd} '#{dmg_file}'" + cmd = shell_out("hdiutil imageinfo #{passphrase_cmd} '#{dmg_file}' | grep -q 'Software License Agreement: true'") + software_license_agreement = (cmd.exitstatus == 0) + fail "Requires EULA Acceptance; add 'accept_eula true' to package resource" if software_license_agreement && !new_resource.accept_eula + accept_eula_cmd = new_resource.accept_eula ? 'echo Y | PAGER=true' : '' + shell_out!("#{accept_eula_cmd} hdiutil attach #{passphrase_cmd} '#{dmg_file}' -quiet") end not_if "hdiutil info #{passphrase_cmd} | grep -q 'image-path.*#{dmg_file}'" end case new_resource.type - when "app" + when 'app' execute "rsync --force --recursive --links --perms --executability --owner --group --times '/Volumes/#{volumes_dir}/#{new_resource.app}.app' '#{new_resource.destination}'" do user new_resource.owner if new_resource.owner end @@ -59,22 +65,25 @@ action :install do mode 0755 ignore_failure true end - when "mpkg", "pkg" - execute "sudo installer -pkg '/Volumes/#{volumes_dir}/#{new_resource.app}.#{new_resource.type}' -target /" + when 'mpkg', 'pkg' + execute "sudo installer -pkg '/Volumes/#{volumes_dir}/#{new_resource.app}.#{new_resource.type}' -target /" do + # Prevent cfprefsd from holding up hdiutil detach for certain disk images + environment('__CFPREFERENCES_AVOID_DAEMON' => '1') if Gem::Version.new(node['platform_version']) >= Gem::Version.new('10.8') + end end - execute "hdiutil detach '/Volumes/#{volumes_dir}'" + execute "hdiutil detach '/Volumes/#{volumes_dir}' || hdiutil detach '/Volumes/#{volumes_dir}' -force" end end private def installed? - if ( ::File.directory?("#{new_resource.destination}/#{new_resource.app}.app") ) + if ::File.directory?("#{new_resource.destination}/#{new_resource.app}.app") Chef::Log.info "Already installed; to upgrade, remove \"#{new_resource.destination}/#{new_resource.app}.app\"" true - elsif ( system("pkgutil --pkgs=#{new_resource.package_id}") ) - Chef::Log.info "Already installed; to upgrade, try \"sudo pkgutil --forget #{new_resource.package_id}\"" + elsif shell_out("pkgutil --pkgs='#{new_resource.package_id}'").exitstatus == 0 + Chef::Log.info "Already installed; to upgrade, try \"sudo pkgutil --forget '#{new_resource.package_id}'\"" true else false diff --git a/chef/cookbooks/dmg/recipes/default.rb b/chef/cookbooks/dmg/recipes/default.rb index 6fcb02a..7cb2db4 100644 --- a/chef/cookbooks/dmg/recipes/default.rb +++ b/chef/cookbooks/dmg/recipes/default.rb @@ -1,4 +1,4 @@ -# +# Encoding: utf-8 # Cookbook Name:: dmg # Recipe:: default # diff --git a/chef/cookbooks/dmg/resources/package.rb b/chef/cookbooks/dmg/resources/package.rb index 3c83681..1c0ad52 100644 --- a/chef/cookbooks/dmg/resources/package.rb +++ b/chef/cookbooks/dmg/resources/package.rb @@ -1,4 +1,4 @@ -# +# Encoding: utf-8 # Cookbook Name:: dmg # Resource:: package # @@ -25,13 +25,13 @@ attribute :destination, :kind_of => String, :default => "/Applications" attribute :checksum, :kind_of => String, :default => nil attribute :volumes_dir, :kind_of => String, :default => nil attribute :dmg_name, :kind_of => String, :default => nil -attribute :type, :kind_of => String, :default => "app" +attribute :type, :kind_of => String, :default => 'app' attribute :installed, :kind_of => [TrueClass, FalseClass], :default => false attribute :package_id, :kind_of => String, :default => nil attribute :dmg_passphrase, :kind_of => String, :default => nil attribute :accept_eula, :kind_of => [TrueClass, FalseClass], :default => false -def initialize(name, run_context=nil) +def initialize(name, run_context = nil) super @action = :install end diff --git a/chef/cookbooks/erlang/.kitchen.yml b/chef/cookbooks/erlang/.kitchen.yml deleted file mode 100644 index bb261dd..0000000 --- a/chef/cookbooks/erlang/.kitchen.yml +++ /dev/null @@ -1,54 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: ["recipe[apt]"] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: ["recipe[apt]"] - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - -suites: -- name: default - run_list: - - "recipe[minitest-handler]" - - "recipe[erlang]" - attributes: {} - -- name: gui_tools - run_list: - - "recipe[minitest-handler]" - - "recipe[erlang]" - attributes: {erlang: {gui_tools: true}} - excludes: ["centos-5.9", "centos-6.4"] - -- name: esl - run_list: - - "recipe[minitest-handler]" - - "recipe[erlang::esl]" - attributes: {} - excludes: ["centos-5.9", "centos-6.4"] - -- name: source - run_list: - - "recipe[minitest-handler]" - - "recipe[erlang::source]" - attributes: {} diff --git a/chef/cookbooks/erlang/Berksfile b/chef/cookbooks/erlang/Berksfile deleted file mode 100644 index f2f9d86..0000000 --- a/chef/cookbooks/erlang/Berksfile +++ /dev/null @@ -1,8 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook "apt" - cookbook "minitest-handler" -end diff --git a/chef/cookbooks/erlang/CHANGELOG.md b/chef/cookbooks/erlang/CHANGELOG.md index ffe1d5c..9fa423d 100644 --- a/chef/cookbooks/erlang/CHANGELOG.md +++ b/chef/cookbooks/erlang/CHANGELOG.md @@ -1,7 +1,27 @@ -erland Cookbook CHANGELOG +erlang Cookbook CHANGELOG ========================= This file is used to list changes made in each version of the erlang cookbook. +v1.4.2 +------ +COOK-4155, use a version in version conditional + +v1.4.0 +------ +Porting to use cookbook yum ~> 3.0 +Moving tests from minitest to bats +Fixing style against rubocop + + +v1.3.6 +------ +fixing metadata version error. locking to 3.0 + + +v1.3.4 +------ +Locking yum dependency to '< 3' + v1.3.2 ------ diff --git a/chef/cookbooks/erlang/CONTRIBUTING.md b/chef/cookbooks/erlang/CONTRIBUTING.md deleted file mode 100644 index 3a99897..0000000 --- a/chef/cookbooks/erlang/CONTRIBUTING.md +++ /dev/null @@ -1,257 +0,0 @@ -# Contributing to Opscode Cookbooks - -We are glad you want to contribute to Opscode Cookbooks! The first -step is the desire to improve the project. - -You can find the answers to additional frequently asked questions -[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). - -You can find additional information about -[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) -on the wiki as well. - -## Quick-contribute - -* Create an account on our [bug tracker](http://tickets.opscode.com) -* Sign our contributor agreement (CLA) -[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) -(keep reading if you're contributing on behalf of your employer) -* Create a ticket for your change on the - [bug tracker](http://tickets.opscode.com) -* Link to your patch as a rebased git branch or pull request from the - ticket -* Resolve the ticket as fixed - -We regularly review contributions and will get back to you if we have -any suggestions or concerns. - -## The Apache License and the CLA/CCLA - -Licensing is very important to open source projects, it helps ensure -the software continues to be available under the terms that the author -desired. Chef uses the Apache 2.0 license to strike a balance between -open contribution and allowing you to use the software however you -would like to. - -The license tells you what rights you have that are provided by the -copyright holder. It is important that the contributor fully -understands what rights they are licensing and agrees to them. -Sometimes the copyright holder isn't the contributor, most often when -the contributor is doing work for a company. - -To make a good faith effort to ensure these criteria are met, Opscode -requires a Contributor License Agreement (CLA) or a Corporate -Contributor License Agreement (CCLA) for all contributions. This is -without exception due to some matters not being related to copyright -and to avoid having to continually check with our lawyers about small -patches. - -It only takes a few minutes to complete a CLA, and you retain the -copyright to your contribution. - -You can complete our contributor agreement (CLA) -[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). -If you're contributing on behalf of your employer, have your employer -fill out our -[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) -instead. - -## Ticket Tracker (JIRA) - -The [ticket tracker](http://tickets.opscode.com) is the most important -documentation for the code base. It provides significant historical -information, such as: - -* Which release a bug fix is included in -* Discussion regarding the design and merits of features -* Error output to aid in finding similar bugs - -Each ticket should aim to fix one bug or add one feature. - -## Using git - -You can get a quick copy of the repository for this cookbook by -running `git clone -git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. - -For collaboration purposes, it is best if you create a Github account -and fork the repository to your own account. Once you do this you will -be able to push your changes to your Github repository for others to -see and use. - -If you have another repository in your GitHub account named the same -as the cookbook, we suggest you suffix the repository with -cookbook. - -### Branches and Commits - -You should submit your patch as a git branch named after the ticket, -such as COOK-1337. This is called a _topic branch_ and allows users to -associate a branch of code with the ticket. - -It is a best practice to have your commit message have a _summary -line_ that includes the ticket number, followed by an empty line and -then a brief description of the commit. This also helps other -contributors understand the purpose of changes to the code. - - [COOK-1757] - platform_family and style - - * use platform_family for platform checking - * update notifies syntax to "resource_type[resource_name]" instead of - resources() lookup - * COOK-692 - delete config files dropped off by packages in conf.d - * dropped debian 4 support because all other platforms have the same - values, and it is older than "old stable" debian release - -Remember that not all users use Chef in the same way or on the same -operating systems as you, so it is helpful to be clear about your use -case and change so they can understand it even when it doesn't apply -to them. - -### Github and Pull Requests - -All of Opscode's open source cookbook projects are available on -[Github](http://www.github.com/opscode-cookbooks). - -We don't require you to use Github, and we will even take patch diffs -attached to tickets on the tracker. However Github has a lot of -convenient features, such as being able to see a diff of changes -between a pull request and the main repository quickly without -downloading the branch. - -If you do choose to use a pull request, please provide a link to the -pull request from the ticket __and__ a link to the ticket from the -pull request. Because pull requests only have two states, open and -closed, we can't easily filter pull requests that are waiting for a -reply from the author for various reasons. - -### More information - -Additional help with git is available on the -[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) -wiki page. - -## Functional and Unit Tests - -This cookbook is set up to run tests under -[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It -uses minitest-chef to run integration tests after the node has been -converged to verify that the state of the node. - -Test kitchen should run completely without exception using the default -[baseboxes provided by Opscode](https://github.com/opscode/bento). -Because Test Kitchen creates VirtualBox machines and runs through -every configuration in the Kitchenfile, it may take some time for -these tests to complete. - -If your changes are only for a specific recipe, run only its -configuration with Test Kitchen. If you are adding a new recipe, or -other functionality such as a LWRP or definition, please add -appropriate tests and ensure they run with Test Kitchen. - -If any don't pass, investigate them before submitting your patch. - -Any new feature should have unit tests included with the patch with -good code coverage to help protect it from future changes. Similarly, -patches that fix a bug or regression should have a _regression test_. -Simply put, this is a test that would fail without your patch but -passes with it. The goal is to ensure this bug doesn't regress in the -future. Consider a regular expression that doesn't match a certain -pattern that it should, so you provide a patch and a test to ensure -that the part of the code that uses this regular expression works as -expected. Later another contributor may modify this regular expression -in a way that breaks your use cases. The test you wrote will fail, -signalling to them to research your ticket and use case and accounting -for it. - -If you need help writing tests, please ask on the Chef Developer's -mailing list, or the #chef-hacking IRC channel. - -## Code Review - -Opscode regularly reviews code contributions and provides suggestions -for improvement in the code itself or the implementation. - -We find contributions by searching the ticket tracker for _resolved_ -tickets with a status of _fixed_. If we have feedback we will reopen -the ticket and you should resolve it again when you've made the -changes or have a response to our feedback. When we believe the patch -is ready to be merged, we will tag the _Code Reviewed_ field with -_Reviewed_. - -Depending on the project, these tickets are then merged within a week -or two, depending on the current release cycle. - -## Release Cycle - -The versioning for Opscode Cookbook projects is X.Y.Z. - -* X is a major release, which may not be fully compatible with prior - major releases -* Y is a minor release, which adds both new features and bug fixes -* Z is a patch release, which adds just bug fixes - -A released version of a cookbook will end in an even number, e.g. -"1.2.4" or "0.8.0". When development for the next version of the -cookbook begins, the "Z" patch number is incremented to the next odd -number, however the next release of the cookbook may be a major or -minor incrementing version. - -Releases of Opscode's cookbooks are usually announced on the Chef user -mailing list. Releases of several cookbooks may be batched together -and announced on the [Opscode Blog](http://www.opscode.com/blog). - -## Working with the community - -These resources will help you learn more about Chef and connect to -other members of the Chef community: - -* [chef](http://lists.opscode.com/sympa/info/chef) and - [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing - lists -* #chef and #chef-hacking IRC channels on irc.freenode.net -* [Community Cookbook site](http://community.opscode.com) -* [Chef wiki](http://wiki.opscode.com/display/chef) -* Opscode Chef [product page](http://www.opscode.com/chef) - - -## Cookbook Contribution Do's and Don't's - -Please do include tests for your contribution. If you need help, ask -on the -[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) -or the -[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). -Not all platforms that a cookbook supports may be supported by Test -Kitchen. Please provide evidence of testing your contribution if it -isn't trivial so we don't have to duplicate effort in testing. Chef -10.14+ "doc" formatted output is sufficient. - -Please do indicate new platform (families) or platform versions in the -commit message, and update the relevant ticket. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] - Updated pool resource to correctly - delete.' - -Please do use [foodcritic](http://acrmp.github.com/foodcritic) to -lint-check the cookbook. Except FC007, it should pass all correctness -rules. FC007 is okay as long as the dependent cookbooks are *required* -for the default behavior of the cookbook, such as to support an -uncommon platform, secondary recipe, etc. - -Please do ensure that your changes do not break or modify behavior for -other platforms supported by the cookbook. For example if your changes -are for Debian, make sure that they do not break on CentOS. - -Please do not modify the version number in the metadata.rb, Opscode -will select the appropriate version based on the release cycle -information above. - -Please do not update the CHANGELOG.md for a new version. Not all -changes to a cookbook may be merged and released in the same versions. -Opscode will update the CHANGELOG.md when releasing a new version of -the cookbook. diff --git a/chef/cookbooks/erlang/LICENSE b/chef/cookbooks/erlang/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/erlang/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/erlang/TESTING.md b/chef/cookbooks/erlang/TESTING.md deleted file mode 100644 index e29ff7c..0000000 --- a/chef/cookbooks/erlang/TESTING.md +++ /dev/null @@ -1,25 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test diff --git a/chef/cookbooks/erlang/attributes/default.rb b/chef/cookbooks/erlang/attributes/default.rb index 26ff0af..4ce8771 100644 --- a/chef/cookbooks/erlang/attributes/default.rb +++ b/chef/cookbooks/erlang/attributes/default.rb @@ -16,11 +16,11 @@ # default['erlang']['gui_tools'] = false -default['erlang']['install_method'] = "package" +default['erlang']['install_method'] = 'package' -default['erlang']['source']['version'] = "R15B01" +default['erlang']['source']['version'] = 'R15B01' default['erlang']['source']['url'] = "http://erlang.org/download/otp_src_#{node['erlang']['source']['version']}.tar.gz" -default['erlang']['source']['checksum'] = "f94f7de7328af3c0cdc42089c1a4ecd03bf98ec680f47eb5e6cddc50261cabde" +default['erlang']['source']['checksum'] = 'f94f7de7328af3c0cdc42089c1a4ecd03bf98ec680f47eb5e6cddc50261cabde' default['erlang']['esl']['version'] = nil default['erlang']['esl']['lsb_codename'] = node['lsb']['codename'] diff --git a/chef/cookbooks/erlang/chefignore b/chef/cookbooks/erlang/chefignore deleted file mode 100644 index a6de142..0000000 --- a/chef/cookbooks/erlang/chefignore +++ /dev/null @@ -1,96 +0,0 @@ -# Put files/directories that should be ignored in this file when uploading -# or sharing to the community site. -# Lines that start with '# ' are comments. - -# OS generated files # -###################### -.DS_Store -Icon? -nohup.out -ehthumbs.db -Thumbs.db - -# SASS # -######## -.sass-cache - -# EDITORS # -########### -\#* -.#* -*~ -*.sw[a-z] -*.bak -REVISION -TAGS* -tmtags -*_flymake.* -*_flymake -*.tmproj -.project -.settings -mkmf.log - -## COMPILED ## -############## -a.out -*.o -*.pyc -*.so -*.com -*.class -*.dll -*.exe -*/rdoc/ - -# Testing # -########### -.watchr -.rspec -spec/* -spec/fixtures/* -test/* -features/* -Guardfile -Procfile - -# SCM # -####### -.git -*/.git -.gitignore -.gitmodules -.gitconfig -.gitattributes -.svn -*/.bzr/* -*/.hg/* -*/.svn/* - -# Berkshelf # -############# -Berksfile -Berksfile.lock -cookbooks/* -tmp - -# Cookbooks # -############# -CONTRIBUTING -CHANGELOG* - -# Strainer # -############ -Colanderfile -Strainerfile -.colander -.strainer - -# Vagrant # -########### -.vagrant -Vagrantfile - -# Travis # -########## -.travis.yml diff --git a/chef/cookbooks/erlang/files/default/tests/minitest/default_test.rb b/chef/cookbooks/erlang/files/default/tests/minitest/default_test.rb deleted file mode 100644 index 816d43a..0000000 --- a/chef/cookbooks/erlang/files/default/tests/minitest/default_test.rb +++ /dev/null @@ -1,39 +0,0 @@ -# -# Cookbook:: erlang -# Minitest Chef Handler -# -# Author:: Joshua Timberman -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe 'erlang::default' do - include Helpers::Erlang - - it 'doesnt install the gui_tools if the attribute is false (default)' do - skip unless node['platform_family'] == 'debian' - skip if node['erlang']['gui_tools'] - package("erlang-gs").wont_be_installed - end - - it 'can process erlang code with the erl command ' do - erl = shell_out("erl -myflag 1 <<-EOH -init:get_argument(myflag). -EOH -") - assert_includes(erl.stdout,'{ok,[["1"]]}') - end -end diff --git a/chef/cookbooks/erlang/files/default/tests/minitest/esl_test.rb b/chef/cookbooks/erlang/files/default/tests/minitest/esl_test.rb deleted file mode 100644 index c9ba034..0000000 --- a/chef/cookbooks/erlang/files/default/tests/minitest/esl_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Cookbook:: erlang_test -# Minitest Chef Handler -# -# Copyright:: Copyright (c) 2013, Opscode, Inc. -# -# 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. -# -require File.expand_path('../support/helpers', __FILE__) - -describe_recipe 'erlang::erlang_solutions' do - include Helpers::Erlang - - it 'installs the esl-erlang package' do - package("esl-erlang").must_be_installed - end - - it "can successfully run 'erl'" do - erl = shell_out("erl -myflag 1 <<-EOH -init:get_argument(myflag). -EOH -") - assert_includes(erl.stdout,'{ok,[["1"]]}') - end -end diff --git a/chef/cookbooks/erlang/files/default/tests/minitest/gui_tools_test.rb b/chef/cookbooks/erlang/files/default/tests/minitest/gui_tools_test.rb deleted file mode 100644 index 3d7455f..0000000 --- a/chef/cookbooks/erlang/files/default/tests/minitest/gui_tools_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -# -# Cookbook:: erlang -# Minitest Chef Handler -# -# Author:: Joshua Timberman -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe 'erlang::default' do - include Helpers::Erlang - - it 'installs the x11 package if gui_tools is true' do - skip unless node['platform_family'] == 'debian' - package("erlang-gs").must_be_installed - end -end diff --git a/chef/cookbooks/erlang/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/erlang/files/default/tests/minitest/support/helpers.rb deleted file mode 100644 index f0b32e4..0000000 --- a/chef/cookbooks/erlang/files/default/tests/minitest/support/helpers.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -# Cookbook:: erlang -# -# Author:: Joshua Timberman -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# -# 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. -# - -module Helpers - module Erlang - require 'chef/mixin/shell_out' - include Chef::Mixin::ShellOut - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - end -end diff --git a/chef/cookbooks/erlang/metadata.json b/chef/cookbooks/erlang/metadata.json new file mode 100644 index 0000000..f462840 --- /dev/null +++ b/chef/cookbooks/erlang/metadata.json @@ -0,0 +1,45 @@ +{ + "name": "erlang", + "version": "1.4.2", + "description": "Installs erlang, optionally install GUI tools.", + "long_description": "", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "ubuntu": ">= 0.0.0", + "debian": ">= 0.0.0", + "redhat": ">= 0.0.0", + "centos": ">= 0.0.0", + "scientific": ">= 0.0.0", + "amazon": ">= 0.0.0", + "oracle": ">= 0.0.0" + }, + "dependencies": { + "apt": ">= 1.7.0", + "yum": "~> 3.0", + "yum-epel": ">= 0.0.0", + "yum-erlang_solutions": ">= 0.0.0", + "build-essential": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "erlang": "Installs Erlang via native package, source, or Erlang Solutions package", + "erlang::package": "Installs Erlang via native package", + "erlang::source": "Installs Erlang via source", + "erlang::esl": "Installs Erlang from Erlang Solutions' package repositories" + } +} \ No newline at end of file diff --git a/chef/cookbooks/erlang/metadata.rb b/chef/cookbooks/erlang/metadata.rb index cce048e..1f3d4a9 100644 --- a/chef/cookbooks/erlang/metadata.rb +++ b/chef/cookbooks/erlang/metadata.rb @@ -1,19 +1,21 @@ -name "erlang" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Installs erlang, optionally install GUI tools." -version "1.3.3" +name 'erlang' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Installs erlang, optionally install GUI tools.' +version '1.4.2' -depends "apt", ">= 1.7.0" -depends "yum", ">= 0.5.0" -depends "build-essential" +depends 'apt', '>= 1.7.0' +depends 'yum', '~> 3.0' +depends 'yum-epel' +depends 'yum-erlang_solutions' +depends 'build-essential' -recipe "erlang", "Installs Erlang via native package, source, or Erlang Solutions package" -recipe "erlang::package", "Installs Erlang via native package" -recipe "erlang::source", "Installs Erlang via source" -recipe "erlang::esl", "Installs Erlang from Erlang Solutions' package repositories" +recipe 'erlang', 'Installs Erlang via native package, source, or Erlang Solutions package' +recipe 'erlang::package', 'Installs Erlang via native package' +recipe 'erlang::source', 'Installs Erlang via source' +recipe 'erlang::esl', "Installs Erlang from Erlang Solutions' package repositories" -%w{ ubuntu debian redhat centos fedora scientific amazon oracle }.each do |os| +%w{ ubuntu debian redhat centos scientific amazon oracle }.each do |os| supports os end diff --git a/chef/cookbooks/erlang/recipes/esl.rb b/chef/cookbooks/erlang/recipes/esl.rb index c14c6b7..6b34b3c 100644 --- a/chef/cookbooks/erlang/recipes/esl.rb +++ b/chef/cookbooks/erlang/recipes/esl.rb @@ -33,57 +33,31 @@ when 'debian' action :add end -when 'rhel' - case node['platform'] - when 'centos', 'fedora' - - if platform?('centos') && node['platform_version'].to_i == 5 - Chef::Log.fatal("Erlang Solutions pacakge repositories are not available for Centos 5") - raise - else - - include_recipe 'yum' - - if platform?('centos') - include_recipe "yum::repoforge" - end - - yum_key "RPM-KEY-Erlang-Solutions" do - # Yes, yes, I know the URL has 'debian' in it... that's the address - url "http://binaries.erlang-solutions.com/debian/erlang_solutions.asc" - action :add - end - - # This replicates the files found at - # http://binaries.erlang-solutions.com/rpm/fedora/erlang_solutions.repo - # http://binaries.erlang-solutions.com/rpm/centos/erlang_solutions.repo - yum_repository "erlang-solutions" do - description "#{node['platform']} $releasever - $basearch - Erlang Solutions" - url "http://binaries.erlang-solutions.com/rpm/#{node['platform']}/$releasever/$basearch" - key "RPM-KEY-Erlang-Solutions" - enabled 1 - end - end - else - Chef::Log.fatal("Erlang Solutions pacakge repositories are currently not supported for RHEL family #{node['platform']} systems") - raise + package 'esl-erlang' do + version node['erlang']['esl']['version'] if node['erlang']['esl']['version'] + end + +when 'rhel' + if node['platform_version'].to_i <= 5 + Chef::Log.fatal('Erlang Solutions pacakge repositories are not available for EL5') + else + # include_recipe 'yum-repoforge' + include_recipe 'yum-erlang_solutions' + end + + package 'erlang' do + version node['erlang']['esl']['version'] if node['erlang']['esl']['version'] end -else - Chef::Log.fatal("Erlang Solutions pacakge repositories are currently not supported for #{node['platform_family']} systems") - raise -end -package "esl-erlang" do - version node['erlang']['esl']['version'] if node['erlang']['esl']['version'] end # There's a small bug in the package for Ubuntu 10.04... this fixes # it. Solution found at # https://github.com/davidcoallier/bigcouch/blob/f6a6daf7590ecbab4d9dc4747624573b3137dfad/README.md#ubuntu-1004-lts-potential-issues -if platform?("ubuntu") && node['platform_version'] == "10.04" - bash "ubuntu-10.04-LTS-erlang-fix" do - user "root" - cwd "/usr/lib/erlang/man/man5" +if platform?('ubuntu') && node['platform_version'] == '10.04' + bash 'ubuntu-10.04-LTS-erlang-fix' do + user 'root' + cwd '/usr/lib/erlang/man/man5' code <<-EOS rm modprobe.d.5 ln -s modprobe.conf.5.gz modprobe.d.5 diff --git a/chef/cookbooks/erlang/recipes/package.rb b/chef/cookbooks/erlang/recipes/package.rb index 50ec677..19f9fce 100644 --- a/chef/cookbooks/erlang/recipes/package.rb +++ b/chef/cookbooks/erlang/recipes/package.rb @@ -1,3 +1,4 @@ +# # Cookbook Name:: erlang # Recipe:: default # Author:: Joe Williams @@ -21,29 +22,26 @@ # case node['platform_family'] -when "debian" - - erlpkg = node['erlang']['gui_tools'] ? "erlang-x11" : "erlang-nox" - +when 'debian' + erlpkg = node['erlang']['gui_tools'] ? 'erlang-x11' : 'erlang-nox' package erlpkg - package "erlang-dev" + package 'erlang-dev' -when "rhel" +when 'rhel' + case node['platform_version'].to_i + when 5 + include_recipe 'yum-epel' - include_recipe "yum::epel" + yum_repository 'EPELErlangrepo' do + description "Updated erlang yum repository for RedHat / Centos 5.x - #{node['kernel']['machine']}" + baseurl 'http://repos.fedorapeople.org/repos/peter/erlang/epel-5Server/$basearch' + gpgcheck false + action :create + end - yum_repository "erlang" do - name "EPELErlangrepo" - url "http://repos.fedorapeople.org/repos/peter/erlang/epel-5Server/$basearch" - description "Updated erlang yum repository for RedHat / Centos 5.x - #{node['kernel']['machine']}" - action :add - only_if { node['platform_version'].to_f >= 5.0 && node['platform_version'].to_f < 6.0 } + else + include_recipe 'yum-erlang_solutions' end - package "erlang" - -else - - package "erlang" - + package 'erlang' end diff --git a/chef/cookbooks/erlang/recipes/source.rb b/chef/cookbooks/erlang/recipes/source.rb index 37b92a6..6444f22 100644 --- a/chef/cookbooks/erlang/recipes/source.rb +++ b/chef/cookbooks/erlang/recipes/source.rb @@ -20,15 +20,15 @@ # limitations under the License. # -include_recipe "build-essential" +include_recipe 'build-essential' -erlang_deps = case node["platform_family"] - when "debian" - [ "libncurses5-dev", "openssl", "libssl-dev" ] - when "rhel", "fedora" - [ "ncurses-devel", "openssl-devel" ] +erlang_deps = case node['platform_family'] + when 'debian' + %w{ libncurses5-dev openssl libssl-dev } + when 'rhel', 'fedora' + %w{ ncurses-devel openssl-devel } else - [ ] + [] end erlang_deps.each do |pkg| @@ -37,7 +37,7 @@ erlang_deps.each do |pkg| end end -bash "install-erlang" do +bash 'install-erlang' do cwd Chef::Config[:file_cache_path] code <<-EOH tar -xzf otp_src_#{node['erlang']['source']['version']}.tar.gz @@ -49,8 +49,8 @@ end remote_file File.join(Chef::Config[:file_cache_path], "otp_src_#{node['erlang']['source']['version']}.tar.gz") do source node['erlang']['source']['url'] - owner "root" + owner 'root' mode 0644 checksum node['erlang']['source']['checksum'] - notifies :run, "bash[install-erlang]", :immediately + notifies :run, 'bash[install-erlang]', :immediately end diff --git a/chef/cookbooks/git/.kitchen.yml b/chef/cookbooks/git/.kitchen.yml deleted file mode 100644 index 2240f65..0000000 --- a/chef/cookbooks/git/.kitchen.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: - - recipe[apt] - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - -suites: -- name: default - run_list: - - recipe[git] - attributes: {} - -- name: source - excludes: ["ubuntu-12.04", "ubuntu-10.04"] - run_list: - - recipe[git::source] - attributes: {} - -- name: server - run_list: - - recipe[git::server] - attributes: {} diff --git a/chef/cookbooks/git/Berksfile b/chef/cookbooks/git/Berksfile deleted file mode 100644 index 34a3b2d..0000000 --- a/chef/cookbooks/git/Berksfile +++ /dev/null @@ -1,8 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook "apt" - cookbook "yum" -end diff --git a/chef/cookbooks/git/CHANGELOG.md b/chef/cookbooks/git/CHANGELOG.md index 366065b..fc8ab32 100644 --- a/chef/cookbooks/git/CHANGELOG.md +++ b/chef/cookbooks/git/CHANGELOG.md @@ -3,6 +3,66 @@ git Cookbook CHANGELOG This file is used to list changes made in each version of the git cookbook. +v4.0.2 (2014-04-23) +------------------- +- [COOK-4482] - Add FreeBSD support for installing git client + + +v4.0.0 (2014-03-18) +------------------- +- [COOK-4397] Only use_inline_resources on Chef 11 + + +v3.1.0 (2014-03-12) +------------------- +- [COOK-4392] - Cleanup git_config LWRP + + +v3.0.0 (2014-02-28) +------------------- +[COOK-4387] Add git_config type +[COOK-4388] Fix up rubocops +[COOK-4390] Add integration tests for default and server suites + + +v2.10.0 (2014-02-25) +-------------------- +- [COOK-4146] - wrong dependency in git::source for rhel 6 +- [COOK-3947] - Git cookbook adds itself to the path every run + + +v2.9.0 +------ +Updating to depend on cookbook yum ~> 3 +Fixing style to pass rubocop +Updating test scaffolding + + +v2.8.4 +------ +fixing metadata version error. locking to 3.0 + + +v2.8.1 +------ +Locking yum dependency to '< 3' + + +v2.8.0 +------ +### Bug +- [COOK-3433] - git::server does not correctly set git-daemon's base-path on Debian + + +v2.7.0 +------ +### Bug +- **[COOK-3624](https://tickets.opscode.com/browse/COOK-3624)** - Don't restart `xinetd` on each Chef client run +- **[COOK-3482](https://tickets.opscode.com/browse/COOK-3482)** - Force git to add itself to the current process' PATH + +### New Feature +- **[COOK-3223](https://tickets.opscode.com/browse/COOK-3223)** - Support Omnios and SmartOS package installs + v2.6.0 ------ ### Improvement diff --git a/chef/cookbooks/git/CONTRIBUTING b/chef/cookbooks/git/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/git/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/git/Gemfile b/chef/cookbooks/git/Gemfile deleted file mode 100644 index 46e0766..0000000 --- a/chef/cookbooks/git/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source :rubygems - -gem 'test-kitchen', '< 1.0' diff --git a/chef/cookbooks/git/LICENSE b/chef/cookbooks/git/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/git/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/git/README.md b/chef/cookbooks/git/README.md index 1725052..85a9295 100644 --- a/chef/cookbooks/git/README.md +++ b/chef/cookbooks/git/README.md @@ -20,6 +20,7 @@ The following platform families are supported: * Arch * RHEL * Fedora +* FreeBSD (client only) * Mac OS X (10.6.0+) * Windows @@ -100,7 +101,7 @@ License and Author ================== - Author:: Joshua Timberman () -- Copyright:: 2009-2012, Opscode, Inc. +- Copyright:: 2009-2014, Chef Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/git/TESTING.md b/chef/cookbooks/git/TESTING.md deleted file mode 100644 index e29ff7c..0000000 --- a/chef/cookbooks/git/TESTING.md +++ /dev/null @@ -1,25 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test diff --git a/chef/cookbooks/git/attributes/default.rb b/chef/cookbooks/git/attributes/default.rb index 1830284..41d3474 100644 --- a/chef/cookbooks/git/attributes/default.rb +++ b/chef/cookbooks/git/attributes/default.rb @@ -3,38 +3,38 @@ # Cookbook Name:: git # Attributes:: default # -# Copyright 2008-2012, Opscode, Inc. +# Copyright 2008-2014, Chef Software, Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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. case node['platform_family'] when 'windows' - default['git']['version'] = "1.8.1.2-preview20130201" + default['git']['version'] = '1.8.1.2-preview20130201' default['git']['url'] = "https://msysgit.googlecode.com/files/Git-#{node['git']['version']}.exe" - default['git']['checksum'] = "796ac91f0c7456b53f2717a81f475075cc581af2f447573131013cac5b63bb2a" + default['git']['checksum'] = '796ac91f0c7456b53f2717a81f475075cc581af2f447573131013cac5b63bb2a' default['git']['display_name'] = "Git version #{ node['git']['version'] }" -when "mac_os_x" - default['git']['osx_dmg']['app_name'] = "git-1.8.2-intel-universal-snow-leopard" - default['git']['osx_dmg']['volumes_dir'] = "Git 1.8.2 Snow Leopard Intel Universal" - default['git']['osx_dmg']['package_id'] = "GitOSX.Installer.git182.git.pkg" - default['git']['osx_dmg']['url'] = "https://git-osx-installer.googlecode.com/files/git-1.8.2-intel-universal-snow-leopard.dmg" - default['git']['osx_dmg']['checksum'] = "e1d0ec7a9d9d03b9e61f93652b63505137f31217908635cdf2f350d07cb33e15" +when 'mac_os_x' + default['git']['osx_dmg']['app_name'] = 'git-1.8.2-intel-universal-snow-leopard' + default['git']['osx_dmg']['volumes_dir'] = 'Git 1.8.2 Snow Leopard Intel Universal' + default['git']['osx_dmg']['package_id'] = 'GitOSX.Installer.git182.git.pkg' + default['git']['osx_dmg']['url'] = 'https://git-osx-installer.googlecode.com/files/git-1.8.2-intel-universal-snow-leopard.dmg' + default['git']['osx_dmg']['checksum'] = 'e1d0ec7a9d9d03b9e61f93652b63505137f31217908635cdf2f350d07cb33e15' else - default['git']['prefix'] = "/usr/local" - default['git']['version'] = "1.8.2.1" + default['git']['prefix'] = '/usr/local' + default['git']['version'] = '1.8.2.1' default['git']['url'] = "https://nodeload.github.com/git/git/tar.gz/v#{node['git']['version']}" - default['git']['checksum'] = "bdc1768f70ce3d8f3e4edcdcd99b2f85a7f8733fb684398aebe58dde3e6bcca2" + default['git']['checksum'] = 'bdc1768f70ce3d8f3e4edcdcd99b2f85a7f8733fb684398aebe58dde3e6bcca2' end -default['git']['server']['base_path'] = "/srv/git" -default['git']['server']['export_all'] = "true" +default['git']['server']['base_path'] = '/srv/git' +default['git']['server']['export_all'] = 'true' diff --git a/chef/cookbooks/git/metadata.json b/chef/cookbooks/git/metadata.json new file mode 100644 index 0000000..17d86ad --- /dev/null +++ b/chef/cookbooks/git/metadata.json @@ -0,0 +1,69 @@ +{ + "name": "git", + "version": "4.0.2", + "description": "Installs git and/or sets up a Git server daemon", + "long_description": "Description\n===========\n\nInstalls git and optionally sets up a git server as a daemon under runit.\n\nRequirements\n============\n## Ohai and Chef:\n\n* Ohai: 6.14.0+\n\nThis cookbook makes use of `node['platform_family']` to simplify platform\nselection logic. This attribute was introduced in Ohai v0.6.12.\n\n## Platform:\n\nThe following platform families are supported:\n\n* Debian\n* Arch\n* RHEL\n* Fedora\n* FreeBSD (client only)\n* Mac OS X (10.6.0+)\n* Windows\n\n## Cookbooks:\n\n* runit (for `git::server`)\n* build-essential (for `git::source`)\n* dmg (for OS X installation)\n* yum (for RHEL 5 installation)\n\n### Windows Dependencies\nThe [`windows_package`](https://github.com/opscode-cookbooks/windows#windows_package) resource from the Windows cookbook is required to\ninstall the git package on Windows.\n\n## Attributes\n\n### default\nThe following attributes are platform-specific.\n\n#### Windows\n\n* `node['git']['version']` - git version to install\n* `node['git']['url']` - URL to git package\n* `node['git']['checksum']` - package SHA256 checksum\n* `node['git']['display_name']` - `windows_package` resource Display Name (makes the package install idempotent)\n\n#### Mac OS X\n\n* `node['git']['osx_dmg']['url']` - URL to git package\n* `node['git']['osx_dmg']['checksum']` - package SHA256 checksum\n\n#### Linux\n\n* `node['git']['prefix']` - git install directory\n* `node['git']['version']` - git version to install\n* `node['git']['url']` - URL to git tarball\n* `node['git']['checksum']` - tarball SHA256 checksum\n\nRecipes\n=======\n\n## default\n\nInstalls base git packages based on platform.\n\n## server\n\nSets up a git daemon to provide a server.\n\n## source\n\nInstalls git from source.\n\n## windows\n\nInstalls git client on Windows\n\nUsage\n=====\n\n\nThis cookbook primarily installs git core packages. It can also be\nused to serve git repositories.\n\nTo install git client (all supported platforms):\n\n include_recipe 'git'\n\nTo install git server:\n\n include_recipe \"git::server\"\n\nThis creates the directory specified by git/server/base_path (default is /srv/git)\nand starts a git daemon, exporting all repositories found. Repositories need to be\nadded manually, but will be available once they are created.\n\nLicense and Author\n==================\n\n- Author:: Joshua Timberman ()\n- Copyright:: 2009-2014, Chef Software, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "maintainer": "Chef Software, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "amazon": ">= 0.0.0", + "arch": ">= 0.0.0", + "centos": ">= 0.0.0", + "debian": ">= 0.0.0", + "fedora": ">= 0.0.0", + "redhat": ">= 0.0.0", + "scientific": ">= 0.0.0", + "oracle": ">= 0.0.0", + "ubuntu": ">= 0.0.0", + "windows": ">= 0.0.0", + "mac_os_x": ">= 10.6.0" + }, + "dependencies": { + "dmg": ">= 0.0.0", + "build-essential": ">= 0.0.0", + "windows": ">= 0.0.0", + "runit": ">= 1.0.0", + "yum": "~> 3.0", + "yum-epel": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + "git/server/base_path": { + "display_name": "Git Daemon Base Path", + "description": "A directory containing git repositories to be exposed by the git-daemon", + "default": "/srv/git", + "recipes": [ + "git::server" + ] + }, + "git/server/export_all": { + "display_name": "Git Daemon Export All", + "description": "Adds the --export-all option to the git-daemon parameters, making all repositories publicly readable even if they lack the 'git-daemon-export-ok' file", + "choice": [ + "true", + "false" + ], + "default": "true", + "recipes": [ + "git::server" + ] + } + }, + "groupings": { + }, + "recipes": { + "git": "Installs git", + "git::server": "Sets up a runit_service for git daemon", + "git::source": "Installs git from source" + } +} \ No newline at end of file diff --git a/chef/cookbooks/git/metadata.rb b/chef/cookbooks/git/metadata.rb index 83a9ce8..ae8559c 100644 --- a/chef/cookbooks/git/metadata.rb +++ b/chef/cookbooks/git/metadata.rb @@ -1,35 +1,37 @@ -name "git" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Installs git and/or sets up a Git server daemon" +name 'git' +maintainer 'Chef Software, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Installs git and/or sets up a Git server daemon' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "2.6.1" -recipe "git", "Installs git" -recipe "git::server", "Sets up a runit_service for git daemon" -recipe "git::source", "Installs git from source" +version '4.0.2' +recipe 'git', 'Installs git' +recipe 'git::server', 'Sets up a runit_service for git daemon' +recipe 'git::source', 'Installs git from source' %w{ amazon arch centos debian fedora redhat scientific oracle amazon ubuntu windows }.each do |os| supports os end -supports "mac_os_x", ">= 10.6.0" +supports 'mac_os_x', '>= 10.6.0' -%w{ dmg build-essential yum windows }.each do |cookbook| +%w{ dmg build-essential windows }.each do |cookbook| depends cookbook end -depends "runit", ">= 1.0" +depends 'runit', '>= 1.0' +depends 'yum', '~> 3.0' +depends 'yum-epel' -attribute "git/server/base_path", - :display_name => "Git Daemon Base Path", - :description => "A directory containing git repositories to be exposed by the git-daemon", - :default => "/srv/git", - :recipes => ["git::server"] +attribute 'git/server/base_path', + :display_name => 'Git Daemon Base Path', + :description => 'A directory containing git repositories to be exposed by the git-daemon', + :default => '/srv/git', + :recipes => ['git::server'] -attribute "git/server/export_all", - :display_name => "Git Daemon Export All", - :description => "Adds the --export-all option to the git-daemon parameters, making all repositories publicly readable even if they lack the \"git-daemon-export-ok\" file", - :choice => ["true", "false"], - :default => "true", - :recipes => ["git::server"] +attribute 'git/server/export_all', + :display_name => 'Git Daemon Export All', + :description => 'Adds the --export-all option to the git-daemon parameters, making all repositories publicly readable even if they lack the \'git-daemon-export-ok\' file', + :choice => %w{ true false }, + :default => 'true', + :recipes => ['git::server'] diff --git a/chef/cookbooks/git/providers/config.rb b/chef/cookbooks/git/providers/config.rb new file mode 100644 index 0000000..78cbc4a --- /dev/null +++ b/chef/cookbooks/git/providers/config.rb @@ -0,0 +1,45 @@ +use_inline_resources if defined?(use_inline_resources) + +def whyrun_supported? + true +end + +action :set do + if @current_resource.exists + Chef::Log.info "#{ @new_resource } already exists - nothing to do." + else + execute "#{config_cmd} #{new_resource.key} \"#{new_resource.value}\"" do + cwd new_resource.path + user new_resource.user + group new_resource.user + environment cmd_env + Chef::Log.info "#{ @new_resource } created." + end + end +end + +def initialize(*args) + super + + @run_context.include_recipe 'git' +end + +def load_current_resource + @current_resource = Chef::Resource::GitConfig.new(@new_resource.name) + @current_resource.exists = true if config == new_resource.value +end + +def config_cmd + "git config --#{new_resource.scope}" +end + +def cmd_env + new_resource.user ? { 'USER' => new_resource.user, 'HOME' => ::Dir.home(new_resource.user) } : nil +end + +def config + cmd = [config_cmd, new_resource.key].join(' ') + git_config = Mixlib::ShellOut.new(cmd, :user => new_resource.user, :group => new_resource.user, :cwd => new_resource.path, :env => cmd_env) + Chef::Log.debug("Current config cmd: #{git_config.inspect}") + git_config.run_command.stdout.chomp +end diff --git a/chef/cookbooks/git/recipes/default.rb b/chef/cookbooks/git/recipes/default.rb index 933face..296ef17 100644 --- a/chef/cookbooks/git/recipes/default.rb +++ b/chef/cookbooks/git/recipes/default.rb @@ -2,7 +2,7 @@ # Cookbook Name:: git # Recipe:: default # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,30 +17,37 @@ # limitations under the License. case node['platform_family'] -when "debian" - if node['platform'] == "ubuntu" && node['platform_version'].to_f < 10.10 - package "git-core" +when 'debian' + if node['platform'] == 'ubuntu' && node['platform_version'].to_f < 10.10 + package 'git-core' else - package "git" + package 'git' end -when "rhel","fedora" +when 'rhel', 'fedora' case node['platform_version'].to_i when 5 - include_recipe "yum::epel" + include_recipe 'yum-epel' end - package "git" -when "windows" + package 'git' +when 'windows' include_recipe 'git::windows' -when "mac_os_x" - dmg_package "GitOSX-Installer" do +when 'mac_os_x' + dmg_package 'GitOSX-Installer' do app node['git']['osx_dmg']['app_name'] package_id node['git']['osx_dmg']['package_id'] volumes_dir node['git']['osx_dmg']['volumes_dir'] source node['git']['osx_dmg']['url'] checksum node['git']['osx_dmg']['checksum'] - type "pkg" + type 'pkg' action :install end else - package "git" + package 'git' do + package_name case node['platform'] + when 'omnios' + 'developer/versioning/git' + when 'smartos' + 'scmgit' + end + end end diff --git a/chef/cookbooks/git/recipes/server.rb b/chef/cookbooks/git/recipes/server.rb index b67461c..a465f5b 100644 --- a/chef/cookbooks/git/recipes/server.rb +++ b/chef/cookbooks/git/recipes/server.rb @@ -2,7 +2,7 @@ # Cookbook Name:: git # Recipe:: server # -# Copyright 2009, Opscode, Inc. +# Copyright 2009-2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,42 +16,41 @@ # See the License for the specific language governing permissions and # limitations under the License. -if node["platform"] == "windows" - return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" -end +return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" if node['platform'] == 'windows' -include_recipe "git" +include_recipe 'git' -directory node["git"]["server"]["base_path"] do - owner "root" - group "root" - mode 00755 +directory node['git']['server']['base_path'] do + owner 'root' + group 'root' + mode '0755' end case node['platform_family'] -when "debian" - include_recipe "runit" - - package "git-daemon-run" - - runit_service "git-daemon" do - sv_templates false - end -when "rhel" - package "git-daemon" - - template "/etc/xinetd.d/git" do - backup false - source "git-xinetd.d.erb" - owner "root" - group "root" - mode 00644 - end - - service "xinetd" do - action [:enable, :restart] - end +when 'debian' + package 'xinetd' +when 'rhel' + package 'git-daemon' else - log "Platform requires setting up a git daemon service script." - log "Hint: /usr/bin/git daemon --export-all --user=nobody --group=daemon --base-path=#{node["git"]["server"]["base_path"]}" + log 'Platform requires setting up a git daemon service script.' + log "Hint: /usr/bin/git daemon --export-all --user=nobody --group=daemon --base-path=#{node['git']['server']['base_path']}" + return +end + +template '/etc/xinetd.d/git' do + backup false + source 'git-xinetd.d.erb' + owner 'root' + group 'root' + mode '0644' + variables( + :git_daemon_binary => value_for_platform_family( + 'debian' => '/usr/lib/git-core/git-daemon', + 'rhel' => '/usr/libexec/git-core/git-daemon' + ) + ) +end + +service 'xinetd' do + action [:enable, :restart] end diff --git a/chef/cookbooks/git/recipes/source.rb b/chef/cookbooks/git/recipes/source.rb index ec2b8c2..66dbf7e 100644 --- a/chef/cookbooks/git/recipes/source.rb +++ b/chef/cookbooks/git/recipes/source.rb @@ -16,28 +16,39 @@ # See the License for the specific language governing permissions and # limitations under the License. -if node["platform"] == "windows" - return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" +return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" if node['platform'] == 'windows' + +include_recipe 'build-essential' +include_recipe 'yum-epel' if node['platform_family'] == 'rhel' && node['platform_version'].to_i < 6 + +# move this to attributes. +case node['platform_family'] +when 'rhel' + case node['platform_version'].to_i + when 5 + pkgs = %w{ expat-devel gettext-devel curl-devel openssl-devel zlib-devel } + when 6 + pkgs = %w{ expat-devel gettext-devel libcurl-devel openssl-devel perl-ExtUtils-MakeMaker zlib-devel } + else + pkgs = %w{ expat-devel gettext-devel curl-devel openssl-devel perl-ExtUtils-MakeMaker zlib-devel } if node['platform'] == 'amazon' + end +when 'debian' + pkgs = %w{ libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev } end -include_recipe "build-essential" - -pkgs = value_for_platform_family( - ["rhel"] => %w{ expat-devel gettext-devel libcurl-devel openssl-devel perl-ExtUtils-MakeMaker zlib-devel }, - ["debian"] => %w{ libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev } -) - pkgs.each do |pkg| package pkg end +# reduce line-noise-eyness remote_file "#{Chef::Config['file_cache_path']}/git-#{node['git']['version']}.tar.gz" do source node['git']['url'] checksum node['git']['checksum'] - mode 00644 + mode '0644' not_if "test -f #{Chef::Config['file_cache_path']}/git-#{node['git']['version']}.tar.gz" end +# reduce line-noise-eyness execute "Extracting and Building Git #{node['git']['version']} from Source" do cwd Chef::Config['file_cache_path'] command <<-COMMAND diff --git a/chef/cookbooks/git/recipes/windows.rb b/chef/cookbooks/git/recipes/windows.rb index 957b94b..97b27d9 100644 --- a/chef/cookbooks/git/recipes/windows.rb +++ b/chef/cookbooks/git/recipes/windows.rb @@ -2,7 +2,7 @@ # Cookbook Name:: git # Recipe:: windows # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,7 +26,19 @@ end # Git is installed to Program Files (x86) on 64-bit machines and # 'Program Files' on 32-bit machines PROGRAM_FILES = ENV['ProgramFiles(x86)'] || ENV['ProgramFiles'] +GIT_PATH = "#{ PROGRAM_FILES }\\Git\\Cmd" -windows_path "#{ PROGRAM_FILES }\\Git\\Cmd" do - action :add +# COOK-3482 - windows_path resource doesn't change the current process +# environment variables. Therefore, git won't actually be on the PATH +# until the next chef-client run +ruby_block 'Add Git Path' do + block do + ENV['PATH'] += ";#{GIT_PATH}" + end + action :nothing +end + +windows_path GIT_PATH do + action :add + notifies :create, 'ruby_block[Add Git Path]', :immediately end diff --git a/chef/cookbooks/git/resources/config.rb b/chef/cookbooks/git/resources/config.rb new file mode 100644 index 0000000..95984ce --- /dev/null +++ b/chef/cookbooks/git/resources/config.rb @@ -0,0 +1,11 @@ +actions :set +default_action :set + +attribute :key, :kind_of => String, :name_attribute => true +attribute :value, :kind_of => String, :required => true +attribute :scope, :equal_to => %w(local global system), :default => 'global' +attribute :path, :kind_of => String +attribute :user, :kind_of => String +attribute :options, :kind_of => String + +attr_accessor :exists diff --git a/chef/cookbooks/git/templates/default/git-xinetd.d.erb b/chef/cookbooks/git/templates/default/git-xinetd.d.erb index e3cf2e6..ff48ffd 100644 --- a/chef/cookbooks/git/templates/default/git-xinetd.d.erb +++ b/chef/cookbooks/git/templates/default/git-xinetd.d.erb @@ -4,7 +4,7 @@ service git socket_type = stream wait = no user = nobody - server = /usr/libexec/git-core/git-daemon + server = <%= @git_daemon_binary %> server_args = --base-path=<%= node["git"]["server"]["base_path"] %> <% if node["git"]["server"]["export_all"] == "true" %>--export-all <% end %>--syslog --inetd --verbose log_on_failure += USERID } diff --git a/chef/cookbooks/haproxy/attributes/default.rb b/chef/cookbooks/haproxy/attributes/default.rb index 7230e0b..1272be3 100644 --- a/chef/cookbooks/haproxy/attributes/default.rb +++ b/chef/cookbooks/haproxy/attributes/default.rb @@ -18,11 +18,13 @@ # # node['haproxy']['backend'] to deside where service backend sources come from -# if 'prefeed', all services' backend info will be choosen from databag +# if 'prefeed', all services' backend info will be choosen from attribute # 'node_mapping'; 'prefeed' is suitable for stable and independent services # if 'autofeed', services' backend info will automaticly learn backend info -# from it's chef server. -default['haproxy']['choose_backend'] = 'prefeed' +# from its chef server. +default['haproxy']['log']['facilities'] = 'local4' +default['haproxy']['log']['file'] = '/var/log/haproxy.log' +default['haproxy']['choose_backend'] = 'autofeed' default['haproxy']['enable_default_http'] = true default['haproxy']['incoming_address'] = "0.0.0.0" default['haproxy']['incoming_port'] = 80 @@ -31,7 +33,8 @@ default['haproxy']['members'] = [{ "ipaddress" => "127.0.0.1", "port" => 4000, "ssl_port" => 4000 -}, { +}, +{ "hostname" => "localhost", "ipaddress" => "127.0.0.1", "port" => 4001, @@ -56,13 +59,15 @@ default['haproxy']['stats_socket_user'] = node['haproxy']['user'] default['haproxy']['stats_socket_group'] = node['haproxy']['group'] default['haproxy']['pid_file'] = "/var/run/haproxy.pid" -default['haproxy']['defaults_options'] = ["tcpka", "httpchk", "tcplog", "httplog"] +default['haproxy']['defaults_options'] = ["tcpka", "httpchk", "tcplog", "httplog", "forceclose", "redispatch"] default['haproxy']['x_forwarded_for'] = false -default['haproxy']['defaults_timeouts']['connect'] = "10s" -default['haproxy']['defaults_timeouts']['check'] = "10s" -default['haproxy']['defaults_timeouts']['queue'] = "100s" -default['haproxy']['defaults_timeouts']['client'] = "100s" -default['haproxy']['defaults_timeouts']['server'] = "100s" +default['haproxy']['defaults_timeouts']['connect'] = "30s" +default['haproxy']['defaults_timeouts']['check'] = "30s" +#default['haproxy']['defaults_timeouts']['queue'] = "100s" +default['haproxy']['defaults_timeouts']['client'] = "300s" +default['haproxy']['defaults_timeouts']['server'] = "300s" +default['haproxy']['tune']['bufsize'] = 1000000 +default['haproxy']['tune']['maxrewrite'] = 1024 default['haproxy']['cookie'] = nil @@ -70,9 +75,9 @@ default['haproxy']['user'] = "haproxy" default['haproxy']['group'] = "haproxy" default['haproxy']['global_max_connections'] = 8192 -default['haproxy']['member_max_connections'] = 100 -default['haproxy']['frontend_max_connections'] = 2000 -default['haproxy']['frontend_ssl_max_connections'] = 2000 +default['haproxy']['member_max_connections'] = 20000 +default['haproxy']['frontend_max_connections'] = 4096 +default['haproxy']['frontend_ssl_max_connections'] = 4096 default['haproxy']['install_method'] = 'package' default['haproxy']['conf_dir'] = '/etc/haproxy' @@ -88,7 +93,43 @@ default['haproxy']['source']['use_pcre'] = false default['haproxy']['source']['use_openssl'] = false default['haproxy']['source']['use_zlib'] = false -default['haproxy']['enabled_services'] = [] +default['haproxy']['enabled_services'] = [ + "dashboard_http", + "dashboard_https", + "glance_api", + "keystone_admin", + "keystone_public_internal", + "nova_compute_api", + "nova_metadata_api", + "novncproxy", + "cinder_api", + "neutron_api" +] + +default['haproxy']['roles'] = { + "os-identity" => [ + "keystone_admin", + "keystone_public_internal" + ], + "os-dashboard" => [ + "dashboard_http", + "dashboard_https" + ], + "os-compute-controller" => [ + "nova_compute_api", + "nova_metadata_api", + "novncproxy" + ], + "os-block-storage-controller" => [ + "cinder_api" + ], + "os-network-server" => [ + "neutron_api" + ], + "os-image" => [ + "glance_api" + ] +} default['haproxy']['listeners'] = { 'listen' => {}, @@ -96,118 +137,106 @@ default['haproxy']['listeners'] = { 'backend' => {} } - default['haproxy']['services'] = { "dashboard_http" => { - "role" => "os-compute-single-controller", + "role" => "os-dashboard", "frontend_port" => "80", "backend_port" => "80", - "balance" => "source", "options" => [ "capture cookie vgnvisitor= len 32", \ "cookie SERVERID insert indirect nocache", \ "mode http", \ "option forwardfor", \ "option httpchk", \ - "option httpclose", \ + "option http-server-close", \ 'rspidel ^Set-cookie:\ IP=' + # "appsession csrftoken len 42 timeout 1h" ] }, "dashboard_https" => { - "role" => "os-compute-single-controller", + "role" => "os-dashboard", "frontend_port" => "443", "backend_port" => "443", "balance" => "source", "options" => [ "option tcpka", "option httpchk", "option tcplog"] }, "glance_api" => { - "role" => "os-compute-single-controller", + "role" => "os-image-api", "frontend_port" => "9292", "backend_port" => "9292", - "balance" => "source", - "options" => [ "option tcpka", "option httpchk", "option tcplog"] + "options" => [ "option tcpka", "option httpchk", "option tcplog", "balance leastconn" ] }, "glance_registry_cluster" => { - "role" => "os-compute-single-controller", + "role" => "os-image-registry", "frontend_port" => "9191", "backend_port" => "9191", - "balance" => "source", - "options" => [ "option tcpka", "option httpchk", "option tcplog"] + "options" => [ "option tcpka", "option httpchk", "option tcplog", "balance leastconn" ] }, "keystone_admin" => { - "role" => "os-compute-single-controller", + "role" => "os-identity", "frontend_port" => "35357", "backend_port" => "35357", - "balance" => "source", - "options" => [ "option tcpka", "option httpchk", "option tcplog"] + "options" => [ "option tcpka", "option httpchk", "option tcplog", "balance leastconn" ] }, "keystone_public_internal" => { - "role" => "os-compute-single-controller", + "role" => "os-identity", "frontend_port" => "5000", "backend_port" => "5000", - "balance" => "source", - "options" => [ "option tcpka", "option httpchk", "option tcplog"] + "options" => [ "option tcpka", "option httpchk", "option tcplog", "balance leastconn" ] }, "nova_ec2_api" => { - "role" => "os-compute-single-controller", + "role" => "os-compute-api", "frontend_port" => "8773", "backend_port" => "8773", - "balance" => "source", "options" => [ "option tcpka", "option httpchk", "option tcplog"] }, "nova_compute_api" => { - "role" => "os-compute-single-controller", + "role" => "os-compute-api", "frontend_port" => "8774", "backend_port" => "8774", - "balance" => "source", - "options" => [ "option tcpka", "option httpchk", "option tcplog"] + "options" => [ "option tcpka", "option httpchk", "option tcplog", "balance leastconn"] }, "novncproxy" => { - "role" => "os-compute-single-controller", + "role" => "os-compute-vncproxy", "frontend_port" => "6080", "backend_port" => "6080", - "balance" => "source", - "options" => [ "option tcpka", "option http-server-close", "option tcplog"] + "balance" => "leastconn", + #"balance" => "source", + "options" => [ "option tcpka", "option http-server-close", "option tcplog", "balance leastconn"] }, "nova_metadata_api" => { - "role" => "os-compute-single-controller", + "role" => "os-compute-api-metadata", "frontend_port" => "8775", "backend_port" => "8775", - "balance" => "source", - "options" => [ "option tcpka", "option httpchk", "option tcplog"] + "options" => [ "option tcpka", "option httpchk", "option tcplog", "balance leastconn"] }, "cinder_api" => { - "role" => "os-compute-single-controller", + "role" => "os-block-storage-api", "frontend_port" => "8776", "backend_port" => "8776", - "balance" => "source", - "options" => [ "option tcpka", "option httpchk", "option tcplog"] + "options" => [ "option tcpka", "option httpchk", "option tcplog", "balance leastconn"] }, "ceilometer_api" => { "role" => "os-compute-single-controller", "frontend_port" => "8777", "backend_port" => "8777", - "balance" => "source", "options" => [ "option tcpka", "option httpchk", "option tcplog"] }, "spice" => { "role" => "os-compute-single-controller", "frontend_port" => "6082", "backend_port" => "6082", - "balance" => "source", "options" => [ "option tcpka", "option httpchk", "option tcplog"] }, "neutron_api" => { - "role" => "os-compute-single-controller", + "role" => "os-network-server", "frontend_port" => "9696", "backend_port" => "9696", - "balance" => "source", - "options" => [ "option tcpka", "option httpchk", "option tcplog"] + "options" => [ "option tcpka", "option httpchk", "option tcplog", "balance leastconn"] }, "swift_proxy" => { "role" => "os-compute-single-controller", "frontend_port" => "8080", "backend_port" => "8080", - "balance" => "source", "options" => [ "option tcpka", "option httpchk", "option tcplog"] } } diff --git a/chef/cookbooks/haproxy/providers/lb.rb b/chef/cookbooks/haproxy/providers/lb.rb index 218dc5b..7210b91 100644 --- a/chef/cookbooks/haproxy/providers/lb.rb +++ b/chef/cookbooks/haproxy/providers/lb.rb @@ -14,7 +14,11 @@ action :create do listener << "balance #{new_resource.balance}" unless new_resource.balance.nil? listener << "mode #{new_resource.mode}" unless new_resource.mode.nil? - listener += new_resource.servers.map {|server| "server #{server}" } + listener += new_resource.servers.map {|server| + if server + "server #{server}" + end + } node.default['haproxy']['listeners'][new_resource.type][new_resource.name] = listener end diff --git a/chef/cookbooks/haproxy/recipes/tcp_lb.rb b/chef/cookbooks/haproxy/recipes/tcp_lb.rb index 63929cf..7b3d325 100644 --- a/chef/cookbooks/haproxy/recipes/tcp_lb.rb +++ b/chef/cookbooks/haproxy/recipes/tcp_lb.rb @@ -17,33 +17,14 @@ # limitations under the License. # -defaultbag = "openstack" -if !Chef::DataBag.list.key?(defaultbag) - Chef::Application.fatal!("databag '#{defaultbag}' doesn't exist.") - return -end - -myitem = node.attribute?('cluster')? node['cluster']:"env_default" - -if !search(defaultbag, "id:#{myitem}") - Chef::Application.fatal!("databagitem '#{myitem}' doesn't exist.") - return -end - -mydata = data_bag_item(defaultbag, myitem) - -if mydata['ha']['status'].eql?('enable') - node.set['haproxy']['enabled_services'] = nil - node.set['haproxy']['incoming_address'] = mydata['ha']['haproxy']['vip'] - - mydata['ha']['haproxy']['roles'].each do |role, services| - services.each do |service| - node.set['haproxy']['services'][service]['role'] = role - unless node['haproxy']['enabled_services'].include?(service) - # node['haproxy']['enabled_services'] << service - node.set['haproxy']['enabled_services'] = node['haproxy']['enabled_services'] + [service] - end +node['haproxy']['roles'].each do |role, services| + services.each do |service| + node.set['haproxy']['services'][service]['role'] = role + unless node['haproxy']['enabled_services'].include?(service) + # node['haproxy']['enabled_services'] << service + node.set['haproxy']['enabled_services'] = node['haproxy']['enabled_services'] + [service] end + node.save end end @@ -54,23 +35,28 @@ node['haproxy']['services'].each do |name, service| if node['haproxy']['choose_backend'].eql?("prefeed") pool_members = [] - mydata['node_mapping'].each do |nodename, nodeinfo| - if nodeinfo['roles'].include?(service['role']) - pool_members << nodename + if node['haproxy'].has_attribute?(:node_mapping) + node['haproxy']['node_mapping'].each do |nodename, nodeinfo| + if nodeinfo['roles'].include?(service['role']) + pool_members << nodename + end end end else pool_members = search(:node, "run_list:role\\[#{service['role']}\\] AND chef_environment:#{node.chef_environment}") || [] + Chef::Log.info("===== search run_list:role\\[#{service['role']}\\] AND chef_environment:#{node.chef_environment}") # load balancer may be in the pool pool_members << node if node.run_list.roles.include?(service[:role]) + pool_members = pool_members.sort_by { |node| node.name } unless pool_members.empty? end # we prefer connecting via local_ipv4 if # pool members are in the same cloud # TODO refactor this logic into library...see COOK-494 pool_members.map! do |member| + Chef::Log.info("processing member ...... #{member}") if node['haproxy']['choose_backend'].eql?("prefeed") - server_ip = mydata['node_mapping']["#{member}"]['management_ip'] + server_ip = node['haproxy']['node_mapping']["#{member}"]['management_ip'] {:ipaddress => server_ip, :hostname => member} else server_ip = begin @@ -92,10 +78,12 @@ node['haproxy']['services'].each do |name, service| pool = service[:options] servers = pool_members.uniq.map do |s| # novncproxy cannot to be checked - if name.eql?("novncproxy") - "#{s[:hostname]} #{s[:ipaddress]}:#{service[:backend_port]}" - else - "#{s[:hostname]} #{s[:ipaddress]}:#{service[:backend_port]} check inter 2000 rise 2 fall 5" + if s[:hostname] and s[:ipaddress] + if name.eql?("novncproxy") + "#{s[:hostname]} #{s[:ipaddress]}:#{service[:backend_port]}" + else + "#{s[:hostname]} #{s[:ipaddress]}:#{service[:backend_port]} check inter 30000 fastinter 1000 rise 2 fall 5" + end end end @@ -135,3 +123,30 @@ service "haproxy" do supports :restart => true, :status => true, :reload => true action [:enable, :start] end + +# Enable haproxy log to file +service "rsyslog" do + supports :status => true, :restart => true, :start => true, :stop => true + action :nothing +end + +ruby_block "enable haproxy log" do + block do + fe = Chef::Util::FileEdit.new('/etc/rsyslog.conf') + fe.search_file_replace_line(/^\#\$ModLoad\s+imudp/, '$ModLoad imudp') + fe.write_file + fe.search_file_replace_line(/^\#\$UDPServerRun\s+514/, '$UDPServerRun 514') + fe.write_file + fe.search_file_replace_line(/^\*.emerg\s+\*/, "#*.emerg *") + fe.write_file + haproxylog = "#{node['haproxy']['log']['facilities']}.* \ + #{node['haproxy']['log']['file']}" + if !::File.readlines('/etc/rsyslog.conf').grep(/#{haproxylog}/).any? + fe.insert_line_after_match('^local7.*', haproxylog) + fe.write_file + end + end + action :nothing + subscribes :run, "template[#{node['haproxy']['conf_dir']}/haproxy.cfg]", :immediately + notifies :restart, "service[rsyslog]", :delayed +end diff --git a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb index 6b3475b..05329a8 100644 --- a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb +++ b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb @@ -1,12 +1,14 @@ global - #log 127.0.0.1 local0 - log 127.0.0.1 local4 notice - log 127.0.0.1 local4 info + log 127.0.0.1 <%= node['haproxy']['log']['facilities'] -%> notice + log 127.0.0.1 <%= node['haproxy']['log']['facilities'] -%> info + log-send-hostname daemon - maxconn <%= node['haproxy']['global_max_connections'] %> - #debug - #quiet - spread-checks 5 + nbproc 8 + # debug + # quiet + spread-checks 5 + tune.bufsize <%= node['haproxy']['tune']['bufsize'] %> + tune.maxrewrite <%= node['haproxy']['tune']['maxrewrite'] %> user <%= node['haproxy']['user'] %> group <%= node['haproxy']['group'] %> <% if node['haproxy']['enable_stats_socket'] -%> @@ -17,6 +19,7 @@ defaults log global mode http retries 3 + maxconn <%= node['haproxy']['member_max_connections'] %> <% @defaults_timeouts.sort.map do | value, time | -%> timeout <%= value %> <%= time %> <% end -%> @@ -26,6 +29,13 @@ defaults balance <%= node['haproxy']['balance_algorithm'] %> # Set up application listeners here. +listen stats + bind 0.0.0.0:8080 + mode http + stats refresh 10s + stats enable + stats uri / + stats realm Strictly\ Private <% node['haproxy']['listeners'].each do |type, listeners | %> <% listeners.each do |name, listen| %> diff --git a/chef/cookbooks/homebrew/CHANGELOG.md b/chef/cookbooks/homebrew/CHANGELOG.md new file mode 100644 index 0000000..7b677aa --- /dev/null +++ b/chef/cookbooks/homebrew/CHANGELOG.md @@ -0,0 +1,71 @@ +homebrew Cookbook CHANGELOG +=========================== +This file is used to list changes made in each version of the homebrew cookbook. + + +v1.6.6 (2014-05-29) +------------------- +- [COOK-3283] Use homebrew_owner for cask and tap +- [COOK-4670] homebrew_tap provider is not idempotent +- [COOK-4671] Syntax Error in README + + +v1.6.4 (2014-05-08) +------------------- +- Fixing cask provider correctly this time. "brew cask list" + + +v1.6.2 (2014-05-08) +------------------- +- Fixing typo in cask provider: 's/brew brew/brew/' + + +v1.6.0 (2014-04-23) +------------------- +- [COOK-3960] Added LWRP for brew cask +- [COOK-4508] Add ChefSpec matchers for homebrew_tap +- [COOK-4566] Guard against "HEAD only" formulae + + +v1.5.4 +------ +- [COOK-4023] Fix installer script's URL. +- Fixing up style for rubocop + + +v1.5.2 +------ +- [COOK-3825] setting $HOME on homebrew_package + + +v1.5.0 +------ +### Bug +- **[COOK-3589](https://tickets.opscode.com/browse/COOK-3589)** - Add homebrew as the default package manager on OS X Server + +v1.4.0 +------ +### Bug +- **[COOK-3283](https://tickets.opscode.com/browse/COOK-3283)** - Support running homebrew cookbook as root user, with sudo, or a non-privileged user + +v1.3.2 +------ +- [COOK-1793] - use homebrew "go" script to install homebrew +- [COOK-1821] - Discovered version using Homebrew Formula factory fails check that verifies that version is a String +- [COOK-1843] - Homebrew README.md contains non-ASCII characters, triggering same issue as COOK-522 + +v1.3.0 +------ +- [COOK-1425] - use new json output format for formula +- [COOK-1578] - Use shell_out! instead of popen4 + +v1.2.0 +------ +Opscode has taken maintenance of this cookbook as the original author has other commitments. This is the initial release with Opscode as maintainer. + +Changes in this release: + +- [pull/2] - support for option passing to brew +- [pull/3] - add brew upgrade and control return value from command +- [pull/9] - added LWRP for "brew tap" +- README is now markdown, not rdoc. diff --git a/chef/cookbooks/homebrew/README.md b/chef/cookbooks/homebrew/README.md new file mode 100644 index 0000000..19d90ed --- /dev/null +++ b/chef/cookbooks/homebrew/README.md @@ -0,0 +1,144 @@ +Homebrew Cookbook +================= +This cookbook installs [Homebrew](http://mxcl.github.com/homebrew/) and replaces MacPorts as the *default package provider* for the package resource on OS X systems. + +This cookbook is now maintained by Opscode. The original author, maintainer and copyright holder is Graeme Mathieson. The cookbook remains licensed under the Apache License version 2. + +[Original blog post by Graeme](http://woss.name/2011/01/23/converging-your-home-directory-with-chef/) + + +Requirements +------------ +### Prerequisites + +In order for this recipe to work, your userid must own `/usr/local`. This is outside the scope of the cookbook because it's anticipated that you'll run the cookbook as your own user, not root and you'd have to be root to take ownership of the directory. Easiest way to get started: + +```bash +sudo chown -R `whoami`:staff /usr/local +``` + +Bear in mind that this will take ownership of the entire folder and its contents, so if you've already got stuff in there (eg MySQL owned by a `mysql` user) you'll need to be a touch more careful. This is a recommendation from Homebrew. + +### Platform + +- Mac OS X (10.6+) + +The only platform supported by Homebrew itself at the time of this writing is Mac OS X. It should work fine on Server edition as well, and on platforms that Homebrew supports in the future. + + +Attributes +---------- +- `node['homebrew']['owner']` - The user that will own the Homebrew installation and packages. Setting this will override the default behavior which is to use the non-privileged user that has invoked the Chef run (or the `SUDO_USER` if invoked with sudo). The default is `nil`. + + +Resources and Providers +----------------------- +### package / homebrew\_package + +This cookbook provides a package provider called `homebrew_package` which will install/remove packages using Homebrew. This becomes the default provider for `package` if your platform is Mac OS X. + +As this extends the built-in package resource/provider in Chef, it has all the resource attributes and actions available to the package resource. However, a couple notes: + +- Homebrew itself doesn't have a notion of "upgrade" per se. The "upgrade" action will simply perform an install, and if the Homebrew Formula for the package is newer, it will upgrade. +- Likewise, Homebrew doesn't have a purge, but the "purge" action will act like "remove". + +#### Examples + +```ruby +package 'mysql' do + action :install +end + +homebrew_package 'mysql' + +package 'mysql' do + provider Chef::Provider::Package::Homebrew +end +``` + +### homebrew\_tap + +LWRP for `brew tap`, a Homebrew command used to add additional formula repositories. From the `brew` man page: + +```text +tap [tap] + Tap a new formula repository from GitHub, or list existing taps. + + tap is of the form user/repo, e.g. brew tap homebrew/dupes. +``` + +Default action is `:tap` which enables the repository. Use `:untap` to disable a tapped repository. + +#### Examples + +```ruby +homebrew_tap 'homebrew/dupes' + +homebrew_tap 'homebrew/dupes' do + action :untap +end +``` + +## homebrew\_cask + +LWRP for `brew cask`, a Homebrew-style CLI workflow for the administration +of Mac applications distributed as binaries. It's implemented as a homebrew +"external command" called cask. + +[homebrew-cask on GitHub](https://github.com/phinze/homebrew-cask) + +## Prerequisites + +You must have the homebrew-cask repository tapped. + + homebrew_tap 'phinze/cask' + +And then install the homebrew cask package before using this LWRP. + + package "brew-cask" do + action :install + end + + +### Examples + + homebrew_cask "google-chrome" + + homebrew_tap "google-chrome" do + action :uncask + end + +Default action is `:cask` which installs the Application binary . Use `:uncask` to +uninstall a an Application. + +[View the list of available Casks](https://github.com/phinze/homebrew-cask/tree/master/Casks) + + +Usage +----- +We strongly recommend that you put "recipe[homebrew]" in your node's run list, to ensure that it is available on the system and that Homebrew itself gets installed. Putting an explicit dependency in the metadata will cause the cookbook to be downloaded and the library loaded, thus resulting in changing the package provider on Mac OS X, so if you have systems you want to use the default (Mac Ports), they would be changed to Homebrew. + +The default itself ensures that Homebrew is installed and up to date. + + +License and Authors +------------------- +- Author:: Graeme Mathieson () +- Author:: Joshua Timberman () + +```text +Copyright:: 2011, Graeme Mathieson +Copyright:: 2012, Opscode, Inc + +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. +``` diff --git a/chef/cookbooks/homebrew/attributes/default.rb b/chef/cookbooks/homebrew/attributes/default.rb new file mode 100644 index 0000000..084e2d9 --- /dev/null +++ b/chef/cookbooks/homebrew/attributes/default.rb @@ -0,0 +1,22 @@ +# +# Author:: Joshua Timberman () +# Author:: Graeme Mathieson () +# Cookbook Name:: homebrew +# Attributes:: default +# +# Copyright 2011-2013, Opscode, Inc. +# +# 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. +# + +default['homebrew']['owner'] = nil diff --git a/chef/cookbooks/homebrew/libraries/homebrew_mixin.rb b/chef/cookbooks/homebrew/libraries/homebrew_mixin.rb new file mode 100644 index 0000000..0170c6a --- /dev/null +++ b/chef/cookbooks/homebrew/libraries/homebrew_mixin.rb @@ -0,0 +1,53 @@ +# +# Author:: Joshua Timberman () +# Author:: Graeme Mathieson () +# Cookbook Name:: homebrew +# Libraries:: homebrew_mixin +# +# Copyright 2011-2013, Opscode, Inc. +# +# 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. +# + +module Homebrew + # Homebrew + module Mixin + def homebrew_owner + @homebrew_owner ||= calculate_owner + end + + private + + def calculate_owner + owner = homebrew_owner_attr || sudo_user || current_user + if owner == 'root' + fail Chef::Exceptions::User, + "Homebrew owner is 'root' which is not supported. " + + "To set an explicit owner, please set node['homebrew']['owner']." + end + owner + end + + def homebrew_owner_attr + node['homebrew']['owner'] + end + + def sudo_user + ENV['SUDO_USER'] + end + + def current_user + ENV['USER'] + end + end +end diff --git a/chef/cookbooks/homebrew/libraries/homebrew_package.rb b/chef/cookbooks/homebrew/libraries/homebrew_package.rb new file mode 100644 index 0000000..dc84e40 --- /dev/null +++ b/chef/cookbooks/homebrew/libraries/homebrew_package.rb @@ -0,0 +1,109 @@ +# +# Author:: Joshua Timberman () +# Author:: Graeme Mathieson () +# Cookbook Name:: homebrew +# Libraries:: homebrew_package +# +# Copyright 2011-2013, Opscode, Inc. +# +# 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. +# + +require 'chef/provider/package' +require 'chef/resource/package' +require 'chef/platform' +require 'chef/mixin/shell_out' + +class Chef + class Provider + class Package + # Package + class Homebrew < Package + # Homebrew packagex + include Chef::Mixin::ShellOut + include ::Homebrew::Mixin + + def load_current_resource + @current_resource = Chef::Resource::Package.new(@new_resource.name) + @current_resource.package_name(@new_resource.package_name) + @current_resource.version(current_installed_version) + + @current_resource + end + + def install_package(name, version) + brew('install', @new_resource.options, name) + end + + def upgrade_package(name, version) + brew('upgrade', name) + end + + def remove_package(name, version) + brew('uninstall', @new_resource.options, name) + end + + # Homebrew doesn't really have a notion of purging, so just remove. + def purge_package(name, version) + @new_resource.options = ((@new_resource.options || '') << ' --force').strip + remove_package(name, version) + end + + protected + + def brew(*args) + get_response_from_command("brew #{args.join(' ')}") + end + + def current_installed_version + pkg = get_version_from_formula + versions = pkg.to_hash['installed'].map { |v| v['version'] } + versions.join(' ') unless versions.empty? + end + + def candidate_version + pkg = get_version_from_formula + pkg.stable ? pkg.stable.version.to_s : pkg.version.to_s + end + + def get_version_from_command(command) + version = get_response_from_command(command).chomp + version.empty? ? nil : version + end + + def get_version_from_formula + brew_cmd = shell_out!('brew --prefix', :user => homebrew_owner) + libpath = ::File.join(brew_cmd.stdout.chomp, 'Library', 'Homebrew') + $LOAD_PATH.unshift(libpath) + + require 'global' + require 'cmd/info' + + Formula[new_resource.package_name] + end + + def get_response_from_command(command) + require 'etc' + home_dir = Etc.getpwnam(homebrew_owner).dir + + Chef::Log.debug "Executing '#{command}' as #{homebrew_owner}" + output = shell_out!(command, :user => homebrew_owner, :environment => { 'HOME' => home_dir }) + output.stdout + end + end + end + end +end + +Chef::Platform.set :platform => :mac_os_x_server, :resource => :package, :provider => Chef::Provider::Package::Homebrew +Chef::Platform.set :platform => :mac_os_x, :resource => :package, :provider => Chef::Provider::Package::Homebrew diff --git a/chef/cookbooks/homebrew/libraries/matchers.rb b/chef/cookbooks/homebrew/libraries/matchers.rb new file mode 100644 index 0000000..a09cf28 --- /dev/null +++ b/chef/cookbooks/homebrew/libraries/matchers.rb @@ -0,0 +1,11 @@ +if defined?(ChefSpec) + + def tap_homebrew_tap(tap) + ChefSpec::Matchers::ResourceMatcher.new(:homebrew_tap, :tap, tap) + end + + def untap_homebrew_tap(tap) + ChefSpec::Matchers::ResourceMatcher.new(:homebrew_tap, :tap, tap) + end + +end diff --git a/chef/cookbooks/homebrew/metadata.json b/chef/cookbooks/homebrew/metadata.json new file mode 100644 index 0000000..57f1d4e --- /dev/null +++ b/chef/cookbooks/homebrew/metadata.json @@ -0,0 +1,32 @@ +{ + "name": "homebrew", + "version": "1.6.6", + "description": "Install Homebrew and use it as your package provider in Mac OS X", + "long_description": "Homebrew Cookbook\n=================\nThis cookbook installs [Homebrew](http://mxcl.github.com/homebrew/) and replaces MacPorts as the *default package provider* for the package resource on OS X systems.\n\nThis cookbook is now maintained by Opscode. The original author, maintainer and copyright holder is Graeme Mathieson. The cookbook remains licensed under the Apache License version 2.\n\n[Original blog post by Graeme](http://woss.name/2011/01/23/converging-your-home-directory-with-chef/)\n\n\nRequirements\n------------\n### Prerequisites\n\nIn order for this recipe to work, your userid must own `/usr/local`. This is outside the scope of the cookbook because it's anticipated that you'll run the cookbook as your own user, not root and you'd have to be root to take ownership of the directory. Easiest way to get started:\n\n```bash\nsudo chown -R `whoami`:staff /usr/local\n```\n\nBear in mind that this will take ownership of the entire folder and its contents, so if you've already got stuff in there (eg MySQL owned by a `mysql` user) you'll need to be a touch more careful. This is a recommendation from Homebrew.\n\n### Platform\n\n- Mac OS X (10.6+)\n\nThe only platform supported by Homebrew itself at the time of this writing is Mac OS X. It should work fine on Server edition as well, and on platforms that Homebrew supports in the future.\n\n\nAttributes\n----------\n- `node['homebrew']['owner']` - The user that will own the Homebrew installation and packages. Setting this will override the default behavior which is to use the non-privileged user that has invoked the Chef run (or the `SUDO_USER` if invoked with sudo). The default is `nil`.\n\n\nResources and Providers\n-----------------------\n### package / homebrew\\_package\n\nThis cookbook provides a package provider called `homebrew_package` which will install/remove packages using Homebrew. This becomes the default provider for `package` if your platform is Mac OS X.\n\nAs this extends the built-in package resource/provider in Chef, it has all the resource attributes and actions available to the package resource. However, a couple notes:\n\n- Homebrew itself doesn't have a notion of \"upgrade\" per se. The \"upgrade\" action will simply perform an install, and if the Homebrew Formula for the package is newer, it will upgrade.\n- Likewise, Homebrew doesn't have a purge, but the \"purge\" action will act like \"remove\".\n\n#### Examples\n\n```ruby\npackage 'mysql' do\n action :install\nend\n\nhomebrew_package 'mysql'\n\npackage 'mysql' do\n provider Chef::Provider::Package::Homebrew\nend\n```\n\n### homebrew\\_tap\n\nLWRP for `brew tap`, a Homebrew command used to add additional formula repositories. From the `brew` man page:\n\n```text\ntap [tap]\n Tap a new formula repository from GitHub, or list existing taps.\n\n tap is of the form user/repo, e.g. brew tap homebrew/dupes.\n```\n\nDefault action is `:tap` which enables the repository. Use `:untap` to disable a tapped repository.\n\n#### Examples\n\n```ruby\nhomebrew_tap 'homebrew/dupes'\n\nhomebrew_tap 'homebrew/dupes' do\n action :untap\nend\n```\n\n## homebrew\\_cask\n\nLWRP for `brew cask`, a Homebrew-style CLI workflow for the administration\nof Mac applications distributed as binaries. It's implemented as a homebrew\n\"external command\" called cask.\n\n[homebrew-cask on GitHub](https://github.com/phinze/homebrew-cask)\n\n## Prerequisites\n\nYou must have the homebrew-cask repository tapped.\n\n homebrew_tap 'phinze/cask' \n \nAnd then install the homebrew cask package before using this LWRP.\n\n package \"brew-cask\" do\n action :install\n end\n\n\n### Examples\n\n homebrew_cask \"google-chrome\"\n\n homebrew_tap \"google-chrome\" do\n action :uncask\n end\n\nDefault action is `:cask` which installs the Application binary . Use `:uncask` to\nuninstall a an Application.\n\n[View the list of available Casks](https://github.com/phinze/homebrew-cask/tree/master/Casks)\n\n\nUsage\n-----\nWe strongly recommend that you put \"recipe[homebrew]\" in your node's run list, to ensure that it is available on the system and that Homebrew itself gets installed. Putting an explicit dependency in the metadata will cause the cookbook to be downloaded and the library loaded, thus resulting in changing the package provider on Mac OS X, so if you have systems you want to use the default (Mac Ports), they would be changed to Homebrew.\n\nThe default itself ensures that Homebrew is installed and up to date.\n\n\nLicense and Authors\n-------------------\n- Author:: Graeme Mathieson ()\n- Author:: Joshua Timberman ()\n\n```text\nCopyright:: 2011, Graeme Mathieson\nCopyright:: 2012, Opscode, Inc \n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may\nnot use this file except in compliance with the License. You may obtain\na copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "mac_os_x": ">= 0.0.0", + "mac_os_x_server": ">= 0.0.0" + }, + "dependencies": { + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "homebrew": "Install Homebrew" + } +} \ No newline at end of file diff --git a/chef/cookbooks/homebrew/metadata.rb b/chef/cookbooks/homebrew/metadata.rb new file mode 100644 index 0000000..80d0717 --- /dev/null +++ b/chef/cookbooks/homebrew/metadata.rb @@ -0,0 +1,10 @@ +name 'homebrew' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Install Homebrew and use it as your package provider in Mac OS X' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '1.6.6' +recipe 'homebrew', 'Install Homebrew' +supports 'mac_os_x' +supports 'mac_os_x_server' diff --git a/chef/cookbooks/homebrew/providers/cask.rb b/chef/cookbooks/homebrew/providers/cask.rb new file mode 100644 index 0000000..c127c28 --- /dev/null +++ b/chef/cookbooks/homebrew/providers/cask.rb @@ -0,0 +1,33 @@ +include ::Homebrew::Mixin + +def load_current_resource + @cask = Chef::Resource::HomebrewCask.new(new_resource.name) + cask_dir = @cask.name + + Chef::Log.debug("Checking whether we've already installed cask #{new_resource.name}") + if ::File.directory?("/opt/homebrew-cask/Caskroom/#{cask_dir}") + @cask.casked true + else + @cask.casked false + end +end + +action :cask do + unless @cask.casked + execute "installing cask #{new_resource.name}" do + command "/usr/local/bin/brew cask install #{new_resource.name}" + user homebrew_owner + not_if "/usr/local/bin/brew cask list | grep #{new_resource.name}" + end + end +end + +action :uncask do + if @cask.casked + execute "uninstalling cask #{new_resource.name}" do + command "/usr/local/bin/brew cask uninstall #{new_resource.name}" + user homebrew_owner + only_if "/usr/local/bin/brew cask list | grep #{new_resource.name}" + end + end +end diff --git a/chef/cookbooks/homebrew/providers/tap.rb b/chef/cookbooks/homebrew/providers/tap.rb new file mode 100644 index 0000000..9a7d440 --- /dev/null +++ b/chef/cookbooks/homebrew/providers/tap.rb @@ -0,0 +1,54 @@ +# +# Author:: Joshua Timberman () +# Author:: Graeme Mathieson () +# Cookbook Name:: homebrew +# Providers:: tap +# +# Copyright 2011-2013, Opscode, Inc. +# +# 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. +# + +include ::Homebrew::Mixin + +def load_current_resource + @tap = Chef::Resource::HomebrewTap.new(new_resource.name) + tap_dir = @tap.name.gsub('/', '-') + + Chef::Log.debug("Checking whether we've already tapped #{new_resource.name}") + if ::File.directory?("/usr/local/Library/Taps/#{tap_dir}") + @tap.tapped true + else + @tap.tapped false + end +end + +action :tap do + unless @tap.tapped + execute "tapping #{new_resource.name}" do + command "/usr/local/bin/brew tap #{new_resource.name}" + not_if "/usr/local/bin/brew tap | grep #{new_resource.name}" + user homebrew_owner + end + end +end + +action :untap do + if @tap.tapped + execute "untapping #{new_resource.name}" do + command "/usr/local/bin/brew untap #{new_resource.name}" + only_if "/usr/local/bin/brew tap | grep #{new_resource.name}" + user homebrew_owner + end + end +end diff --git a/chef/cookbooks/homebrew/recipes/default.rb b/chef/cookbooks/homebrew/recipes/default.rb new file mode 100644 index 0000000..d9d4717 --- /dev/null +++ b/chef/cookbooks/homebrew/recipes/default.rb @@ -0,0 +1,46 @@ +# +# Author:: Joshua Timberman () +# Author:: Graeme Mathieson () +# Cookbook Name:: homebrew +# Recipes:: default +# +# Copyright 2011-2013, Opscode, Inc. +# +# 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. +# + +extend(Homebrew::Mixin) + +homebrew_go = "#{Chef::Config[:file_cache_path]}/homebrew_go" +owner = homebrew_owner + +Chef::Log.debug("Homebrew owner is '#{homebrew_owner}'") + +remote_file homebrew_go do + source 'https://raw.github.com/Homebrew/homebrew/go/install' + mode 00755 +end + +execute homebrew_go do + user owner + not_if { ::File.exist? '/usr/local/bin/brew' } +end + +package 'git' do + not_if 'which git' +end + +execute 'update homebrew from github' do + user owner + command '/usr/local/bin/brew update || true' +end diff --git a/chef/cookbooks/homebrew/resources/cask.rb b/chef/cookbooks/homebrew/resources/cask.rb new file mode 100644 index 0000000..cfa2fdc --- /dev/null +++ b/chef/cookbooks/homebrew/resources/cask.rb @@ -0,0 +1,14 @@ +actions :cask, :uncask +attribute :name, + :name_attribute => true, + :kind_of => String, + :regex => /\w+(?:\/\w+)+/ + +attribute :casked, + :kind_of => [TrueClass, FalseClass] + +### hax for default action +def initialize( *args ) + super + @action = :cask +end diff --git a/chef/cookbooks/yum/resources/key.rb b/chef/cookbooks/homebrew/resources/tap.rb similarity index 56% rename from chef/cookbooks/yum/resources/key.rb rename to chef/cookbooks/homebrew/resources/tap.rb index 96f9e72..9953716 100644 --- a/chef/cookbooks/yum/resources/key.rb +++ b/chef/cookbooks/homebrew/resources/tap.rb @@ -1,8 +1,10 @@ # -# Cookbook Name:: yum -# Resource:: key +# Author:: Joshua Timberman () +# Author:: Graeme Mathieson () +# Cookbook Name:: homebrew +# Resources:: tap # -# Copyright 2011, Opscode, Inc. +# Copyright 2011-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,13 +19,17 @@ # limitations under the License. # -actions :add, :remove -default_action :add +actions :tap, :untap +attribute :name, + :name_attribute => true, + :kind_of => String, + :regex => /\w+(?:\/\w+)+/ -attribute :key, :kind_of => String, :name_attribute => true -attribute :url, :kind_of => String, :default => nil +attribute :tapped, + :kind_of => [TrueClass, FalseClass] +### hax for default action def initialize(*args) super - @action = :add + @action = :tap end diff --git a/chef/cookbooks/iptables/CHANGELOG.md b/chef/cookbooks/iptables/CHANGELOG.md index 9d3ff4b..7d73f79 100644 --- a/chef/cookbooks/iptables/CHANGELOG.md +++ b/chef/cookbooks/iptables/CHANGELOG.md @@ -1,16 +1,34 @@ -## v0.12.0: +iptables CHANGELOG +================== -* [COOK-2213] - iptables disabled recipe +v0.13.2 (2014-04-09) +-------------------- +- [COOK-4496] Added Amazon Linux support -## v0.11.0: -* [COOK-1883] - add perl package so rebuild script works +v0.13.0 (2014-03-19) +-------------------- +- [COOK-3927] Substitute Perl version of rebuild-iptables with Ruby version -## v0.10.0: -* [COOK-641] - be able to save output on rhel-family -* [COOK-655] - use a template from other cookbooks +v0.12.2 (2014-03-18) +-------------------- +- [COOK-4411] - Add newling to iptables.snat -## v0.9.3: -* Current public release. +v0.12.0 +------- +- [COOK-2213] - iptables disabled recipe + +v0.11.0 +-------- +- [COOK-1883] - add perl package so rebuild script works + +v0.10.0 +------- +- [COOK-641] - be able to save output on rhel-family +- [COOK-655] - use a template from other cookbooks + +v0.9.3 +------ +- Current public release. diff --git a/chef/cookbooks/iptables/CONTRIBUTING b/chef/cookbooks/iptables/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/iptables/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/iptables/LICENSE b/chef/cookbooks/iptables/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/iptables/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/iptables/README.md b/chef/cookbooks/iptables/README.md index b08a41d..ce6af87 100644 --- a/chef/cookbooks/iptables/README.md +++ b/chef/cookbooks/iptables/README.md @@ -59,7 +59,7 @@ template: -A FWR -p tcp -m tcp --dport 80 -j ACCEPT This would go in the cookbook, -`httpd/templates/default/port_http.erb`. Then to use it in +`httpd/templates/default/http.erb`. Then to use it in `recipe[httpd]`: iptables_rule "http" diff --git a/chef/cookbooks/iptables/files/default/rebuild-iptables b/chef/cookbooks/iptables/files/default/rebuild-iptables deleted file mode 100644 index 6918537..0000000 --- a/chef/cookbooks/iptables/files/default/rebuild-iptables +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/perl -w -our $ID = q$Id: rebuild-iptables 344 2006-10-04 02:48:30Z digant $; - -# -# rebuild-iptables -- Construct an iptables rules file from fragments. -# -# Written by Russ Allbery -# Adapted by Digant C Kasundra -# Adapted by Joe Williams (2011) -# Copyright 2005, 2006 Board of Trustees, Leland Stanford Jr. University -# -# Constructs an iptables rules file from the prefix, standard, and suffix -# files in the iptables configuration area, adding any additional modules -# specified in the command line, and prints the resulting iptables rules to -# standard output (suitable for saving into /var/lib/iptables or some other -# appropriate location on the system). - -############################################################################## -# Modules and declarations -############################################################################## - -require 5.006; -use strict; - -use Getopt::Long qw(GetOptions); - -# Path to the iptables template area. -our $TEMPLATE = '/etc/iptables.d'; - -############################################################################## -# Installation -############################################################################## - -# Return the prefix -sub prefix { - my $data; - ( $data = <<'END_OF_PREFIX' ) =~ s/^\s+//gm; - *filter - :INPUT ACCEPT - :FORWARD ACCEPT - :OUTPUT ACCEPT - :FWR - - -A INPUT -j FWR - -A FWR -i lo -j ACCEPT -END_OF_PREFIX - - return $data; -} - -# Return the suffix -sub suffix { - my $data; - ( $data = <<'END_OF_SUFFIX' ) =~ s/^\s+//gm; - # Rejects all remaining connections with port-unreachable errors. - - -A FWR -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j REJECT --reject-with icmp-port-unreachable - -A FWR -p udp -j REJECT --reject-with icmp-port-unreachable - COMMIT -END_OF_SUFFIX - - return $data; -} - -sub snat { - my $data = ""; - if ( -f "/etc/iptables.snat" ) { - open( SNAT, "<", "/etc/iptables.snat" ) - or die "$0: cannot open /etc/iptables.snat: $!\n"; - while () { - $data = $data . $_; - } - close(SNAT); - } - return $data; -} - -# Read in a file, processing includes as required. Returns the contents of -# the file as an array. -sub read_iptables { - my ($file) = @_; - my @data; - $file = $TEMPLATE . '/' . $file unless $file =~ m%^\.?/%; - local *MODULE; - open( MODULE, '<', $file ) or die "$0: cannot open $file: $!\n"; - local $_; - while () { - if (/^\s*include\s+(\S+)$/) { - my $included = $1; - $included = $TEMPLATE . '/' . $included - unless $included =~ m%^\.?/%; - if ( $file eq $included ) { - die "$0: include loop in $file, line $.\n"; - } - push( @data, "\n" ); - push( @data, read_iptables($included) ); - push( @data, "\n" ); - } elsif (/^\s*include\s/) { - die "$0: malformed include line in $file, line $.\n"; - } else { - push( @data, $_ ); - } - } - close MODULE; - return @data; -} - -# Write a file carefully. -sub write_iptables { - my ( $file, @data ) = @_; - open( NEW, "> $file.new" ) or die "$0: cannot create $file.new: $!\n"; - print NEW @data or die "$0: cannot write to $file.new: $!\n"; - close NEW or die "$0: cannot flush $file.new: $!\n"; - rename( "$file.new", $file ) - or die "$0: cannot install new $file: $!\n"; -} - -# Install iptables on a Red Hat system. Takes the array containing the new -# iptables data. -sub install_redhat { - my (@data) = @_; - write_iptables( '/etc/sysconfig/iptables', @data ); - system( "/sbin/service", "iptables", "restart" ); -} - -# Install iptables on a Debian system. Take the array containing the new -# iptables data. -sub install_debian { - my (@data) = @_; - unless ( -d '/etc/iptables' ) { - mkdir( '/etc/iptables', 0755 ) - or die "$0: cannot mkdir /etc/iptables: $!\n"; - } - write_iptables( "/etc/iptables/general", @data ); - system("/sbin/iptables-restore < /etc/iptables/general") == 0 - or die "rebuild-iptables: iptables-restore failed! - $?" -} - -############################################################################## -# Main routine -############################################################################## - -# Fix things up for error reporting. -$| = 1; -my $fullpath = $0; -$0 =~ s%.*/%%; - -# Parse command-line options. -my ( $help, $version ); -Getopt::Long::config( 'bundling', 'no_ignore_case' ); -GetOptions( - 'h|help' => \$help, - 'v|version' => \$version -) or exit 1; -if ($help) { - print "Feeding myself to perldoc, please wait....\n"; - exec( 'perldoc', '-t', $fullpath ); -} elsif ($version) { - my $version = join( ' ', ( split( ' ', $ID ) )[ 1 .. 3 ] ); - $version =~ s/,v\b//; - $version =~ s/(\S+)$/($1)/; - $version =~ tr%/%-%; - print $version, "\n"; - exit; -} -my @modules; - -if ( -d '/etc/iptables.d' ) { - @modules = ; -} - -# Concatenate everything together. -my @data; -push( @data, prefix() ); -push( @data, "\n" ); -for my $module (@modules) { - push( @data, read_iptables($module) ); - push( @data, "\n" ); -} -push( @data, suffix() ); -push( @data, snat() ); - -if ( -f '/etc/debian_version' ) { - install_debian(@data); -} elsif ( -f '/etc/redhat-release' ) { - install_redhat(@data); -} else { - die "$0: cannot figure out whether this is Red Hat or Debian\n"; -} - -exit 0; -__END__ - -############################################################################## -# Documentation -############################################################################## - -=head1 NAME - -rebuild-iptables - Construct an iptables rules file from fragments - -=head1 SYNOPSIS - -rebuild-iptables [B<-hv>] - -=head1 DESCRIPTION - -B constructs an iptables configuration file by concatenating -various modules found in F. The resulting iptables -configuration file is written to the appropriate file for either Red Hat or -Debian (determined automatically) and iptables is restarted. - -Each module is just a text file located in the directory mentioned above that -contains one or more iptables configuration lines (basically the arguments to -an B invocation), possibly including comments. - -Along with the modules in the directory specified, a standard prefix and suffix -is added. - -Normally, the contents of each module are read in verbatim, but a module may -also contain the directive: - - include - -on a separate line, where is the path to another module to include, -specified the same way as modules given on the command line (hence, either a -file name relative to F or an -absolute path). Such a line will be replaced with the contents of the named -file. Be careful when using this directive to not create loops; files -including themselves will be detected, but more complex loops will not and -will result in infinite output. - -=head1 OPTIONS - -=over 4 - -=item B<-h>, B<--help> - -Print out this documentation (which is done simply by feeding the script to -C). - -=item B<-v>, B<--version> - -Print out the version of B and exit. - -=back - -=head1 FILES - -=over 4 - -=item F - -The default module location. - -=item F - -If this file exists, the system is assumed to be a Debian system for -determining the installation location when B<-i> is used. - -=item F - -The install location of the generated configuration file on Debian. - -=item F - -If this file exists, the system is assumed to be a Red Hat system for -determining the installation location when B<-i> is used. - -=item F - -The install location of the generated configuration file on Red Hat. - -=back - -=head1 AUTHOR - -Russ Allbery -Digant C Kasundra - -=head1 SEE ALSO - -iptables(8) - -=cut diff --git a/chef/cookbooks/iptables/metadata.json b/chef/cookbooks/iptables/metadata.json index 72582b7..ea5c42d 100644 --- a/chef/cookbooks/iptables/metadata.json +++ b/chef/cookbooks/iptables/metadata.json @@ -1,5 +1,6 @@ { "name": "iptables", + "version": "0.13.2", "description": "Sets up iptables to use a script to maintain rules", "long_description": "", "maintainer": "Opscode, Inc.", @@ -9,7 +10,8 @@ "redhat": ">= 0.0.0", "centos": ">= 0.0.0", "debian": ">= 0.0.0", - "ubuntu": ">= 0.0.0" + "ubuntu": ">= 0.0.0", + "amazon": ">= 0.0.0" }, "dependencies": { }, @@ -29,6 +31,5 @@ }, "recipes": { "iptables": "Installs iptables and sets up .d style config directory of iptables rules" - }, - "version": "0.12.0" + } } \ No newline at end of file diff --git a/chef/cookbooks/iptables/metadata.rb b/chef/cookbooks/iptables/metadata.rb index 3fdfdf0..8c0d434 100644 --- a/chef/cookbooks/iptables/metadata.rb +++ b/chef/cookbooks/iptables/metadata.rb @@ -3,9 +3,9 @@ maintainer "Opscode, Inc." maintainer_email "cookbooks@opscode.com" license "Apache 2.0" description "Sets up iptables to use a script to maintain rules" -version "0.12.0" +version "0.13.2" recipe "iptables", "Installs iptables and sets up .d style config directory of iptables rules" -%w{ redhat centos debian ubuntu}.each do |os| +%w{redhat centos debian ubuntu amazon}.each do |os| supports os end diff --git a/chef/cookbooks/iptables/recipes/default.rb b/chef/cookbooks/iptables/recipes/default.rb index 2b52790..39b913f 100644 --- a/chef/cookbooks/iptables/recipes/default.rb +++ b/chef/cookbooks/iptables/recipes/default.rb @@ -18,7 +18,6 @@ # package "iptables" -package "perl" execute "rebuild-iptables" do command "/usr/sbin/rebuild-iptables" @@ -29,9 +28,12 @@ directory "/etc/iptables.d" do action :create end -cookbook_file "/usr/sbin/rebuild-iptables" do - source "rebuild-iptables" +template "/usr/sbin/rebuild-iptables" do + source "rebuild-iptables.erb" mode 0755 + variables( + :hashbang => ::File.exist?('/usr/bin/ruby') ? '/usr/bin/ruby' : '/opt/chef/embedded/bin/ruby' + ) end case node[:platform] diff --git a/chef/cookbooks/iptables/templates/default/rebuild-iptables.erb b/chef/cookbooks/iptables/templates/default/rebuild-iptables.erb new file mode 100644 index 0000000..f7325db --- /dev/null +++ b/chef/cookbooks/iptables/templates/default/rebuild-iptables.erb @@ -0,0 +1,131 @@ +#!<%= @hashbang %> -w + +# +# rebuild-iptables.rb -- Construct an iptables rules file from fragments. +# +# Written by Phil Cohen +# Copyright 2011, Phil Cohen +# +# Constructs an iptables rules file from the prefix, standard, and suffix +# files in the iptables configuration area, adding any additional modules +# specified in the command line, and prints the resulting iptables rules to +# standard output (suitable for saving into /var/lib/iptables or some other +# appropriate location on the system). + +############################################################################## +# Modules and declarations +############################################################################## + +# Path to the iptables template area. +TEMPLATE_PATH = "/etc/iptables.d" + +############################################################################## +# Installation +############################################################################## + +# Read in a file, processing includes as required. +def read_iptables(file, table = :filter) + file = File.join(TEMPLATE_PATH, file) unless File.dirname(file) =~ /iptables\.d/ + rule = File.readlines(file).map{ |line| line.chomp } + rule.each do |line| + if line =~ /^\s*include\s+(\S+)$/ + read_iptables($1, table) + elsif line =~ /^\s*\*([a-z]+)\s*$/ + table = $1.to_sym + elsif line =~ /^\s*:([-a-zA-Z0-9_]+)(?:\s+([A-Z]+(?:\s*\[.*?\])))?$/ + @data[table][:chains][$1] = $2 || '-' + elsif line !~ /^\s*COMMIT\s*$/ + #detect new chains + if chain = line.match(/\-[ADRILFZN]\s+([-a-zA-Z0-9_]+)\s/) + @data[table][:chains][chain[1]] ||= '-' + end + @data[table][:rules].push line + end + end +end + +# Write a file carefully. +def write_iptables(file, data) + File.open("#{file}.new", "w") { |f| f.write(data) } + File.rename("#{file}.new", file) +end + +# Install iptables on a Red Hat system. Takes the new iptables data. +def install_redhat(data) + write_iptables("/etc/sysconfig/iptables", data) + system("/sbin/service", "iptables", "restart") +end + +# Install iptables on a Debian system. Takes the new iptables data. +def install_debian(data) + Dir.mkdir("/etc/iptables") unless File.directory?("/etc/iptables") + write_iptables("/etc/iptables/general", data) + system("/sbin/iptables-restore < /etc/iptables/general") +end + +############################################################################## +# Main routine +############################################################################## + +@data = { + :filter => { + :chains => { + 'INPUT' => 'ACCEPT [0,0]', + 'FORWARD' => 'ACCEPT [0,0]', + 'OUTPUT' => 'ACCEPT [0,0]' + }, + :rules => [] + }, + :mangle => { + :chains => { + 'PREROUTING' => 'ACCEPT [0,0]', + 'INPUT' => 'ACCEPT [0,0]', + 'FORWARD' => 'ACCEPT [0,0]', + 'OUTPUT' => 'ACCEPT [0,0]', + 'POSTROUTING' => 'ACCEPT [0,0]' + }, + :rules => [] + }, + :nat => { + :chains => { + 'PREROUTING' => 'ACCEPT [0,0]', + 'POSTROUTING' => 'ACCEPT [0,0]', + 'OUTPUT' => 'ACCEPT [0,0]' + }, + :rules => [], + } +} + +templates = Dir["#{TEMPLATE_PATH}/*"].sort.delete_if do |template| + %w[prefix suffix postfix].include?(File.basename(template)) +end + +templates.unshift 'prefix' if File.exists? "#{TEMPLATE_PATH}/prefix" +templates.push 'suffix' if File.exists? "#{TEMPLATE_PATH}/suffix" +templates.push 'postfix' if File.exists? "#{TEMPLATE_PATH}/postfix" + +templates.each { |template| read_iptables(template) } + +iptables_rules = "" +@data.each do |table, table_data| + if table_data[:rules].any? + iptables_rules << "*#{table.to_s}\n" + table_data[:chains].each do |chain, rule| + iptables_rules << ":#{chain} #{rule}\n" + end + iptables_rules << table_data[:rules].join("\n") + iptables_rules << "\nCOMMIT\n" + end +end + +if File.exists?("/etc/debian_version") + install_debian(iptables_rules) +elsif File.exists?("/etc/redhat-release") + install_redhat(iptables_rules) +elsif File.exists?("/etc/system-release") # Amazon Linux + install_redhat(iptables_rules) +else + raise "#{$0}: cannot figure out whether this is Red Hat or Debian\n"; +end + +exit 0 diff --git a/chef/cookbooks/keepalived/attributes/default.rb b/chef/cookbooks/keepalived/attributes/default.rb index 710afd2..628a6d8 100644 --- a/chef/cookbooks/keepalived/attributes/default.rb +++ b/chef/cookbooks/keepalived/attributes/default.rb @@ -5,8 +5,8 @@ default['keepalived']['global']['smtp_server'] = '127.0.0.1' default['keepalived']['global']['smtp_connect_timeout'] = 30 default['keepalived']['global']['router_id'] = 'DEFAULT_ROUT_ID' default['keepalived']['global']['router_ids'] = { - "centos-10-145-88-152" => "lsb01", - "centos-10-145-88-153" => "lsb02" + # "centos-10-145-88-152" => "lsb01", + # "centos-10-145-88-153" => "lsb02" } # node name based mapping default['keepalived']['check_scripts'] = { "haproxy" => { @@ -19,22 +19,23 @@ default['keepalived']['instance_defaults']['state'] = 'MASTER' default['keepalived']['instance_defaults']['priority'] = 100 default['keepalived']['instance_defaults']['virtual_router_id'] = 10 default['keepalived']['vip'] = { - "eth0" => "10.145.88.161" + "ipaddress" => "127.0.0.1", + "interface" => "eth0" } default['keepalived']['instances'] = { "openstack" => { "virtual_router_id" => "50", "advert_int" => "1", "priorities" => { - "centos-10-145-88-152" => 110, - "centos-10-145-88-153" => 101 + # "centos-10-145-88-152" => 110, + # "centos-10-145-88-153" => 101 }, "states" => { - "centos-10-145-88-152" => "BACKUP", - "centos-10-145-88-153" => "MASTER" + # "centos-10-145-88-152" => "BACKUP", + # "centos-10-145-88-153" => "MASTER" }, - "interface" => "eth0", - "ip_addresses" => ["#{node['keepalived']['vip']['eth0']} dev eth0"], + "interface" => "#{node['keepalived']['vip']['interface']}", + "ip_addresses" => ["#{node['keepalived']['vip']['ipaddress']} dev #{node['keepalived']['vip']['interface']}"], "track_script" => "haproxy" } } diff --git a/chef/cookbooks/keepalived/libraries/default.rb b/chef/cookbooks/keepalived/libraries/default.rb new file mode 100644 index 0000000..aa3d508 --- /dev/null +++ b/chef/cookbooks/keepalived/libraries/default.rb @@ -0,0 +1,33 @@ + +def keepalived_master(role, tag, chef_environment = node.chef_environment) + chef_environment = chef_environment || node.chef_environment + master = search(:node, "run_list:role\\[#{role}\\] AND \ + chef_environment:#{chef_environment} AND \ + tags:#{tag}") || [] + master = master.sort_by { |node| node.name } unless master.empty? + if master.empty? + nodes = search(:node, "run_list:role\\[#{role}\\] AND \ + chef_environment:#{chef_environment}") || [] + if nodes.empty? + Chef::Log.error("Cannot find the role #{role} in the environment #{chef_environment}\n") + end + nodes = nodes.sort_by { |node| node.name } unless nodes.empty? + if node.name.eql?(nodes.first.name) + node.tags << tag unless node.tags.include?(tag) + node.save + end + return nodes.first + else + if master.length.eql?(1) + return master.first + else + head, *tail = master + for m in tail + print node,m + m.tags.delete(tag) + m.save + end + return head + end + end +end diff --git a/chef/cookbooks/keepalived/metadata.rb b/chef/cookbooks/keepalived/metadata.rb index 11088bd..d3df108 100644 --- a/chef/cookbooks/keepalived/metadata.rb +++ b/chef/cookbooks/keepalived/metadata.rb @@ -8,3 +8,7 @@ version "1.2.0" supports "ubuntu" recipe "keepalived", "Installs and configures keepalived" + +depends 'apt', '>= 2.3.8' +depends 'yum', '>= 3.1.4' +depends 'yum-epel', '>= 0.3.4' diff --git a/chef/cookbooks/keepalived/recipes/default.rb b/chef/cookbooks/keepalived/recipes/default.rb index 48875e5..60baed0 100644 --- a/chef/cookbooks/keepalived/recipes/default.rb +++ b/chef/cookbooks/keepalived/recipes/default.rb @@ -19,37 +19,25 @@ require 'chef/util/file_edit' -defaultbag = "openstack" -if !Chef::DataBag.list.key?(defaultbag) - Chef::Application.fatal!("databag '#{defaultbag}' doesn't exist.") - return -end - -myitem = node.attribute?('cluster')? node['cluster']:"env_default" - -if !search(defaultbag, "id:#{myitem}") - Chef::Application.fatal!("databagitem '#{myitem}' doesn't exist.") - return -end - -mydata = data_bag_item(defaultbag, myitem) - -if mydata['ha']['status'].eql?('enable') - mydata['ha']['keepalived']['router_ids'].each do |nodename, routerid| - node.override['keepalived']['global']['router_ids']["#{nodename}"] = routerid +# The following code block is trying to automaticly elect master node +# however it is not polished very well, currently only support two keepalived +# nodes. If you are going to build a keepalived cluster with 3 and up nodes, +# either poilish it or use your own recipe to handle the situation. +master_node = keepalived_master('os-ha', 'keepalived_default_master') +instance = node.set['keepalived']['instances']['openstack'] +router_ids = node.set['keepalived']['global']['router_ids'] +if node.name.eql?(master_node.name) + if instance['states']["#{node.name}"].empty? + router_ids["#{node.name}"] = 'lsb01' + instance['priorities']["#{node.name}"] = '110' + instance['states']["#{node.name}"] = 'MASTER' end - - mydata['ha']['keepalived']['instance_name']['priorities'].each do |nodename, priority| - node.override['keepalived']['instances']['openstack']['priorities']["#{nodename}"] = priority +else + if instance['states']["#{node.name}"].empty? + router_ids["#{node.name}"] = 'lsb02' + instance['priorities']["#{node.name}"] = '101' + instance['states']["#{node.name}"] = 'BACKUP' end - - mydata['ha']['keepalived']['instance_name']['states'].each do |nodename, status| - node.override['keepalived']['instances']['openstack']['states']["#{nodename}"] = status - end - - interface = node['keepalived']['instances']['openstack']['interface'] - node.override['keepalived']['instances']['openstack']['ip_addresses'] = [ - "#{mydata['ha']['keepalived']['instance_name']['vip']} dev #{interface}" ] end case node["platform_family"] @@ -86,7 +74,6 @@ if node['keepalived']['shared_address'] block do fe = Chef::Util::FileEdit.new('/etc/sysctl.conf') fe.search_file_delete_line(/^net.ipv4.ip_nonlocal_bind\s*=\s*0/) - fe.write_file fe.insert_line_if_no_match(/^net.ipv4.ip_nonlocal_bind\s*=s*1/, "net.ipv4.ip_nonlocal_bind = 1") fe.write_file diff --git a/chef/cookbooks/logrotate/.gitignore b/chef/cookbooks/logrotate/.gitignore new file mode 100644 index 0000000..1e25d19 --- /dev/null +++ b/chef/cookbooks/logrotate/.gitignore @@ -0,0 +1,26 @@ +*~ +*# +.#* +\#*# +.*.sw[a-z] +*.un~ +*.tmp +*.bk +*.bkup +.kitchen.local.yml +Berksfile.lock +Gemfile.lock + +.bundle/ +.cache/ +.kitchen/ +.vagrant/ +.vagrant.d/ +bin/ +tmp/ +vendor/ + +# RVM +.ruby-version +.ruby-gemset +.rvmrc \ No newline at end of file diff --git a/chef/cookbooks/logrotate/.kitchen.yml b/chef/cookbooks/logrotate/.kitchen.yml new file mode 100644 index 0000000..f5f5406 --- /dev/null +++ b/chef/cookbooks/logrotate/.kitchen.yml @@ -0,0 +1,20 @@ +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: + - name: ubuntu-12.04 + run_list: + - recipe[apt::default] + - name: centos-6.4 + +suites: + - name: default + run_list: + - recipe[logrotate::default] + - name: definition + run_list: + - recipe[fake::definition] + - name: global + run_list: + - recipe[logrotate::global] diff --git a/chef/cookbooks/logrotate/.rubocop.yml b/chef/cookbooks/logrotate/.rubocop.yml new file mode 100644 index 0000000..e11136d --- /dev/null +++ b/chef/cookbooks/logrotate/.rubocop.yml @@ -0,0 +1,14 @@ +AllCops: + Excludes: + - vendor/** + +AlignParameters: + Enabled: false +Encoding: + Enabled: false +HashSyntax: + Enabled: false +LineLength: + Enabled: false +MethodLength: + Max: 30 diff --git a/chef/cookbooks/logrotate/.travis.yml b/chef/cookbooks/logrotate/.travis.yml new file mode 100644 index 0000000..67836a1 --- /dev/null +++ b/chef/cookbooks/logrotate/.travis.yml @@ -0,0 +1,9 @@ +rvm: + - 1.9.3 + - 2.0.0 +before_script: + - bundle exec berks install +script: + - bundle exec foodcritic -f any . --tags ~FC015 + - bundle exec rspec --color --format progress + - bundle exec rubocop diff --git a/chef/cookbooks/logrotate/Berksfile b/chef/cookbooks/logrotate/Berksfile new file mode 100644 index 0000000..c4543c7 --- /dev/null +++ b/chef/cookbooks/logrotate/Berksfile @@ -0,0 +1,7 @@ +site :opscode +metadata + +group :integration do + cookbook 'apt', '~> 2.0' + cookbook 'fake', path: 'test/fixtures/cookbooks/fake' +end diff --git a/chef/cookbooks/logrotate/CHANGELOG.md b/chef/cookbooks/logrotate/CHANGELOG.md new file mode 100644 index 0000000..043738a --- /dev/null +++ b/chef/cookbooks/logrotate/CHANGELOG.md @@ -0,0 +1,65 @@ +logrotate Cookbook CHANGELOG +============================ +This file is used to list changes made in each version of the logrotate cookbook. + +v1.5.0 +------ + +### Bugs +- Fix missing end tag in template +- Don't re-initialize constants. +- Fix rubocop finding + +### Improvements +- [COOK-3911] Allow to use maxsize parameter. +- [COOK-4000] Allow to use dateyesterday option. +- [COOK-4024] Allow to use su parameter. +- [COOK-4175] Allows use of the dateformat parameter. +- Loosen test-kitchen version constraint +- Add rvm files to gitignore + + +v1.4.0 +------ +### Bug +- **[COOK-3632](https://tickets.opscode.com/browse/COOK-3632)** - Raise Exception when adding more than one invalid option +- **[COOK-3141](https://tickets.opscode.com/browse/COOK-3141)** - Do not duplicate template entires for multiple paths +- **[COOK-3034](https://tickets.opscode.com/browse/COOK-3034)** - Update logrotate_app params to accept arrays and strings + +### Improvement +- **[COOK-2646](https://tickets.opscode.com/browse/COOK-2646)** - Add ability to choose file mode for logrotate template + +v1.3.0 +------ +### Improvement +- **[COOK-3341](https://tickets.opscode.com/browse/COOK-3341)** - Add optional `frequency` and `rotate` params when defined globally +- **[COOK-3298](https://tickets.opscode.com/browse/COOK-3298)** - Use `Array` instead of `respond_to?(:each)` +- **[COOK-3285](https://tickets.opscode.com/browse/COOK-3285)** - Change `logrotate.d` config file mode to `0644` +- **[COOK-3250](https://tickets.opscode.com/browse/COOK-3250)** - Add `minsize` + +### Bug +- **[COOK-3274](https://tickets.opscode.com/browse/COOK-3274)** - Fix README typo that suggested the opposite action + +### New Feature +- **[COOK-2923](https://tickets.opscode.com/browse/COOK-2923)** - Add `olddir` option +- **[COOK-1651](https://tickets.opscode.com/browse/COOK-1651)** - Add `dateext` ability + +v1.2.2 +----- +### Bug +- [COOK-2872]: Add firstaction/lastaction ability to logrotate +- [COOK-2908]: Argument error in `logrotate_app` definition + +v1.2.0 +----- +- [COOK-2401] - Add the ability to manage the global logrotate configuration + +v1.1.0 +----- +- [COOK-2218] - Logrotate size parameter + +v1.0.2 +----- +- [COOK-1027] - Add support for pre-/post-rotate commands +- [COOK-1338] - Update log rotate for more flexibility of rotate options +- [COOK-1598] - "Create" isn't a mandatory option diff --git a/chef/cookbooks/logrotate/CONTRIBUTING.md b/chef/cookbooks/logrotate/CONTRIBUTING.md new file mode 100644 index 0000000..1ff72bb --- /dev/null +++ b/chef/cookbooks/logrotate/CONTRIBUTING.md @@ -0,0 +1,16 @@ +## Contribution Guidelines + +- Please submit improvements and bug fixes via Github pull requests or + by sending an email to steve@getchef.com in git's format-patch + format. + +- All patches should have well-written commit message. The first line + should summarize the change while the rest of the commit message + should explain the reason the change is needed. + +- Please ensure all tests and lint checking pass before submitting + pull requests. + +## Testing + +Please read TESTING.md for details on testing this cookbook. diff --git a/chef/cookbooks/logrotate/Gemfile b/chef/cookbooks/logrotate/Gemfile new file mode 100644 index 0000000..3b01690 --- /dev/null +++ b/chef/cookbooks/logrotate/Gemfile @@ -0,0 +1,11 @@ +source 'https://rubygems.org' + +gem 'berkshelf', '~> 2.0' +gem 'chefspec', '~> 2.0' +gem 'foodcritic', '~> 3.0' +gem 'rubocop', '~> 0.12' + +group :integration do + gem 'test-kitchen', '~> 1.0' + gem 'kitchen-vagrant', '~> 0.11' +end diff --git a/chef/cookbooks/apache2/LICENSE b/chef/cookbooks/logrotate/LICENSE similarity index 100% rename from chef/cookbooks/apache2/LICENSE rename to chef/cookbooks/logrotate/LICENSE diff --git a/chef/cookbooks/logrotate/README.md b/chef/cookbooks/logrotate/README.md new file mode 100644 index 0000000..306b90b --- /dev/null +++ b/chef/cookbooks/logrotate/README.md @@ -0,0 +1,168 @@ +logrotate Cookbook +================== +[![Build Status](https://secure.travis-ci.org/stevendanna/logrotate.png?branch=master)](http://travis-ci.org/stevendanna/logrotate) + +Manages the logrotate package and provides a definition to manage application specific logrotate configuration. + + +Requirements +------------ +Should work on any platform that includes a 'logrotate' package and writes logrotate configuration to /etc/logrotate.d. Tested on Ubuntu, Debian and Red Hat/CentOS. + + +Recipes +------- +### global +Generates and controls a global `/etc/logrotate.conf` file that will include additional files generated by the `logrotate_app` definition (see below). The contents of the configuration file is controlled through node attributes under `node['logrotate']['global']`. The default attributes are based on the configuration from the Ubuntu logrotate package. + +To define a valueless directive (e.g. `compress`, `copy`) simply add an attribute named for the directive with a truthy value : + +```ruby +node['logrotate']['global']['compress'] = 'any value here' +``` + +Note that defining a valueless directive with a falsey value will not make it false, but will remove it: + +```ruby +# Removes a defaulted 'compress' directive; does not add a 'nocompress' directive. +node.override['logrotate']['global']['compress'] = false +``` + +To fully overrride a booleanish directive like `compress`, you should probably remove the positive form and add the negative form: + +```ruby +node.override['logrotate']['global']['compress'] = false +node.override['logrotate']['global']['nocompress'] = true +``` + +The same is true of frequency directives; to be certain the frequency directive you want is included in the global configuration, you should override the ones you don't want as false: + +```ruby +%w[ daily, weekly, yearly ].do |freq| + node.override['logrotate']['global'][freq] = false +end +node.override['logrotate']['global']['monthly'] = true +``` + +To define a parameter with a value (e.g. `create`, `mail`) add an attribute with the desired value: + +```ruby +node['logrotate']['global']['create'] = '0644 root adm' +``` + +To define a path stanza in the global configuration (generally unneeded because of the `logrotate_app` definition) just add an attribute with the path as the name and a hash containing directives and parameters as described above: + +```ruby +node['logrotate']['global']['/var/log/wtmp'] = { + 'missingok' => true, + 'monthly' => true, + 'create' => '0660 root utmp', + 'rotate' => 1 +} +``` + +`firstaction`, `prerotate`, `postrotate`, and `lastaction` scripts can be defined either as arrays of the lines to put in the script or multiline strings: + +```ruby +node['logrotate']['global']['/var/log/foo/*.log'] = { + 'missingok' => true, + 'monthly' => true, + 'create' => '0660 root adm', + 'rotate' => 1, + 'prerotate' => ['service foo start_rotate', 'logger started foo service log rotation'], + 'postrotate' => <<-EOF + service foo end_rotate + logger completed foo service log rotation + EOF +} +``` + + +Definitions +----------- +### logrotate_app +This definition can be used to drop off customized logrotate config files on a per application basis. + +The definition takes the following params: + +- `path`: specifies a single path (string) or multiple paths (array) that should have logrotation stanzas created in the config file. No default, this must be specified. +- `enable`: true/false, if true it will create the template in /etc/logrotate.d. +- `frequency`: sets the frequency for rotation. Default value is 'weekly'. Valid values are: daily, weekly, monthly, yearly, see the logrotate man page for more information. +- `dateformat`: specifies date extension with %Y, %m, %d, and %s. The default value is -%Y%m%d. +- `size`: Log files are rotated when they grow bigger than size bytes. +- `maxsize`: Log files are rotated when they grow bigger than size bytes even before the additionally specified time interval. +- `su`: Rotate log files set under this user and group instead of using default user/group. +- `template`: sets the template source, default is "logrotate.erb". +- `template_mode`: the mode to create the logrotate template with (default "0440") +- `template_owner`: the owner of the logrotate template (default "root") +- `template_group`: the group of the logrotate template (default "root") +- `cookbook`: select the template source from the specified cookbook. By default it will use the cookbook where the definition is used. +- `create`: creation parameters for the logrotate "create" config, follows the form "mode owner group". This is an optional parameter, and is nil by default. +- `postrotate`: lines to be executed after the log file is rotated +- `prerotate`: lines to be executed before the log file is rotated +- `sharedscripts`: if true, the sharedscripts options is specified which makes sure prescript and postscript commands are run only once (even if multiple files match the path) + + +Usage +----- +The default recipe will ensure logrotate is always up to date. + +To create application specific logrotate configs, use the `logrotate_app` definition. For example, to rotate logs for a tomcat application named myapp that writes its log file to `/var/log/tomcat/myapp.log`: + +```ruby +logrotate_app 'tomcat-myapp' do + cookbook 'logrotate' + path '/var/log/tomcat/myapp.log' + frequency 'daily' + rotate 30 + create '644 root adm' +end +``` + +To rotate multiple logfile paths, specify the path as an array: + +```ruby +logrotate_app 'tomcat-myapp' do + cookbook 'logrotate' + path ['/var/log/tomcat/myapp.log', '/opt/local/tomcat/catalina.out'] + frequency 'daily' + create '644 root adm' + rotate 7 +end +``` + +To specify which logrotate options, specify the options as an array: + +```ruby +logrotate_app 'tomcat-myapp' do + cookbook 'logrotate' + path '/var/log/tomcat/myapp.log' + options ['missingok', 'delaycompress', 'notifempty'] + frequency 'daily' + rotate 30 + create '644 root adm' +end +``` + + +License & Authors +----------------- +- Author:: Scott M. Likens () +- Author:: Joshua Timberman () + +```text +Copyright 2009, Scott M. Likens +Copyright 2011-2012, Opscode, Inc. + +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. +``` diff --git a/chef/cookbooks/logrotate/TESTING.md b/chef/cookbooks/logrotate/TESTING.md new file mode 100644 index 0000000..16d40bd --- /dev/null +++ b/chef/cookbooks/logrotate/TESTING.md @@ -0,0 +1,49 @@ +This cookbook uses a variety of testing components: + +- Unit tests: [ChefSpec](https://github.com/acrmp/chefspec) +- Integration tests: [Test Kitchen](https://github.com/opscode/test-kitchen) +- Chef Style lints: [Foodcritic](https://github.com/acrmp/foodcritic) +- Ruby Style lints: [Rubocop](https://github.com/bbatsov/rubocop) + + +Prerequisites +------------- +To develop on this cookbook, you must have a sane Ruby 1.9+ environment. Given the nature of this installation process (and it's variance across multiple operating systems), we will leave this installation process to the user. + +You must also have `bundler` installed: + + $ gem install bundler + +You must also have Vagrant and VirtualBox installed: + +- [Vagrant](https://vagrantup.com) +- [VirtualBox](https://virtualbox.org) + +Once installed, you must install the `vagrant-berkshelf` plugin: + + $ vagrant plugin install vagrant-berkshelf + + +Development +----------- +1. Clone the git repository from GitHub: + + $ git clone git@github.com:stevendanna/logrotate.git + +2. Install the dependencies using bundler: + + $ bundle install + +3. Create a branch for your changes: + + $ git checkout -b my_bug_fix + +4. Make any changes +5. Write tests to support those changes. It is highly recommended you write both unit and integration tests. +6. Run the tests: + - `bundle exec rspec` + - `bundle exec foodcritic .` + - `bundle exec rubocop` + - `bundle exec kitchen test` + +7. Assuming the tests pass, open a Pull Request on GitHub diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/attributes/default.rb b/chef/cookbooks/logrotate/attributes/default.rb similarity index 56% rename from chef/cookbooks/mysql/test/cookbooks/mysql_test/attributes/default.rb rename to chef/cookbooks/logrotate/attributes/default.rb index b2dbc85..df44106 100644 --- a/chef/cookbooks/mysql/test/cookbooks/mysql_test/attributes/default.rb +++ b/chef/cookbooks/logrotate/attributes/default.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: mysql_test -# Attributes:: default +# Cookbook Name:: logrotate +# Attribute:: default # -# Copyright 2012, Opscode, Inc. +# Copyright 2013, Opscode # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,11 +17,22 @@ # limitations under the License. # -# Must be specified for chef-solo for successful re-converge -override['mysql']['server_root_password'] = 'e;br$ilRoj7' +default['logrotate']['global'] = { + 'weekly' => true, + 'rotate' => 4, + 'create' => '', -default['mysql_test']['database'] = 'mysql_test' -default['mysql_test']['username'] = 'test_user' -default['mysql_test']['password'] = 'neshFiapog' + '/var/log/wtmp' => { + 'missingok' => true, + 'monthly' => true, + 'create' => '0664 root utmp', + 'rotate' => 1 + }, -override['mysql']['bind_address'] = 'localhost' + '/var/log/btmp' => { + 'missingok' => true, + 'monthly' => true, + 'create' => '0660 root utmp', + 'rotate' => 1 + } +} diff --git a/chef/cookbooks/logrotate/definitions/logrotate_app.rb b/chef/cookbooks/logrotate/definitions/logrotate_app.rb new file mode 100644 index 0000000..f3ae0ff --- /dev/null +++ b/chef/cookbooks/logrotate/definitions/logrotate_app.rb @@ -0,0 +1,79 @@ +# +# Cookbook Name:: logrotate +# Definition:: logrotate_instance +# +# Copyright 2009, Scott M. Likens +# +# 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. +# + +log_rotate_params = { + :enable => true, + :frequency => 'weekly', + :template => 'logrotate.erb', + :cookbook => 'logrotate', + :template_mode => '0440', + :template_owner => 'root', + :template_group => 'root', + :postrotate => nil, + :prerotate => nil, + :firstaction => nil, + :lastaction => nil, + :sharedscripts => false +} + +define(:logrotate_app, log_rotate_params) do + include_recipe 'logrotate::default' + + acceptable_options = %w(missingok compress delaycompress dateext dateyesterday copytruncate notifempty delaycompress ifempty mailfirst nocompress nocopy nocopytruncate nocreate nodelaycompress nomail nomissingok noolddir nosharedscripts notifempty sharedscripts) + options_tmp = params[:options] ||= %w(missingok compress delaycompress copytruncate notifempty) + options = options_tmp.respond_to?(:each) ? options_tmp : options_tmp.split + + if params[:enable] + invalid_options = options - acceptable_options + unless invalid_options.empty? + Chef::Application.fatal! "The passed value(s) [#{invalid_options.join(',')}] are not valid" + end + + template "/etc/logrotate.d/#{params[:name]}" do + source params[:template] + cookbook params[:cookbook] + mode params[:template_mode] + owner params[:template_owner] + group params[:template_group] + backup false + variables( + :path => Array(params[:path]).map { |path| path.to_s.inspect }.join(' '), + :create => params[:create], + :frequency => params[:frequency], + :dateformat => params[:dateformat], + :size => params[:size], + :minsize => params[:minsize], + :maxsize => params[:maxsize], + :su => params[:su], + :rotate => params[:rotate], + :olddir => params[:olddir], + :sharedscripts => params[:sharedscripts], + :postrotate => Array(params[:postrotate]).join("\n"), + :prerotate => Array(params[:prerotate]).join("\n"), + :firstaction => Array(params[:firstaction]).join("\n"), + :lastaction => Array(params[:lastaction]).join("\n"), + :options => options + ) + end + else + file "/etc/logrotate.d/#{params[:name]}" do + action :delete + end + end +end diff --git a/chef/cookbooks/logrotate/libraries/logrotate_config.rb b/chef/cookbooks/logrotate/libraries/logrotate_config.rb new file mode 100644 index 0000000..e35e549 --- /dev/null +++ b/chef/cookbooks/logrotate/libraries/logrotate_config.rb @@ -0,0 +1,93 @@ +# +# Cookbook Name:: logrotate +# Library:: CookbookLogrotate +# +# Copyright 2013, Opscode +# +# 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. +# + +# Helper module for Logrotate configuration +module CookbookLogrotate + DIRECTIVES = %w[ + compress copy copytruncate daily dateext + delaycompress ifempty mailfirst maillast missingok + monthly nocompress nocopy nocopytruncate nocreate + nodelaycompress nodateext nomail nomissingok noolddir + nosharedscripts noshred notifempty sharedscripts shred + weekly yearly + ] unless const_defined?(:DIRECTIVES) + + VALUES = %w[ + compresscmd uncompresscmd compressext compressoptions + create dateformat include mail + maxage minsize rotate size + shredcycles start tabooext + ] unless const_defined?(:VALUES) + + SCRIPTS = %w[firstaction prerotate postrotate lastaction] unless const_defined?(:SCRIPTS) + + DIRECTIVES_AND_VALUES = DIRECTIVES + VALUES unless const_defined?(:DIRECTIVES_AND_VALUES) + + # Helper class for creating configurations + class LogrotateConfiguration + attr_reader :directives, :values, :paths + + class << self + def from_hash(hash) + new(hash) + end + + def directives_from(hash) + hash.select { |k, v| DIRECTIVES.include?(k) && v }.keys + end + + def values_from(hash) + hash.select { |k| VALUES.include?(k) } + end + + def paths_from(hash) + hash.select { |k| !(DIRECTIVES_AND_VALUES.include?(k)) }.reduce({}) do | accum_paths, (path, config) | + accum_paths[path] = { + 'directives' => directives_from(config), + 'values' => values_from(config), + 'scripts' => scripts_from(config) + } + + accum_paths + end + end + + def scripts_from(hash) + defined_scripts = hash.select { |k| SCRIPTS.include?(k) } + defined_scripts.reduce({}) do | accum_scripts, (script, lines) | + if lines.respond_to?(:join) + accum_scripts[script] = lines.join("\n") + else + accum_scripts[script] = lines + end + + accum_scripts + end + end + end + + private + + def initialize(hash) + @directives = LogrotateConfiguration.directives_from(hash) + @values = LogrotateConfiguration.values_from(hash) + @paths = LogrotateConfiguration.paths_from(hash) + end + end +end diff --git a/chef/cookbooks/logrotate/metadata.json b/chef/cookbooks/logrotate/metadata.json new file mode 100644 index 0000000..8d16423 --- /dev/null +++ b/chef/cookbooks/logrotate/metadata.json @@ -0,0 +1 @@ +{"name":"logrotate","description":"Installs logrotate package and provides a definition for logrotate configs","long_description":"logrotate Cookbook\n==================\n[![Build Status](https://secure.travis-ci.org/stevendanna/logrotate.png?branch=master)](http://travis-ci.org/stevendanna/logrotate)\n\nManages the logrotate package and provides a definition to manage application specific logrotate configuration.\n\n\nRequirements\n------------\nShould work on any platform that includes a 'logrotate' package and writes logrotate configuration to /etc/logrotate.d. Tested on Ubuntu, Debian and Red Hat/CentOS.\n\n\nRecipes\n-------\n### global\nGenerates and controls a global `/etc/logrotate.conf` file that will include additional files generated by the `logrotate_app` definition (see below). The contents of the configuration file is controlled through node attributes under `node['logrotate']['global']`. The default attributes are based on the configuration from the Ubuntu logrotate package.\n\nTo define a valueless directive (e.g. `compress`, `copy`) simply add an attribute named for the directive with a truthy value :\n\n```ruby\nnode['logrotate']['global']['compress'] = 'any value here'\n```\n\nNote that defining a valueless directive with a falsey value will not make it false, but will remove it:\n\n```ruby\n# Removes a defaulted 'compress' directive; does not add a 'nocompress' directive.\nnode.override['logrotate']['global']['compress'] = false\n```\n\nTo fully overrride a booleanish directive like `compress`, you should probably remove the positive form and add the negative form:\n\n```ruby\nnode.override['logrotate']['global']['compress'] = false\nnode.override['logrotate']['global']['nocompress'] = true\n```\n\nThe same is true of frequency directives; to be certain the frequency directive you want is included in the global configuration, you should override the ones you don't want as false:\n\n```ruby\n%w[ daily, weekly, yearly ].do |freq|\n node.override['logrotate']['global'][freq] = false\nend\nnode.override['logrotate']['global']['monthly'] = true\n```\n\nTo define a parameter with a value (e.g. `create`, `mail`) add an attribute with the desired value:\n\n```ruby\nnode['logrotate']['global']['create'] = '0644 root adm'\n```\n\nTo define a path stanza in the global configuration (generally unneeded because of the `logrotate_app` definition) just add an attribute with the path as the name and a hash containing directives and parameters as described above:\n\n```ruby\nnode['logrotate']['global']['/var/log/wtmp'] = {\n 'missingok' => true,\n 'monthly' => true,\n 'create' => '0660 root utmp',\n 'rotate' => 1\n}\n```\n\n`firstaction`, `prerotate`, `postrotate`, and `lastaction` scripts can be defined either as arrays of the lines to put in the script or multiline strings:\n\n```ruby\nnode['logrotate']['global']['/var/log/foo/*.log'] = {\n 'missingok' => true,\n 'monthly' => true,\n 'create' => '0660 root adm',\n 'rotate' => 1,\n 'prerotate' => ['service foo start_rotate', 'logger started foo service log rotation'],\n 'postrotate' => <<-EOF\n service foo end_rotate\n logger completed foo service log rotation\n EOF\n}\n```\n\n\nDefinitions\n-----------\n### logrotate_app\nThis definition can be used to drop off customized logrotate config files on a per application basis.\n\nThe definition takes the following params:\n\n- `path`: specifies a single path (string) or multiple paths (array) that should have logrotation stanzas created in the config file. No default, this must be specified.\n- `enable`: true/false, if true it will create the template in /etc/logrotate.d.\n- `frequency`: sets the frequency for rotation. Default value is 'weekly'. Valid values are: daily, weekly, monthly, yearly, see the logrotate man page for more information.\n- `dateformat`: specifies date extension with %Y, %m, %d, and %s. The default value is -%Y%m%d.\n- `size`: Log files are rotated when they grow bigger than size bytes.\n- `maxsize`: Log files are rotated when they grow bigger than size bytes even before the additionally specified time interval.\n- `su`: Rotate log files set under this user and group instead of using default user/group.\n- `template`: sets the template source, default is \"logrotate.erb\".\n- `template_mode`: the mode to create the logrotate template with (default \"0440\")\n- `template_owner`: the owner of the logrotate template (default \"root\")\n- `template_group`: the group of the logrotate template (default \"root\")\n- `cookbook`: select the template source from the specified cookbook. By default it will use the cookbook where the definition is used.\n- `create`: creation parameters for the logrotate \"create\" config, follows the form \"mode owner group\". This is an optional parameter, and is nil by default.\n- `postrotate`: lines to be executed after the log file is rotated\n- `prerotate`: lines to be executed before the log file is rotated\n- `sharedscripts`: if true, the sharedscripts options is specified which makes sure prescript and postscript commands are run only once (even if multiple files match the path)\n\n\nUsage\n-----\nThe default recipe will ensure logrotate is always up to date.\n\nTo create application specific logrotate configs, use the `logrotate_app` definition. For example, to rotate logs for a tomcat application named myapp that writes its log file to `/var/log/tomcat/myapp.log`:\n\n```ruby\nlogrotate_app 'tomcat-myapp' do\n cookbook 'logrotate'\n path '/var/log/tomcat/myapp.log'\n frequency 'daily'\n rotate 30\n create '644 root adm'\nend\n```\n\nTo rotate multiple logfile paths, specify the path as an array:\n\n```ruby\nlogrotate_app 'tomcat-myapp' do\n cookbook 'logrotate'\n path ['/var/log/tomcat/myapp.log', '/opt/local/tomcat/catalina.out']\n frequency 'daily'\n create '644 root adm'\n rotate 7\nend\n```\n\nTo specify which logrotate options, specify the options as an array:\n\n```ruby\nlogrotate_app 'tomcat-myapp' do\n cookbook 'logrotate'\n path '/var/log/tomcat/myapp.log'\n options ['missingok', 'delaycompress', 'notifempty']\n frequency 'daily'\n rotate 30\n create '644 root adm'\nend\n```\n\n\nLicense & Authors\n-----------------\n- Author:: Scott M. Likens ()\n- Author:: Joshua Timberman ()\n\n```text\nCopyright 2009, Scott M. Likens\nCopyright 2011-2012, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","maintainer":"Opscode, Inc.","maintainer_email":"cookbooks@opscode.com","license":"Apache 2.0","platforms":{"amazon":">= 0.0.0","centos":">= 0.0.0","debian":">= 0.0.0","fedora":">= 0.0.0","redhat":">= 0.0.0","scientific":">= 0.0.0","ubuntu":">= 0.0.0"},"dependencies":{},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{"logrotate_app":">= 0.0.0"},"replacing":{},"attributes":{},"groupings":{},"recipes":{"logrotate":"Installs logrotate package"},"version":"1.5.0"} \ No newline at end of file diff --git a/chef/cookbooks/logrotate/metadata.rb b/chef/cookbooks/logrotate/metadata.rb new file mode 100644 index 0000000..32680c7 --- /dev/null +++ b/chef/cookbooks/logrotate/metadata.rb @@ -0,0 +1,18 @@ +name 'logrotate' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Installs logrotate package and provides a definition for logrotate configs' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '1.5.0' + +recipe 'logrotate', 'Installs logrotate package' +provides 'logrotate_app' + +supports 'amazon' +supports 'centos' +supports 'debian' +supports 'fedora' +supports 'redhat' +supports 'scientific' +supports 'ubuntu' diff --git a/chef/cookbooks/openstack-block-storage/recipes/default.rb b/chef/cookbooks/logrotate/recipes/default.rb similarity index 86% rename from chef/cookbooks/openstack-block-storage/recipes/default.rb rename to chef/cookbooks/logrotate/recipes/default.rb index e42044e..f06d682 100644 --- a/chef/cookbooks/openstack-block-storage/recipes/default.rb +++ b/chef/cookbooks/logrotate/recipes/default.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: openstack-block-storage +# Cookbook Name:: logrotate # Recipe:: default # -# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2009-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,3 +16,5 @@ # See the License for the specific language governing permissions and # limitations under the License. # + +package 'logrotate' diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng_test.rb b/chef/cookbooks/logrotate/recipes/global.rb similarity index 61% rename from chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng_test.rb rename to chef/cookbooks/logrotate/recipes/global.rb index f3cdce5..bebf5c9 100644 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng_test.rb +++ b/chef/cookbooks/logrotate/recipes/global.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: apt_test +# Cookbook Name:: logrotate # Recipe:: default # -# Copyright 2012, Opscode, Inc. +# Copyright 2009-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,12 +17,14 @@ # limitations under the License. # -require File.expand_path('../support/helpers', __FILE__) +include_recipe 'logrotate::default' -describe "apt_test::default" do - include Helpers::AptTest +parsed_configuration = CookbookLogrotate::LogrotateConfiguration.from_hash(node['logrotate']['global'].to_hash) - it 'runs the cacher service' do - service("apt-cacher-ng").must_be_running - end +template '/etc/logrotate.conf' do + source 'logrotate-global.erb' + mode '0644' + variables( + :configuration => parsed_configuration + ) end diff --git a/chef/cookbooks/logrotate/templates/default/logrotate-global.erb b/chef/cookbooks/logrotate/templates/default/logrotate-global.erb new file mode 100644 index 0000000..96ba504 --- /dev/null +++ b/chef/cookbooks/logrotate/templates/default/logrotate-global.erb @@ -0,0 +1,29 @@ +# This file was generated by Chef for <%= node['fqdn'] %>. +# Do not modify this file by hand! + +<% @configuration.directives.each do |d| -%> +<%= d %> +<% end -%> + +<% @configuration.values.each do |k, v| -%> +<%= k %> <%= v %> +<% end -%> + +include /etc/logrotate.d + +<% @configuration.paths.each do |path, path_config| -%> +<%= path %> { + <% path_config['directives'].each do |d|-%> + <%= d %> + <% end -%> + <% path_config['values'].each do | k, v | -%> + <%= k %> <%= v %> + <% end -%> + <% path_config['scripts'].each do | scripttype, body | -%> + <%= scripttype %> + <%= body %> + endscript + <% end -%> +} + +<% end -%> diff --git a/chef/cookbooks/logrotate/templates/default/logrotate.erb b/chef/cookbooks/logrotate/templates/default/logrotate.erb new file mode 100644 index 0000000..2c776e9 --- /dev/null +++ b/chef/cookbooks/logrotate/templates/default/logrotate.erb @@ -0,0 +1,58 @@ +# This file was generated by Chef for <%= node['fqdn'] %>. +# Do not modify this file by hand! + +<%= @path %> { +<%- if @frequency %> + <%= @frequency %> +<%- end %> +<%- if @dateformat %> + dateformat <%= @dateformat %> +<%- end %> +<%- if @size %> + size <%= @size %> +<%- end %> +<%- if @minsize %> + minsize <%= @minsize %> +<%- end %> +<%- if @maxsize %> + maxsize <%= @maxsize %> +<%- end %> +<%- if @su %> + su <%= @su %> +<%- end %> +<%- if @rotate %> + rotate <%= @rotate %> +<%- end %> +<%- if @create %> + create <%= @create %> +<%- end %> +<%- if @olddir %> + olddir <%= @olddir %> +<%- end %> +<% @options.each do |o| -%> + <%= o %> +<% end -%> +<%- if @sharedscripts %> + sharedscripts +<%- end %> +<%- unless @postrotate.empty? %> + postrotate +<%= @postrotate %> + endscript +<%- end %> +<%- unless @prerotate.empty? %> + prerotate +<%= @prerotate %> + endscript +<%- end %> +<%- unless @firstaction.empty? %> + firstaction +<%= @firstaction %> + endscript +<%- end %> +<%- unless @lastaction.empty? %> + lastaction +<%= @lastaction %> + endscript +<%- end %> +} diff --git a/chef/cookbooks/memcached/.kitchen.yml b/chef/cookbooks/memcached/.kitchen.yml deleted file mode 100644 index b6c2c55..0000000 --- a/chef/cookbooks/memcached/.kitchen.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: - - recipe[apt] - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - -suites: -- name: default - run_list: - - recipe[memcached] - attributes: {} - -- name: instance - run_list: - - recipe[memcached] - - recipe[memcached_test::instance] diff --git a/chef/cookbooks/memcached/Berksfile b/chef/cookbooks/memcached/Berksfile deleted file mode 100644 index 502b147..0000000 --- a/chef/cookbooks/memcached/Berksfile +++ /dev/null @@ -1,9 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook "apt" - cookbook "yum" - cookbook "memcached_test", :path => "./test/cookbooks/memcached_test" -end diff --git a/chef/cookbooks/memcached/CHANGELOG.md b/chef/cookbooks/memcached/CHANGELOG.md index e71b6ca..a218b36 100644 --- a/chef/cookbooks/memcached/CHANGELOG.md +++ b/chef/cookbooks/memcached/CHANGELOG.md @@ -3,6 +3,42 @@ memcached Cookbook CHANGELOG This file is used to list changes made in each version of the memcached cookbook. +v1.7.2 (2014-03-12) +------------------- +- [COOK-4308] - Enable memcache on RHEL, Fedora, and Suse +- [COOK-4212] - Support max_object_size rhel and fedora + + +v1.7.0 +------ +Updating for yum ~> 3.0. +Fixing up style issues for rubocop. +Updating test-kitchen harness + + +v1.6.6 +------ +fixing metadata version error. locking to 3.0 + + +v1.6.4 +------ +Locking yum dependency to '< 3' + + +v1.6.2 +------ +[COOK-3741] UDP settings for memcached + + +v1.6.0 +------ +### Bug +- **[COOK-3682](https://tickets.opscode.com/browse/COOK-3682)** - Set user when using Debian packages + +### Improvement +- **[COOK-3336](https://tickets.opscode.com/browse/COOK-3336)** - Add an option to specify the logfile (fix) + v1.5.0 ------ ### Improvement diff --git a/chef/cookbooks/memcached/CONTRIBUTING b/chef/cookbooks/memcached/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/memcached/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/memcached/Gemfile b/chef/cookbooks/memcached/Gemfile deleted file mode 100644 index 46e0766..0000000 --- a/chef/cookbooks/memcached/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source :rubygems - -gem 'test-kitchen', '< 1.0' diff --git a/chef/cookbooks/memcached/LICENSE b/chef/cookbooks/memcached/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/memcached/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/memcached/README.md b/chef/cookbooks/memcached/README.md index 8c41980..e15ccab 100644 --- a/chef/cookbooks/memcached/README.md +++ b/chef/cookbooks/memcached/README.md @@ -1,71 +1,61 @@ -Description -=========== +memcached Cookbook +================== +[![Build Status](https://secure.travis-ci.org/opscode-cookbooks/memcached.png?branch=master)](http://travis-ci.org/opscode-cookbooks/memcached) + + +Installs memcached and provides a define to set up an instance of memcache via runit. -Installs memcached and provides a define to set up an instance of -memcache via runit. Requirements -============ +------------ +A runit service can be set up for instances using the `memcache_instance` definition. -A runit service can be set up for instances using the -`memcache_instance` definition. +### Platforms +- Ubuntu 10.04, 12.04 +- CentOS 5.8, 6.3 +- openSUSE 12.3 +- SLES 12 SP2 +- SmartOS base64 1.8.1 - Note that SMF directly configures memcached with no opportunity to alter settings. If you need custom parameters, use the `memcached_instance` provider instead. -## Platforms: +May work on other systems with or without modification. -Tested on: +### Cookbooks +- runit -* Ubuntu 10.04, 12.04 -* CentOS 5.8, 6.3 -* openSUSE 12.3 -* SLES 12 SP2 -* SmartOS base64 1.8.1 - * Note that SMF directly configures memcached with no opportunity to alter settings. - If you need custom parameters, use the `memcached_instance` provider instead. - -May work on any Debian or Red Hat family distributions with or without -modification. - -## Cookbooks: - -* runit Attributes -========== +---------- +The following are node attributes passed to the template for the runit service. -The following are node attributes passed to the template for the runit -service. +- `memcached['memory']` - maximum memory for memcached instances. +- `memcached['user']` - user to run memcached as. +- `memcached['port']` - TCP port for memcached to listen on. +- `memcached['udp_port']` - UDP port for memcached to listen on. +- `memcached['listen']` - IP address for memcache to listen on, defaults to **0.0.0.0** (world accessible). +- `memcached['maxconn']` - maximum number of connections to accept (defaults to 1024) +- `memcached['max_object_size']` - maximum size of an object to cache (defaults to 1MB) +- `memcached['logfilename']` - logfile to which memcached output will be redirected in /var/log/$logfilename. -* `memcached['memory']` - maximum memory for memcached instances. -* `memcached['user']` - user to run memcached as. -* `memcached['port']` - port for memcached to listen on. -* `memcached['listen']` - IP address for memcache to listen on, defaults to **0.0.0.0** (world accessible). -* `memcached['maxconn']` - maximum number of connections to accept (defaults to 1024) -* `memcached['max_object_size']` - maximum size of an object to cache (defaults to 1MB) -* `memcached['logfilename']` - logfile to which memcached output will be redirected in /var/log/$logfilename. Usage -===== +----- +Simply set the attributes and it will configure the `/etc/memcached.conf` file. If you want to use multiple memcached instances, you'll need to modify the recipe to disable the startup script and the template in the default recipe. -Simply set the attributes and it will configure the -/etc/memcached.conf file. If you want to use multiple memcached -instances, you'll need to modify the recipe to disable the startup -script and the template in the default recipe. +Use the definition, `memcached_instance`, to set up a runit service for the named memcached instance. -Use the define, memcached_instance, to set up a runit service for the -named memcached instance. +```ruby +memcached_instance 'myproj' +``` - memcached_instance "myproj" +The recipe also reads in whether to start up memcached from a `/etc/default/memcached` "ENABLE_MEMCACHED" setting, which is "yes" by default. -The recipe also reads in whether to start up memcached from a -/etc/default/memcached "ENABLE_MEMCACHED" setting, which is "yes" by -default. -License and Author -================== - -Author:: Joshua Timberman () -Author:: Joshua Sierles () +License & Authors +----------------- +- Author:: Joshua Timberman () +- Author:: Joshua Sierles () +```text Copyright:: 2009-2012, Opscode, Inc Copyright:: 2009, 37signals @@ -80,3 +70,4 @@ 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. +``` diff --git a/chef/cookbooks/memcached/TESTING.md b/chef/cookbooks/memcached/TESTING.md deleted file mode 100644 index e29ff7c..0000000 --- a/chef/cookbooks/memcached/TESTING.md +++ /dev/null @@ -1,25 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test diff --git a/chef/cookbooks/memcached/attributes/default.rb b/chef/cookbooks/memcached/attributes/default.rb index fe63541..dc13370 100644 --- a/chef/cookbooks/memcached/attributes/default.rb +++ b/chef/cookbooks/memcached/attributes/default.rb @@ -2,7 +2,7 @@ # Cookbook Name:: memcached # Attributes:: default # -# Copyright 2009, Opscode, Inc. +# Copyright 2009-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,19 +17,25 @@ # limitations under the License. # -default['memcached']['memory'] = 64 +default['memcached']['memory'] = (node['memory']['total'].to_i/1024/3) < 8192 ? node['memory']['total'].to_i/1024/3 : 8192 default['memcached']['port'] = 11211 -default['memcached']['listen'] = "0.0.0.0" -default['memcached']['maxconn'] = 1024 -default['memcached']['max_object_size'] = "1m" -default['memcached']['logfilename'] = "memcached.log" +default['memcached']['udp_port'] = 11211 +default['memcached']['listen'] = '0.0.0.0' +default['memcached']['bind_interface'] = nil +default['memcached']['maxconn'] = 4096 +default['memcached']['max_object_size'] = '1m' +default['memcached']['logfilename'] = 'memcached.log' + case node['platform_family'] when 'suse', 'fedora', 'rhel' default['memcached']['user'] = 'memcached' default['memcached']['group'] = 'memcached' -when 'debian', 'ubuntu' +when 'ubuntu' default['memcached']['user'] = 'memcache' default['memcached']['group'] = 'memcache' +when 'debian' + default['memcached']['user'] = 'nobody' + default['memcached']['group'] = 'nogroup' else default['memcached']['user'] = 'nobody' default['memcached']['user'] = 'nogroup' diff --git a/chef/cookbooks/memcached/definitions/memcached_instance.rb b/chef/cookbooks/memcached/definitions/memcached_instance.rb index fe44498..e19e059 100644 --- a/chef/cookbooks/memcached/definitions/memcached_instance.rb +++ b/chef/cookbooks/memcached/definitions/memcached_instance.rb @@ -2,7 +2,7 @@ # Cookbook Name:: memcached # Definition:: memcached_instance # -# Copyright 2009, Opscode, Inc. +# Copyright 2009-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,21 +18,21 @@ # define :memcached_instance do - include_recipe "runit" - include_recipe "memcached" + include_recipe 'runit::default' + include_recipe 'memcached::default' opts = params runit_service "memcached-#{params[:name]}" do - run_template_name "memcached" - default_logger true - cookbook "memcached" + run_template_name 'memcached' + default_logger true + cookbook 'memcached' options({ - :memory => node['memcached']['memory'], - :port => node['memcached']['port'], - :listen => node['memcached']['listen'], + :memory => node['memcached']['memory'], + :port => node['memcached']['port'], + :listen => node['memcached']['listen'], :maxconn => node['memcached']['maxconn'], - :user => node['memcached']['user']}.merge(opts) - ) + :user => node['memcached']['user'] + }.merge(opts)) end end diff --git a/chef/cookbooks/memcached/metadata.json b/chef/cookbooks/memcached/metadata.json new file mode 100644 index 0000000..749b811 --- /dev/null +++ b/chef/cookbooks/memcached/metadata.json @@ -0,0 +1,67 @@ +{ + "name": "memcached", + "version": "1.7.2", + "description": "Installs memcached and provides a define to set up an instance of memcache via runit", + "long_description": "memcached Cookbook\n==================\n[![Build Status](https://secure.travis-ci.org/opscode-cookbooks/memcached.png?branch=master)](http://travis-ci.org/opscode-cookbooks/memcached)\n\n\nInstalls memcached and provides a define to set up an instance of memcache via runit.\n\n\nRequirements\n------------\nA runit service can be set up for instances using the `memcache_instance` definition.\n\n### Platforms\n- Ubuntu 10.04, 12.04\n- CentOS 5.8, 6.3\n- openSUSE 12.3\n- SLES 12 SP2\n- SmartOS base64 1.8.1 - Note that SMF directly configures memcached with no opportunity to alter settings. If you need custom parameters, use the `memcached_instance` provider instead.\n\nMay work on other systems with or without modification.\n\n### Cookbooks\n- runit\n\n\nAttributes\n----------\nThe following are node attributes passed to the template for the runit service.\n\n- `memcached['memory']` - maximum memory for memcached instances.\n- `memcached['user']` - user to run memcached as.\n- `memcached['port']` - TCP port for memcached to listen on.\n- `memcached['udp_port']` - UDP port for memcached to listen on.\n- `memcached['listen']` - IP address for memcache to listen on, defaults to **0.0.0.0** (world accessible).\n- `memcached['maxconn']` - maximum number of connections to accept (defaults to 1024)\n- `memcached['max_object_size']` - maximum size of an object to cache (defaults to 1MB)\n- `memcached['logfilename']` - logfile to which memcached output will be redirected in /var/log/$logfilename.\n\n\nUsage\n-----\nSimply set the attributes and it will configure the `/etc/memcached.conf` file. If you want to use multiple memcached instances, you'll need to modify the recipe to disable the startup script and the template in the default recipe.\n\nUse the definition, `memcached_instance`, to set up a runit service for the named memcached instance.\n\n```ruby\nmemcached_instance 'myproj'\n```\n\nThe recipe also reads in whether to start up memcached from a `/etc/default/memcached` \"ENABLE_MEMCACHED\" setting, which is \"yes\" by default.\n\n\nLicense & Authors\n-----------------\n- Author:: Joshua Timberman ()\n- Author:: Joshua Sierles ()\n\n```text\nCopyright:: 2009-2012, Opscode, Inc\nCopyright:: 2009, 37signals\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "amazon": ">= 0.0.0", + "centos": ">= 0.0.0", + "debian": ">= 0.0.0", + "fedora": ">= 0.0.0", + "redhat": ">= 0.0.0", + "scientific": ">= 0.0.0", + "smartos": ">= 0.0.0", + "suse": ">= 0.0.0", + "ubuntu": ">= 0.0.0" + }, + "dependencies": { + "runit": "~> 1.0", + "yum": "~> 3.0", + "yum-epel": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + "memcached/memory": { + "display_name": "Memcached Memory", + "description": "Memory allocated for memcached instance", + "default": "64" + }, + "memcached/port": { + "display_name": "Memcached Port", + "description": "Port to use for memcached instance", + "default": "11211" + }, + "memcached/user": { + "display_name": "Memcached User", + "description": "User to run memcached instance as", + "default": "nobody" + }, + "memcached/listen": { + "display_name": "Memcached IP Address", + "description": "IP address to use for memcached instance", + "default": "0.0.0.0" + }, + "memcached/logfilename": { + "display_name": "Memcached logfilename", + "description": "The filename used to log memcached", + "default": "memcached.log" + } + }, + "groupings": { + }, + "recipes": { + "memcached": "Installs and configures memcached" + } +} \ No newline at end of file diff --git a/chef/cookbooks/memcached/metadata.rb b/chef/cookbooks/memcached/metadata.rb index d453023..c0d8387 100644 --- a/chef/cookbooks/memcached/metadata.rb +++ b/chef/cookbooks/memcached/metadata.rb @@ -1,40 +1,48 @@ -name "memcached" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Installs memcached and provides a define to set up an instance of memcache via runit" +name 'memcached' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Installs memcached and provides a define to set up an instance of memcache via runit' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "1.5.0" -depends "runit", "~> 1.0" -depends "yum" +version '1.7.2' -recipe "memcached", "Installs and configures memcached" +depends 'runit', '~> 1.0' +depends 'yum', '~> 3.0' +depends 'yum-epel' -%w{ ubuntu debian redhat fedora centos - scientific amazon smartos suse }.each do |os| - supports os -end +supports 'amazon' +supports 'centos' +supports 'debian' +supports 'fedora' +supports 'redhat' +supports 'scientific' +supports 'smartos' +supports 'suse' +supports 'ubuntu' -attribute "memcached/memory", - :display_name => "Memcached Memory", - :description => "Memory allocated for memcached instance", - :default => "64" +recipe 'memcached', 'Installs and configures memcached' -attribute "memcached/port", - :display_name => "Memcached Port", - :description => "Port to use for memcached instance", - :default => "11211" +attribute 'memcached/memory', + :display_name => 'Memcached Memory', + :description => 'Memory allocated for memcached instance', + :default => '64' -attribute "memcached/user", - :display_name => "Memcached User", - :description => "User to run memcached instance as", - :default => "nobody" +attribute 'memcached/port', + :display_name => 'Memcached Port', + :description => 'Port to use for memcached instance', + :default => '11211' -attribute "memcached/listen", - :display_name => "Memcached IP Address", - :description => "IP address to use for memcached instance", - :default => "0.0.0.0" -attribute "memcached/logfilename", - :display_name => "Memcached logfilename", - :description => "The filename used to log memcached", - :default => "memcached.log" +attribute 'memcached/user', + :display_name => 'Memcached User', + :description => 'User to run memcached instance as', + :default => 'nobody' + +attribute 'memcached/listen', + :display_name => 'Memcached IP Address', + :description => 'IP address to use for memcached instance', + :default => '0.0.0.0' + +attribute 'memcached/logfilename', + :display_name => 'Memcached logfilename', + :description => 'The filename used to log memcached', + :default => 'memcached.log' diff --git a/chef/cookbooks/memcached/recipes/default.rb b/chef/cookbooks/memcached/recipes/default.rb index ce016fb..d3aa361 100644 --- a/chef/cookbooks/memcached/recipes/default.rb +++ b/chef/cookbooks/memcached/recipes/default.rb @@ -2,7 +2,7 @@ # Cookbook Name:: memcached # Recipe:: default # -# Copyright 2009, Opscode, Inc. +# Copyright 2009-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,76 +18,82 @@ # # include epel on redhat/centos 5 and below in order to get the memcached packages -if node['platform_family'] == "rhel" and node['platform_version'].to_i < 6 - include_recipe "yum::epel" +include_recipe 'yum-epel' if node['platform_family'] == 'rhel' && node['platform_version'].to_i == 5 + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack end -package "memcached" do - action :install -end +package 'memcached' -package "libmemcache-dev" do +package 'libmemcache-dev' do case node['platform_family'] - when "rhel", "fedora" - package_name "libmemcached-devel" - when "smartos" - package_name "libmemcached" - when "suse" + when 'rhel', 'fedora' + package_name 'libmemcached-devel' + when 'smartos' + package_name 'libmemcached' + when 'suse' if node['platform_version'].to_f < 12 - package_name "libmemcache-devel" + package_name 'libmemcache-devel' else - package_name "libmemcached-devel" + package_name 'libmemcached-devel' end else - package_name "libmemcache-dev" + package_name 'libmemcache-dev' end - action :install end -service "memcached" do - action :nothing - supports :status => true, :start => true, :stop => true, :restart => true +service 'memcached' do + action :enable + supports :status => true, :start => true, :stop => true, :restart => true, :enable => true +end + +if !node['memcached']['bind_interface'].nil? + node.set['memcached']['listen'] = address_for(node['memcached']['bind_interface']) end case node['platform_family'] -when "rhel", "fedora", "suse" +when 'rhel', 'fedora', 'suse' family = node['platform_family'] == 'suse' ? 'suse' : 'redhat' - template "/etc/sysconfig/memcached" do + template '/etc/sysconfig/memcached' do source "memcached.sysconfig.#{family}.erb" - owner "root" - group "root" - mode 00644 + owner 'root' + group 'root' + mode '0644' variables( - :listen => node['memcached']['listen'], - :user => node['memcached']['user'], - :group => node['memcached']['group'], - :port => node['memcached']['port'], - :maxconn => node['memcached']['maxconn'], - :memory => node['memcached']['memory'], - :logfilename => node['memcached-chat']['logfilename'] + :listen => node['memcached']['listen'], + :user => node['memcached']['user'], + :group => node['memcached']['group'], + :port => node['memcached']['port'], + :udp_port => node['memcached']['udp_port'], + :maxconn => node['memcached']['maxconn'], + :memory => node['memcached']['memory'], + :max_object_size => node['memcached']['max_object_size'], + :logfilename => node['memcached']['logfilename'] ) - notifies :restart, "service[memcached]" + notifies :restart, 'service[memcached]' end -when "smartos" +when 'smartos' # SMF directly configures memcached with no opportunity to alter settings # If you need custom parameters, use the memcached_instance provider - service "memcached" do + service 'memcached' do action :enable end else - template "/etc/memcached.conf" do - source "memcached.conf.erb" - owner "root" - group "root" - mode 00644 + template '/etc/memcached.conf' do + source 'memcached.conf.erb' + owner 'root' + group 'root' + mode '0644' variables( - :listen => node['memcached']['listen'], - :user => node['memcached']['user'], - :port => node['memcached']['port'], - :maxconn => node['memcached']['maxconn'], - :memory => node['memcached']['memory'], + :listen => node['memcached']['listen'], + :user => node['memcached']['user'], + :port => node['memcached']['port'], + :udp_port => node['memcached']['udp_port'], + :maxconn => node['memcached']['maxconn'], + :memory => node['memcached']['memory'], :max_object_size => node['memcached']['max_object_size'] ) - notifies :restart, "service[memcached]" + notifies :restart, 'service[memcached]' end end diff --git a/chef/cookbooks/memcached/templates/default/memcached.conf.erb b/chef/cookbooks/memcached/templates/default/memcached.conf.erb index 85037d3..9455979 100644 --- a/chef/cookbooks/memcached/templates/default/memcached.conf.erb +++ b/chef/cookbooks/memcached/templates/default/memcached.conf.erb @@ -27,6 +27,7 @@ logfile /var/log/memcached.log # Default connection port is 11211 -p <%= @port %> +-U <%= @udp_port %> # Run the daemon as root. The start-memcached will default to running as root if no # -u command is present in this config file diff --git a/chef/cookbooks/memcached/templates/default/memcached.sysconfig.redhat.erb b/chef/cookbooks/memcached/templates/default/memcached.sysconfig.redhat.erb index 5ec7c18..98cde35 100644 --- a/chef/cookbooks/memcached/templates/default/memcached.sysconfig.redhat.erb +++ b/chef/cookbooks/memcached/templates/default/memcached.sysconfig.redhat.erb @@ -10,4 +10,4 @@ PORT="<%= @port %>" USER="<%= @user %>" MAXCONN="<%= @maxconn %>" CACHESIZE="<%= @memory %>" -OPTIONS="-l <%= @listen %> >> /var/log/<%= @logfilename %> 2>&1" +OPTIONS="-U <%= @udp_port %> -l <%= @listen %> -I <%= @max_object_size %> >> /var/log/<%= @logfilename %> 2>&1" diff --git a/chef/cookbooks/memcached/templates/default/memcached.sysconfig.suse.erb b/chef/cookbooks/memcached/templates/default/memcached.sysconfig.suse.erb index 9a1b360..e4373d1 100644 --- a/chef/cookbooks/memcached/templates/default/memcached.sysconfig.suse.erb +++ b/chef/cookbooks/memcached/templates/default/memcached.sysconfig.suse.erb @@ -9,7 +9,7 @@ # see man 1 memcached for more # MEMCACHED_PARAMS="<%= "-l #{@listen} -c #{@maxconn} " + - "-m #{@memory} -p #{@port} >> /var/log/#{@logfilename} 2>&1"%>" + "-m #{@memory} -U #{@udp_port} -p #{@port} >> /var/log/#{@logfilename} 2>&1"%>" ## Path: Network/WWW/Memcached ## Description: username memcached should run as diff --git a/chef/cookbooks/memcached/templates/default/sv-memcached-run.erb b/chef/cookbooks/memcached/templates/default/sv-memcached-run.erb index 28c2fd5..01c68ad 100644 --- a/chef/cookbooks/memcached/templates/default/sv-memcached-run.erb +++ b/chef/cookbooks/memcached/templates/default/sv-memcached-run.erb @@ -1,3 +1,3 @@ #!/bin/sh exec 2>&1 -exec chpst -u <%= @options[:user] %> /usr/bin/memcached -v -m <%= @options[:memory] %> -p <%= @options[:port] %> -u <%= @options[:user] %> -l <%= @options[:listen] %> -c <%= @options[:maxconn] %> +exec chpst -u <%= @options[:user] %> /usr/bin/memcached -v -m <%= @options[:memory] %> -U <%= @options[:udp_port] %> -p <%= @options[:port] %> -u <%= @options[:user] %> -l <%= @options[:listen] %> -c <%= @options[:maxconn] %> diff --git a/chef/cookbooks/memcached/test/cookbooks/memcached_test/README.md b/chef/cookbooks/memcached/test/cookbooks/memcached_test/README.md deleted file mode 100644 index da45e2e..0000000 --- a/chef/cookbooks/memcached/test/cookbooks/memcached_test/README.md +++ /dev/null @@ -1 +0,0 @@ -This cookbook is used with test-kitchen to test the parent, memcached cookbok diff --git a/chef/cookbooks/memcached/test/cookbooks/memcached_test/metadata.rb b/chef/cookbooks/memcached/test/cookbooks/memcached_test/metadata.rb deleted file mode 100644 index e75ea4f..0000000 --- a/chef/cookbooks/memcached/test/cookbooks/memcached_test/metadata.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "memcached_test" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "This cookbook is used with test-kitchen to test the parent, memcached cookbok" -version "1.0.0" diff --git a/chef/cookbooks/mysql/.kitchen.yml b/chef/cookbooks/mysql/.kitchen.yml deleted file mode 100644 index 3bc37ab..0000000 --- a/chef/cookbooks/mysql/.kitchen.yml +++ /dev/null @@ -1,48 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: - - recipe[apt] - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - run_list: - - recipe[yum::epel] - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - run_list: - - recipe[yum::epel] - -suites: -- name: client - run_list: - - recipe[mysql::client] - attributes: {} -- name: ruby - run_list: - - recipe[mysql::ruby] - attributes: {} -- name: server - run_list: - - recipe[minitest-handler] - - recipe[mysql_test::server] - attributes: {} diff --git a/chef/cookbooks/mysql/Berksfile b/chef/cookbooks/mysql/Berksfile deleted file mode 100644 index bdbe545..0000000 --- a/chef/cookbooks/mysql/Berksfile +++ /dev/null @@ -1,11 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook "apt" - cookbook "yum" - - cookbook "mysql_test", :path => "./test/cookbooks/mysql_test" - cookbook "minitest-handler" -end diff --git a/chef/cookbooks/mysql/CHANGELOG.md b/chef/cookbooks/mysql/CHANGELOG.md index 73aff60..cef65b3 100644 --- a/chef/cookbooks/mysql/CHANGELOG.md +++ b/chef/cookbooks/mysql/CHANGELOG.md @@ -2,6 +2,121 @@ mysql Cookbook CHANGELOG ======================== This file is used to list changes made in each version of the mysql cookbook. + +v4.1.2 (2014-02-28) +------------------- +- [COOK-4349] - Fix invalid platform check +- [COOK-4184] - Better handling of Ubuntu upstart service +- [COOK-2100] - Changing innodb_log_file_size tunable results in inability to start MySQL + + +v4.1.1 (2014-02-25) +------------------- +- **[COOK-2966] - Address foodcritic failures' +- **[COOK-4182] - Template parse failure in /etc/init/mysql.conf (data_dir)' +- **[COOK-4198] - Added missing tunable' +- **[COOK-4206] - create root@127.0.0.1, as well as root@localhost' + + +v4.0.20 (2014-01-18) +-------------------- +* [COOK-3931] - MySQL Server Recipe Regression for Non-LTS Ubuntu Versions +* [COOK-3945] - MySQL cookbook fails on Ubuntu 13.04/13.10 +* [COOK-3966] - mysql::server recipe can't find a template with debian 7.x +* [COOK-3985] - Missing /etc/mysql/debian.cnf template on mysql::_server_debian.rb recipe (mysql 4.0.4) +* [COOK-3974] - debian.cnf not updated +* [COOK-4001] - Pull request: Fixes for broken mysql::server on Debian +* [COOK-4071] - Mysql cookbook doesn't work on debian 7.2 + + +v4.0.14 +------- +Fixing style cops + + +v4.0.12 +------- +### Bug +- **[COOK-4068](https://tickets.opscode.com/browse/COOK-4068)** - rework MySQL Windows recipe + +### Improvement +- **[COOK-3801](https://tickets.opscode.com/browse/COOK-3801)** - Add innodb_adaptive_flushing_method and innodb_adaptive_checkpoint + + +v4.0.10 +------- +fixing metadata version error. locking to 3.0 + + +v4.0.8 +------ +Locking yum dependency to '< 3' + + +v4.0.6 +------ +# Bug +- [COOK-3943] Notifying service restart on grants update + + +v4.0.4 +------ +[COOK-3952] - Adding 'recursive true' to directory resources + + +v4.0.2 +------ +### BUGS +- Adding support for Amazon Linux in attributes/server_rhel.rb +- Fixing bug where unprivileged users cannot connect over a local socket. Adding integration test. +- Fixing bug in mysql_grants_cmd generation + + +v4.0.0 +------ +- [COOK-3928] Heavily refactoring for readability. Moving platform implementation into separate recipes +- Moving integration tests from minitest to serverspec, removing "improper" tests +- Moving many attributes into the ['mysql']['server']['whatever'] namespace +- [COOK-3481] - Merged Lucas Welsh's Windows bits and moved into own recipe +- [COOK-3697] - Adding security hardening attributes +- [COOK-3780] - Fixing data_dir on Debian and Ubuntu +- [COOK-3807] - Don't use execute[assign-root-password] on Debian and Ubuntu +- [COOK-3881] - Fixing /etc being owned by mysql user + + +v3.0.12 +------- +### Bug +- **[COOK-3752](https://tickets.opscode.com/browse/COOK-3752)** - mysql service fails to start in mysql::server recipe + + +v3.0.10 +------- +- Fix a failed release attempt for v3.0.8 + + +v3.0.8 +------ +### Bug +- **[COOK-3749](https://tickets.opscode.com/browse/COOK-3749)** - Fix a regression with Chef 11-specific features + + +v3.0.6 +------ +### Bug +- **[COOK-3674](https://tickets.opscode.com/browse/COOK-3674)** - Fix an issue where the MySQL server fails to set the root password correctly when `data_dir` is a non-default value +- **[COOK-3647](https://tickets.opscode.com/browse/COOK-3647)** - Fix README typo (databas => database) +- **[COOK-3477](https://tickets.opscode.com/browse/COOK-3477)** - Fix log-queries-not-using-indexes not working +- **[COOK-3436](https://tickets.opscode.com/browse/COOK-3436)** - Pull percona repo in compilation phase +- **[COOK-3208](https://tickets.opscode.com/browse/COOK-3208)** - Fix README typo (LitenPort => ListenPort) +- **[COOK-3149](https://tickets.opscode.com/browse/COOK-3149)** - Create my.cnf before installing +- **[COOK-2681](https://tickets.opscode.com/browse/COOK-2681)** - Fix log_slow_queries for 5.5+ +- **[COOK-2606](https://tickets.opscode.com/browse/COOK-2606)** - Use proper bind address on cloud providers + +### Improvement +- **[COOK-3498](https://tickets.opscode.com/browse/COOK-3498)** - Add support for replicate_* variables in my.cnf + + v3.0.4 ------ ### Bug diff --git a/chef/cookbooks/mysql/CONTRIBUTING b/chef/cookbooks/mysql/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/mysql/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/mysql/LICENSE b/chef/cookbooks/mysql/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/mysql/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/mysql/README.md b/chef/cookbooks/mysql/README.md index 9c935d5..c90a4a2 100644 --- a/chef/cookbooks/mysql/README.md +++ b/chef/cookbooks/mysql/README.md @@ -1,13 +1,13 @@ mysql Cookbook ============== -Installs and configures MySQL client or server. +[![Build Status](https://secure.travis-ci.org/opscode-cookbooks/mysql.png?branch=master)](http://travis-ci.org/opscode-cookbooks/mysql) +Installs and configures MySQL client or server. Requirements ------------ Chef 0.10.10+. - Platform -------- - Debian, Ubuntu @@ -16,10 +16,8 @@ Platform Tested on: -- Debian 5.0, 6.0 -- Ubuntu 10.04-12.04 -- CentOS 5.5-5.8, 6.2-6.3 -- Mac OS X 10.7.2 +- Ubuntu 10.04, 12.04 +- CentOS 5.9, 6.5 See TESTING.md for information about running tests in Opscode's Test Kitchen. @@ -43,39 +41,31 @@ Attributes ---------- See the `attributes/server.rb` or `attributes/client.rb` for default values. Several attributes have values that vary based on the node's platform and version. +* `node['mysql']['port']` - Listen port for MySQLd +* `node['mysql']['data_dir']` - Location for mysql data directory. `WARNING` This will only on initial converge. It will not move data around if you change it. + * `node['mysql']['client']['packages']` - An array of package names that should be installed on "client" systems. This can be modified, e.g., to specify packages for Percona. * `node['mysql']['server']['packages']` - An array of package names that should be installed on "server" systems. This can be modified, e.g., to specify packages for Percona. - * `node['mysql']['auto-increment-increment']` - auto-increment-increment value in my.cnf -* `node['mysql']['auto-increment-offset]` - auto-increment-offset - value in my.cnf -* `node['mysql']['basedir']` - Base directory where MySQL is installed +* `node['mysql']['auto-increment-offset]` - auto-increment-offset value in my.cnf +* `node['mysql']['server']['basedir']` - Base directory where MySQL is installed * `node['mysql']['bind_address']` - Listen address for MySQLd -* `node['mysql']['conf_dir']` - Location for mysql conf directory -* `node['mysql']['confd_dir']` - Location for mysql conf.d style - include directory -* `node['mysql']['data_dir']` - Location for mysql data directory -* `node['mysql']['ec2_path']` - location of mysql data_dir on EC2 - nodes -* `node['mysql']['grants_path']` - Path where the grants.sql should be - written +* `node['mysql']['ec2_path']` - location of mysql data_dir on EC2 nodes +* `node['mysql']['grants_path']` - Path where the grants.sql should be written * `node['mysql']['mysqladmin_bin']` - Path to the mysqladmin binary -* `node['mysql']['old_passwords']` - Sets the `old_passwords` value in - my.cnf. -* `node['mysql']['pid_file']` - Path to the mysqld.pid file -* `node['mysql']['port']` - Liten port for MySQLd -* `node['mysql']['reload_action']` - Action to take when mysql conf +* `node['mysql']['server']['old_passwords']` - Sets the `old_passwords` value in my.cnf. +* `node['mysql']['server']['pid_file']` - Path to the mysqld.pid file + +* `node['mysql']['server']['reload_action']` - Action to take when mysql conf files are modified. Also allows "reload" and "none". -* `node['mysql']['root_group']` - The default group of the "root" user -* `node['mysql']['service_name']` - The name of the mysqld service -* `node['mysql']['socket']` - Path to the mysqld.sock file -* `node['mysql']['use_upstart']` - Whether to use upstart for the - service provider +* `node['mysql']['server']['root_group']` - The default group of the "root" user +* `node['mysql']['server']['service_name']` - The name of the mysqld service +* `node['mysql']['server']['socket']` - Path to the mysqld.sock file * `mysql['root_network_acl']` - Set define the network the root user will be able to login from, default is nil Performance and other "tunable" attributes are under the `node['mysql']['tunable']` attribute, corresponding to the same-named parameter in my.cnf, and the default values are used. See `attributes/server.rb`. @@ -88,7 +78,7 @@ Normally, root should only be allowed to connect from 'localhost'. This ensures * `node['mysql']['allow_remote_root']` - If true Sets root access from '%'. If false deletes any non-localhost root users. -By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. This will also drop any user privileges to the test databae and any DB named test_% . +By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. This will also drop any user privileges to the test database and any DB named test_% . * `node['mysql']['remove_test_database']` - Delete the test database and access to it. @@ -122,6 +112,17 @@ The following attributes are specific to Windows platforms. * `node['mysql']['client']['ruby_dir']` - location where the Ruby binaries will be +## Security Options + +Further information is already available at [Symantec](http://www.symantec.com/connect/articles/securing-mysql-step-step) and [Deutsche Telekom (German)](http://www.telekom.com/static/-/155996/7/technische-sicherheitsanforderungen-si) + +* default['mysql']['security']['chroot'] - [chroot](http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_chroot) +* default['mysql']['security']['safe_user_create'] - [safe-user-create](http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_safe-user-create) +* default['mysql']['security']['secure_auth'] - [secure-auth](http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-auth) +* default['mysql']['security']['skip_symbolic_links'] - [skip-symbolic-links](http://dev.mysql.com/doc/refman/5.7/en/server- +options.html#option_mysqld_symbolic-links) +* default['mysql']['security']['skip_show_database'] - [skip-show-database](http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_skip-show-database) +* default['mysql']['security']['local_infile'] - [local-infile](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_local_infile) Usage ----- @@ -202,6 +203,8 @@ License & Authors - Author:: Brian Bianco () - Author:: Jesse Howarth () - Author:: Andrew Crump () +- Author:: Christoph Hartmann () +- Author:: Sean OMeara () ```text Copyright:: 2009-2013 Opscode, Inc diff --git a/chef/cookbooks/mysql/TESTING.md b/chef/cookbooks/mysql/TESTING.md deleted file mode 100644 index e29ff7c..0000000 --- a/chef/cookbooks/mysql/TESTING.md +++ /dev/null @@ -1,25 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test diff --git a/chef/cookbooks/mysql/attributes/client.rb b/chef/cookbooks/mysql/attributes/client.rb index fcaa1da..92d017a 100644 --- a/chef/cookbooks/mysql/attributes/client.rb +++ b/chef/cookbooks/mysql/attributes/client.rb @@ -2,7 +2,7 @@ # Cookbook Name:: mysql # Attributes:: client # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,26 +16,27 @@ # See the License for the specific language governing permissions and # limitations under the License. # + # Include Opscode helper in Node class to get access # to debian_before_squeeze? and ubuntu_before_lucid? ::Chef::Node.send(:include, Opscode::Mysql::Helpers) case node['platform_family'] -when "rhel", "fedora" - default['mysql']['client']['packages'] = %w{mysql mysql-devel} -when "suse" - default['mysql']['client']['packages'] = %w{mysql-community-server-client libmysqlclient-devel} -when "debian" +when 'rhel', 'fedora' + default['mysql']['client']['packages'] = %w[mysql mysql-devel] +when 'suse' + default['mysql']['client']['packages'] = %w[mysql-community-server-client libmysqlclient-devel] +when 'debian' if debian_before_squeeze? || ubuntu_before_lucid? - default['mysql']['client']['packages'] = %w{mysql-client libmysqlclient15-dev} + default['mysql']['client']['packages'] = %w[mysql-client libmysqlclient15-dev] else - default['mysql']['client']['packages'] = %w{mysql-client libmysqlclient-dev} + default['mysql']['client']['packages'] = %w[mysql-client libmysqlclient-dev] end -when "freebsd" - default['mysql']['client']['packages'] = %w{mysql55-client} -when "windows" - default['mysql']['client']['version'] = "6.0.2" - default['mysql']['client']['arch'] = "win32" # force 32 bit to work with mysql gem +when 'freebsd' + default['mysql']['client']['packages'] = %w[mysql55-client] +when 'windows' + default['mysql']['client']['version'] = '6.0.2' + default['mysql']['client']['arch'] = 'win32' # force 32 bit to work with mysql gem default['mysql']['client']['package_file'] = "mysql-connector-c-#{mysql['client']['version']}-#{mysql['client']['arch']}.msi" default['mysql']['client']['url'] = "http://www.mysql.com/get/Downloads/Connector-C/#{mysql['client']['package_file']}/from/http://mysql.mirrors.pair.com/" default['mysql']['client']['packages'] = ["MySQL Connector C #{mysql['client']['version']}"] @@ -44,9 +45,8 @@ when "windows" default['mysql']['client']['lib_dir'] = "#{mysql['client']['basedir']}\\lib/opt" default['mysql']['client']['bin_dir'] = "#{mysql['client']['basedir']}\\bin" default['mysql']['client']['ruby_dir'] = RbConfig::CONFIG['bindir'] -when "mac_os_x" - default['mysql']['client']['packages'] = %w{mysql-connector-c} +when 'mac_os_x' + default['mysql']['client']['packages'] = %w[mysql-connector-c] else - default['mysql']['client']['packages'] = %w{mysql-client libmysqlclient-dev} + default['mysql']['client']['packages'] = %w[mysql-client libmysqlclient-dev] end - diff --git a/chef/cookbooks/mysql/attributes/percona_repo.rb b/chef/cookbooks/mysql/attributes/percona_repo.rb index 80650a6..0e0fe20 100644 --- a/chef/cookbooks/mysql/attributes/percona_repo.rb +++ b/chef/cookbooks/mysql/attributes/percona_repo.rb @@ -1,3 +1,22 @@ +# +# Cookbook Name:: mysql +# Attributes:: client +# +# Copyright 2013, Opscode, Inc. +# +# 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. +# + default['mysql']['percona']['apt_key_id'] = 'CD2EFD2A' -default['mysql']['percona']['apt_uri'] = "http://repo.percona.com/apt" -default['mysql']['percona']['apt_keyserver'] = "keys.gnupg.net" +default['mysql']['percona']['apt_uri'] = 'http://repo.percona.com/apt' +default['mysql']['percona']['apt_keyserver'] = 'keys.gnupg.net' diff --git a/chef/cookbooks/mysql/attributes/server.rb b/chef/cookbooks/mysql/attributes/server.rb index 6458c55..67c2579 100644 --- a/chef/cookbooks/mysql/attributes/server.rb +++ b/chef/cookbooks/mysql/attributes/server.rb @@ -2,7 +2,7 @@ # Cookbook Name:: mysql # Attributes:: server # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,175 +17,58 @@ # limitations under the License. # -#if (defined? node['mycluster']) -# item = data_bag_item('openstack',node['mycluster']) -#else -# item = data_bag_item('openstack','env_default') -#end - -#binding_interface = item['network']['control']['interface'] -#file = File.open('/home/test.txt','w') -#file.write(binding_interface) - -default['mysql']['bind_address'] = node.attribute?('cloud') ? node.cloud['local_ipv4'] : node['ipaddress'] +# Probably driven from wrapper cookbooks, environments, or roles. +# Keep in this namespace for backwards compat +default['mysql']['bind_address'] = node.attribute?('cloud') && node['cloud']['local_ipv4'] ? node['cloud']['local_ipv4'] : node['ipaddress'] default['mysql']['port'] = 3306 default['mysql']['nice'] = 0 -case node["platform_family"] -when "debian" - default['mysql']['server']['packages'] = %w{mysql-server} - default['mysql']['service_name'] = "mysql" - default['mysql']['basedir'] = "/usr" - default['mysql']['data_dir'] = "/var/lib/mysql" - default['mysql']['root_group'] = "root" - default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" - default['mysql']['mysql_bin'] = "/usr/bin/mysql" - - default['mysql']['conf_dir'] = '/etc/mysql' - default['mysql']['confd_dir'] = '/etc/mysql/conf.d' - default['mysql']['socket'] = "/var/run/mysqld/mysqld.sock" - default['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" - default['mysql']['old_passwords'] = 0 - default['mysql']['grants_path'] = "/etc/mysql/grants.sql" -when "rhel", "fedora" - if node["mysql"]["version"].to_f >= 5.5 - default['mysql']['service_name'] = "mysql" - default['mysql']['pid_file'] = "/var/run/mysql/mysql.pid" - else - default['mysql']['service_name'] = "mysqld" - default['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" - end - default['mysql']['server']['packages'] = %w{mysql-server} - default['mysql']['basedir'] = "/usr" - default['mysql']['data_dir'] = "/var/lib/mysql" - default['mysql']['root_group'] = "root" - default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" - default['mysql']['mysql_bin'] = "/usr/bin/mysql" - - default['mysql']['conf_dir'] = '/etc' - default['mysql']['confd_dir'] = '/etc/mysql/conf.d' - default['mysql']['socket'] = "/var/lib/mysql/mysql.sock" - default['mysql']['old_passwords'] = 1 - default['mysql']['grants_path'] = "/etc/mysql_grants.sql" - # RHEL/CentOS mysql package does not support this option. - default['mysql']['tunable']['innodb_adaptive_flushing'] = false -when "suse" - default['mysql']['service_name'] = "mysql" - default['mysql']['server']['packages'] = %w{mysql-community-server} - default['mysql']['basedir'] = "/usr" - default['mysql']['data_dir'] = "/var/lib/mysql" - default['mysql']['root_group'] = "root" - default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" - default['mysql']['mysql_bin'] = "/usr/bin/mysql" - default['mysql']['conf_dir'] = '/etc' - default['mysql']['confd_dir'] = '/etc/mysql/conf.d' - default['mysql']['socket'] = "/var/run/mysql/mysql.sock" - default['mysql']['pid_file'] = "/var/run/mysql/mysqld.pid" - default['mysql']['old_passwords'] = 1 - default['mysql']['grants_path'] = "/etc/mysql_grants.sql" -when "freebsd" - default['mysql']['server']['packages'] = %w{mysql55-server} - default['mysql']['service_name'] = "mysql-server" - default['mysql']['basedir'] = "/usr/local" - default['mysql']['data_dir'] = "/var/db/mysql" - default['mysql']['root_group'] = "wheel" - default['mysql']['mysqladmin_bin'] = "/usr/local/bin/mysqladmin" - default['mysql']['mysql_bin'] = "/usr/local/bin/mysql" - - default['mysql']['conf_dir'] = '/usr/local/etc' - default['mysql']['confd_dir'] = '/usr/local/etc/mysql/conf.d' - default['mysql']['socket'] = "/tmp/mysqld.sock" - default['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" - default['mysql']['old_passwords'] = 0 - default['mysql']['grants_path'] = "/var/db/mysql/grants.sql" -when "windows" - default['mysql']['server']['packages'] = ["MySQL Server 5.5"] - default['mysql']['version'] = '5.5.21' - default['mysql']['arch'] = 'win32' - default['mysql']['package_file'] = "mysql-#{mysql['version']}-#{mysql['arch']}.msi" - default['mysql']['url'] = "http://www.mysql.com/get/Downloads/MySQL-5.5/#{mysql['package_file']}/from/http://mysql.mirrors.pair.com/" - - default['mysql']['service_name'] = "mysql" - default['mysql']['basedir'] = "#{ENV['SYSTEMDRIVE']}\\Program Files (x86)\\MySQL\\#{mysql['server']['packages'].first}" - default['mysql']['data_dir'] = "#{node['mysql']['basedir']}\\Data" - default['mysql']['bin_dir'] = "#{node['mysql']['basedir']}\\bin" - default['mysql']['mysqladmin_bin'] = "#{node['mysql']['bin_dir']}\\mysqladmin" - default['mysql']['mysql_bin'] = "#{node['mysql']['bin_dir']}\\mysql" - - default['mysql']['conf_dir'] = node['mysql']['basedir'] - default['mysql']['old_passwords'] = 0 - default['mysql']['grants_path'] = "#{node['mysql']['conf_dir']}\\grants.sql" -when "mac_os_x" - default['mysql']['server']['packages'] = %w{mysql} - default['mysql']['basedir'] = "/usr/local/Cellar" - default['mysql']['data_dir'] = "/usr/local/var/mysql" - default['mysql']['root_group'] = "admin" - default['mysql']['mysqladmin_bin'] = "/usr/local/bin/mysqladmin" - default['mysql']['mysql_bin'] = "/usr/local/bin/mysql" -else - default['mysql']['server']['packages'] = %w{mysql-server} - default['mysql']['service_name'] = "mysql" - default['mysql']['basedir'] = "/usr" - default['mysql']['data_dir'] = "/var/lib/mysql" - default['mysql']['root_group'] = "root" - default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" - default['mysql']['mysql_bin'] = "/usr/bin/mysql" - - default['mysql']['conf_dir'] = '/etc/mysql' - default['mysql']['confd_dir'] = '/etc/mysql/conf.d' - default['mysql']['socket'] = "/var/run/mysqld/mysqld.sock" - default['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" - default['mysql']['old_passwords'] = 0 - default['mysql']['grants_path'] = "/etc/mysql/grants.sql" -end - +# eventually remove? where is this used? if attribute?('ec2') - default['mysql']['ec2_path'] = "/mnt/mysql" - default['mysql']['ebs_vol_dev'] = "/dev/sdi" + default['mysql']['ec2_path'] = '/mnt/mysql' + default['mysql']['ebs_vol_dev'] = '/dev/sdi' default['mysql']['ebs_vol_size'] = 50 end -default['mysql']['reload_action'] = "restart" # or "reload" or "none" - -default['mysql']['use_upstart'] = node['platform'] == "ubuntu" && node['platform_version'].to_f >= 10.04 - +# actual configs start here default['mysql']['auto-increment-increment'] = 1 default['mysql']['auto-increment-offset'] = 1 -default['mysql']['allow_remote_root'] = true +default['mysql']['allow_remote_root'] = false default['mysql']['remove_anonymous_users'] = false default['mysql']['remove_test_database'] = false default['mysql']['root_network_acl'] = nil -default['mysql']['tunable']['character-set-server'] = "utf8" -default['mysql']['tunable']['collation-server'] = "utf8_general_ci" +default['mysql']['tunable']['character-set-server'] = 'utf8' +default['mysql']['tunable']['collation-server'] = 'utf8_general_ci' default['mysql']['tunable']['lower_case_table_names'] = nil -default['mysql']['tunable']['back_log'] = "128" -default['mysql']['tunable']['key_buffer_size'] = "256M" -default['mysql']['tunable']['myisam_sort_buffer_size'] = "8M" -default['mysql']['tunable']['myisam_max_sort_file_size'] = "2147483648" -default['mysql']['tunable']['myisam_repair_threads'] = "1" -default['mysql']['tunable']['myisam-recover'] = "BACKUP" -default['mysql']['tunable']['max_allowed_packet'] = "16M" -default['mysql']['tunable']['max_connections'] = "800" -default['mysql']['tunable']['max_connect_errors'] = "10" -default['mysql']['tunable']['concurrent_insert'] = "2" -default['mysql']['tunable']['connect_timeout'] = "10" -default['mysql']['tunable']['tmp_table_size'] = "32M" +default['mysql']['tunable']['back_log'] = '128' +default['mysql']['tunable']['key_buffer_size'] = '256M' +default['mysql']['tunable']['myisam_sort_buffer_size'] = '8M' +default['mysql']['tunable']['myisam_max_sort_file_size'] = '2147483648' +default['mysql']['tunable']['myisam_repair_threads'] = '1' +default['mysql']['tunable']['myisam-recover'] = 'BACKUP' +default['mysql']['tunable']['max_allowed_packet'] = '16M' +default['mysql']['tunable']['max_connections'] = '3000' +default['mysql']['tunable']['max_connect_errors'] = '10' +default['mysql']['tunable']['concurrent_insert'] = '2' +default['mysql']['tunable']['connect_timeout'] = '60' +default['mysql']['tunable']['tmp_table_size'] = '32M' default['mysql']['tunable']['max_heap_table_size'] = node['mysql']['tunable']['tmp_table_size'] default['mysql']['tunable']['bulk_insert_buffer_size'] = node['mysql']['tunable']['tmp_table_size'] -default['mysql']['tunable']['net_read_timeout'] = "30" -default['mysql']['tunable']['net_write_timeout'] = "30" -default['mysql']['tunable']['table_cache'] = "128" - +default['mysql']['tunable']['net_read_timeout'] = '30' +default['mysql']['tunable']['net_write_timeout'] = '30' +default['mysql']['tunable']['table_cache'] = '128' +default['mysql']['tunable']['table_open_cache'] = node['mysql']['tunable']['table_cache'] # table_cache is deprecated + # in favor of table_open_cache default['mysql']['tunable']['thread_cache_size'] = 8 default['mysql']['tunable']['thread_concurrency'] = 10 -default['mysql']['tunable']['thread_stack'] = "256K" -default['mysql']['tunable']['sort_buffer_size'] = "2M" -default['mysql']['tunable']['read_buffer_size'] = "128k" -default['mysql']['tunable']['read_rnd_buffer_size'] = "256k" -default['mysql']['tunable']['join_buffer_size'] = "128k" -default['mysql']['tunable']['wait_timeout'] = "180" -default['mysql']['tunable']['open-files-limit'] = "1024" +default['mysql']['tunable']['thread_stack'] = '256K' +default['mysql']['tunable']['sort_buffer_size'] = '2M' +default['mysql']['tunable']['read_buffer_size'] = '128k' +default['mysql']['tunable']['read_rnd_buffer_size'] = '256k' +default['mysql']['tunable']['join_buffer_size'] = '128k' +default['mysql']['tunable']['wait_timeout'] = '180' +default['mysql']['tunable']['open-files-limit'] = '1024' default['mysql']['tunable']['sql_mode'] = nil @@ -202,6 +85,13 @@ default['mysql']['tunable']['relay_log'] = nil default['mysql']['tunable']['relay_log_index'] = nil default['mysql']['tunable']['log_slave_updates'] = false +default['mysql']['tunable']['replicate_do_db'] = nil +default['mysql']['tunable']['replicate_do_table'] = nil +default['mysql']['tunable']['replicate_ignore_db'] = nil +default['mysql']['tunable']['replicate_ignore_table'] = nil +default['mysql']['tunable']['replicate_wild_do_table'] = nil +default['mysql']['tunable']['replicate_wild_ignore_table'] = nil + default['mysql']['tunable']['sync_binlog'] = 0 default['mysql']['tunable']['skip_slave_start'] = false default['mysql']['tunable']['read_only'] = false @@ -211,54 +101,68 @@ default['mysql']['tunable']['log_warnings'] = false default['mysql']['tunable']['log_queries_not_using_index'] = true default['mysql']['tunable']['log_bin_trust_function_creators'] = false -default['mysql']['tunable']['innodb_log_file_size'] = "5M" -default['mysql']['tunable']['innodb_buffer_pool_size'] = "128M" -default['mysql']['tunable']['innodb_buffer_pool_instances'] = "4" -default['mysql']['tunable']['innodb_additional_mem_pool_size'] = "8M" -default['mysql']['tunable']['innodb_data_file_path'] = "ibdata1:10M:autoextend" +default['mysql']['tunable']['innodb_log_file_size'] = '5M' +# default['mysql']['tunable']['innodb_buffer_pool_size'] = '128M' +default['mysql']['tunable']['innodb_buffer_pool_size'] = (node['memory']['total'].to_i/1024) < 4096 ? '128M' : '4096M' +default['mysql']['tunable']['innodb_buffer_pool_instances'] = '4' +default['mysql']['tunable']['innodb_additional_mem_pool_size'] = '8M' +default['mysql']['tunable']['innodb_data_file_path'] = 'ibdata1:10M:autoextend' default['mysql']['tunable']['innodb_flush_method'] = false -default['mysql']['tunable']['innodb_log_buffer_size'] = "8M" -default['mysql']['tunable']['innodb_write_io_threads'] = "4" -default['mysql']['tunable']['innodb_io_capacity'] = "200" +default['mysql']['tunable']['innodb_log_buffer_size'] = '8M' +default['mysql']['tunable']['innodb_write_io_threads'] = '4' +default['mysql']['tunable']['innodb_io_capacity'] = '200' default['mysql']['tunable']['innodb_file_per_table'] = true -default['mysql']['tunable']['innodb_lock_wait_timeout'] = "60" -if node['cpu'].nil? or node['cpu']['total'].nil? - default['mysql']['tunable']['innodb_thread_concurrency'] = "8" - default['mysql']['tunable']['innodb_commit_concurrency'] = "8" - default['mysql']['tunable']['innodb_read_io_threads'] = "8" +default['mysql']['tunable']['innodb_lock_wait_timeout'] = '60' +default['mysql']['tunable']['innodb_rollback_on_timeout'] = false +if node['cpu'].nil? || node['cpu']['total'].nil? + default['mysql']['tunable']['innodb_thread_concurrency'] = '8' + default['mysql']['tunable']['innodb_commit_concurrency'] = '8' + default['mysql']['tunable']['innodb_read_io_threads'] = '8' else - default['mysql']['tunable']['innodb_thread_concurrency'] = "#{(Integer(node['cpu']['total'])) * 2}" - default['mysql']['tunable']['innodb_commit_concurrency'] = "#{(Integer(node['cpu']['total'])) * 2}" - default['mysql']['tunable']['innodb_read_io_threads'] = "#{(Integer(node['cpu']['total'])) * 2}" + default['mysql']['tunable']['innodb_thread_concurrency'] = (node['cpu']['total'].to_i * 2).to_s + default['mysql']['tunable']['innodb_commit_concurrency'] = (node['cpu']['total'].to_i * 2).to_s + default['mysql']['tunable']['innodb_read_io_threads'] = (node['cpu']['total'].to_i * 2).to_s end -default['mysql']['tunable']['innodb_flush_log_at_trx_commit'] = "1" +default['mysql']['tunable']['innodb_flush_log_at_trx_commit'] = '1' default['mysql']['tunable']['innodb_support_xa'] = true default['mysql']['tunable']['innodb_table_locks'] = true default['mysql']['tunable']['skip-innodb-doublewrite'] = false default['mysql']['tunable']['transaction-isolation'] = nil -default['mysql']['tunable']['query_cache_limit'] = "1M" -default['mysql']['tunable']['query_cache_size'] = "16M" +default['mysql']['tunable']['query_cache_limit'] = '1M' +default['mysql']['tunable']['query_cache_size'] = '16M' -default['mysql']['tunable']['log_slow_queries'] = "/var/log/mysql/slow.log" -default['mysql']['tunable']['slow_query_log'] = node['mysql']['tunable']['log_slow_queries'] # log_slow_queries is deprecated - # in favor of slow_query_log default['mysql']['tunable']['long_query_time'] = 2 - default['mysql']['tunable']['expire_logs_days'] = 10 -default['mysql']['tunable']['max_binlog_size'] = "100M" -default['mysql']['tunable']['binlog_cache_size'] = "32K" +default['mysql']['tunable']['max_binlog_size'] = '100M' +default['mysql']['tunable']['binlog_cache_size'] = '32K' -default['mysql']['tmpdir'] = ["/tmp"] +default['mysql']['tmpdir'] = ['/tmp'] -default['mysql']['log_dir'] = node['mysql']['data_dir'] +# default['mysql']['log_dir'] = node['mysql']['data_dir'] default['mysql']['log_files_in_group'] = false default['mysql']['innodb_status_file'] = false -unless node['platform_family'] == "rhel" && node['platform_version'].to_i < 6 +unless node['platform_family'] == 'rhel' && node['platform_version'].to_i < 6 # older RHEL platforms don't support these options default['mysql']['tunable']['event_scheduler'] = 0 - default['mysql']['tunable']['table_open_cache'] = "128" - default['mysql']['tunable']['binlog_format'] = "statement" if node['mysql']['tunable']['log_bin'] + default['mysql']['tunable']['binlog_format'] = 'statement' if node['mysql']['tunable']['log_bin'] end + +# security options +# @see http://www.symantec.com/connect/articles/securing-mysql-step-step +# @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_chroot +default['mysql']['security']['chroot'] = nil +# @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_safe-user-create +default['mysql']['security']['safe_user_create'] = nil +# @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-auth +default['mysql']['security']['secure_auth'] = nil +# @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_symbolic-links +default['mysql']['security']['skip_symbolic_links'] = nil +# @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-file-priv +default['mysql']['security']['secure_file_priv'] = nil +# @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_skip-show-database +default['mysql']['security']['skip_show_database'] = nil +# @see http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_local_infile +default['mysql']['security']['local_infile'] = nil diff --git a/chef/cookbooks/mysql/attributes/server_debian.rb b/chef/cookbooks/mysql/attributes/server_debian.rb new file mode 100644 index 0000000..1228df6 --- /dev/null +++ b/chef/cookbooks/mysql/attributes/server_debian.rb @@ -0,0 +1,32 @@ +case node['platform_family'] +when 'debian' + + # Probably driven from wrapper cookbooks, environments, or roles. + # Keep in this namespace for backwards compat + default['mysql']['data_dir'] = '/var/lib/mysql' + + default['mysql']['server']['packages'] = %w{ mysql-server apparmor-utils } + default['mysql']['server']['slow_query_log'] = 1 + default['mysql']['server']['slow_query_log_file'] = '/var/log/mysql/slow.log' + + # Platformisms.. filesystem locations and such. + default['mysql']['server']['basedir'] = '/usr' + default['mysql']['server']['tmpdir'] = ['/tmp'] + + default['mysql']['server']['directories']['run_dir'] = '/var/run/mysqld' + default['mysql']['server']['directories']['log_dir'] = '/var/lib/mysql' + default['mysql']['server']['directories']['slow_log_dir'] = '/var/log/mysql' + default['mysql']['server']['directories']['confd_dir'] = '/etc/mysql/conf.d' + + default['mysql']['server']['mysqladmin_bin'] = '/usr/bin/mysqladmin' + default['mysql']['server']['mysql_bin'] = '/usr/bin/mysql' + + default['mysql']['server']['pid_file'] = '/var/run/mysqld/mysqld.pid' + default['mysql']['server']['socket'] = '/var/run/mysqld/mysqld.sock' + default['mysql']['server']['grants_path'] = '/etc/mysql_grants.sql' + default['mysql']['server']['old_passwords'] = 1 + + # wat + default['mysql']['tunable']['innodb_adaptive_flushing'] = false + default['mysql']['server']['skip_federated'] = false +end diff --git a/chef/cookbooks/mysql/attributes/server_freebsd.rb b/chef/cookbooks/mysql/attributes/server_freebsd.rb new file mode 100644 index 0000000..5a98ddd --- /dev/null +++ b/chef/cookbooks/mysql/attributes/server_freebsd.rb @@ -0,0 +1,17 @@ +case node['platform_family'] + +when 'freebsd' + default['mysql']['data_dir'] = '/var/db/mysql' + default['mysql']['server']['packages'] = %w[mysql55-server] + default['mysql']['server']['service_name'] = 'mysql-server' + default['mysql']['server']['basedir'] = '/usr/local' + default['mysql']['server']['root_group'] = 'wheel' + default['mysql']['server']['mysqladmin_bin'] = '/usr/local/bin/mysqladmin' + default['mysql']['server']['mysql_bin'] = '/usr/local/bin/mysql' + default['mysql']['server']['conf_dir'] = '/usr/local/etc' + default['mysql']['server']['confd_dir'] = '/usr/local/etc/mysql/conf.d' + default['mysql']['server']['socket'] = '/tmp/mysqld.sock' + default['mysql']['server']['pid_file'] = '/var/run/mysqld/mysqld.pid' + default['mysql']['server']['old_passwords'] = 0 + default['mysql']['server']['grants_path'] = '/var/db/mysql/grants.sql' +end diff --git a/chef/cookbooks/mysql/attributes/server_mac_os_x.rb b/chef/cookbooks/mysql/attributes/server_mac_os_x.rb new file mode 100644 index 0000000..8e33343 --- /dev/null +++ b/chef/cookbooks/mysql/attributes/server_mac_os_x.rb @@ -0,0 +1,9 @@ +case node['platform_family'] +when 'mac_os_x' + default['mysql']['server']['packages'] = %w[mysql] + default['mysql']['basedir'] = '/usr/local/Cellar' + default['mysql']['data_dir'] = '/usr/local/var/mysql' + default['mysql']['root_group'] = 'admin' + default['mysql']['mysqladmin_bin'] = '/usr/local/bin/mysqladmin' + default['mysql']['mysql_bin'] = '/usr/local/bin/mysql' +end diff --git a/chef/cookbooks/mysql/attributes/server_rhel.rb b/chef/cookbooks/mysql/attributes/server_rhel.rb new file mode 100644 index 0000000..61c8d6c --- /dev/null +++ b/chef/cookbooks/mysql/attributes/server_rhel.rb @@ -0,0 +1,45 @@ +case node['platform_family'] +when 'rhel' + + # Probably driven from wrapper cookbooks, environments, or roles. + # Keep in this namespace for backwards compat + default['mysql']['data_dir'] = '/var/lib/mysql' + + # switching logic to account for differences in platform native + # package versions + case node['platform_version'].to_i + when 5 + default['mysql']['server']['packages'] = ['mysql-server'] + default['mysql']['server']['log_slow_queries'] = '/var/log/mysql/slow.log' + when 6 + default['mysql']['server']['packages'] = ['mysql-server'] + default['mysql']['server']['slow_query_log'] = 1 + default['mysql']['server']['slow_query_log_file'] = '/var/log/mysql/slow.log' + when 2013 # amazon linux + default['mysql']['server']['packages'] = ['mysql-server'] + default['mysql']['server']['slow_query_log'] = 1 + default['mysql']['server']['slow_query_log_file'] = '/var/log/mysql/slow.log' + end + + # Platformisms.. filesystem locations and such. + default['mysql']['server']['basedir'] = '/usr' + default['mysql']['server']['tmpdir'] = ['/tmp'] + + default['mysql']['server']['directories']['run_dir'] = '/var/run/mysqld' + default['mysql']['server']['directories']['log_dir'] = '/var/lib/mysql' + default['mysql']['server']['directories']['slow_log_dir'] = '/var/log/mysql' + default['mysql']['server']['directories']['confd_dir'] = '/etc/mysql/conf.d' + + default['mysql']['server']['mysqladmin_bin'] = '/usr/bin/mysqladmin' + default['mysql']['server']['mysql_bin'] = '/usr/bin/mysql' + + default['mysql']['server']['pid_file'] = '/var/run/mysqld/mysqld.pid' + default['mysql']['server']['socket'] = '/var/lib/mysql/mysql.sock' + default['mysql']['server']['grants_path'] = '/etc/mysql_grants.sql' + default['mysql']['server']['old_passwords'] = 1 + default['mysql']['server']['service_name'] = 'mysqld' + + # RHEL/CentOS mysql package does not support this option. + default['mysql']['tunable']['innodb_adaptive_flushing'] = false + default['mysql']['server']['skip_federated'] = false +end diff --git a/chef/cookbooks/mysql/attributes/server_suse.rb b/chef/cookbooks/mysql/attributes/server_suse.rb new file mode 100644 index 0000000..6508360 --- /dev/null +++ b/chef/cookbooks/mysql/attributes/server_suse.rb @@ -0,0 +1,16 @@ +case node['platform_family'] +when 'suse' + default['mysql']['data_dir'] = '/var/lib/mysql' + default['mysql']['server']['service_name'] = 'mysql' + default['mysql']['server']['server']['packages'] = %w[mysql-community-server] + default['mysql']['server']['basedir'] = '/usr' + default['mysql']['server']['root_group'] = 'root' + default['mysql']['server']['mysqladmin_bin'] = '/usr/bin/mysqladmin' + default['mysql']['server']['mysql_bin'] = '/usr/bin/mysql' + default['mysql']['server']['conf_dir'] = '/etc' + default['mysql']['server']['confd_dir'] = '/etc/mysql/conf.d' + default['mysql']['server']['socket'] = '/var/run/mysql/mysql.sock' + default['mysql']['server']['pid_file'] = '/var/run/mysql/mysqld.pid' + default['mysql']['server']['old_passwords'] = 1 + default['mysql']['server']['grants_path'] = '/etc/mysql_grants.sql' +end diff --git a/chef/cookbooks/mysql/attributes/server_windows.rb b/chef/cookbooks/mysql/attributes/server_windows.rb new file mode 100644 index 0000000..10552f5 --- /dev/null +++ b/chef/cookbooks/mysql/attributes/server_windows.rb @@ -0,0 +1,22 @@ +case node['platform_family'] +when 'windows' + default['mysql']['windows']['version'] = '5.5.34' + default['mysql']['windows']['arch'] = node['kernel']['machine'] == 'x86_64' ? 'winx64' : 'win32' + default['mysql']['windows']['package_file'] = "mysql-#{node['mysql']['windows']['version']}-#{node['mysql']['windows']['arch']}.msi" + default['mysql']['windows']['packages'] = ['MySQL Server 5.5'] + default['mysql']['windows']['url'] = "http://dev.mysql.com/get/Downloads/MySQL-5.5/#{node['mysql']['windows']['package_file']}" + + default['mysql']['windows']['programdir'] = node['kernel']['machine'] == 'x86_64' ? 'Program Files' : 'Program Files (x86)' + default['mysql']['windows']['basedir'] = "#{ENV['SYSTEMDRIVE']}\\#{node['mysql']['windows']['programdir']}\\MySQL\\#{node['mysql']['windows']['packages'].first}" + default['mysql']['windows']['data_dir'] = "#{ENV['ProgramData']}\\MySQL\\#{node['mysql']['windows']['packages'].first}\\Data" + default['mysql']['windows']['bin_dir'] = "#{node['mysql']['windows']['basedir']}\\bin" + default['mysql']['windows']['mysqladmin_bin'] = "#{node['mysql']['windows']['bin_dir']}\\mysqladmin" + default['mysql']['windows']['mysql_bin'] = "#{node['mysql']['windows']['bin_dir']}\\mysql" + + default['mysql']['windows']['conf_dir'] = node['mysql']['windows']['basedir'] + default['mysql']['windows']['old_passwords'] = 0 + default['mysql']['windows']['grants_path'] = "#{node['mysql']['windows']['conf_dir']}\\grants.sql" + + default['mysql']['server']['service_name'] = 'mysql' + default['mysql']['server']['slow_query_log'] = 1 +end diff --git a/chef/cookbooks/mysql/libraries/helpers.rb b/chef/cookbooks/mysql/libraries/helpers.rb index 40adcfc..c850233 100644 --- a/chef/cookbooks/mysql/libraries/helpers.rb +++ b/chef/cookbooks/mysql/libraries/helpers.rb @@ -1,7 +1,8 @@ # # Author:: Seth Chisamore () -# Copyright:: Copyright (c) 2011 Opscode, Inc. -# License:: Apache License, Version 2.0 +# Author:: Sean OMeara () +# +# Copyright:: Copyright (c) 2011-2013 Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,16 +19,27 @@ module Opscode module Mysql + # Opscode Mysql Helpers module Helpers - def debian_before_squeeze? - (node['platform'] == "debian") && (node['platform_version'].to_f < 6.0) + (node['platform'] == 'debian') && (node['platform_version'].to_f < 6.0) end def ubuntu_before_lucid? - (node['platform'] == "ubuntu") && (node['platform_version'].to_f < 10.0) + (node['platform'] == 'ubuntu') && (node['platform_version'].to_f < 10.0) end + def assign_root_password_cmd + str = '/usr/bin/mysqladmin' + str << ' -u root password ' + str << node['mysql']['server_root_password'] + end + + def install_grants_cmd + str = '/usr/bin/mysql' + str << ' -u root ' + node['mysql']['server_root_password'].empty? ? str << ' < /etc/mysql_grants.sql' : str << " -p#{node['mysql']['server_root_password']} < /etc/mysql_grants.sql" + end end end end diff --git a/chef/cookbooks/mysql/metadata.json b/chef/cookbooks/mysql/metadata.json new file mode 100644 index 0000000..2d92621 --- /dev/null +++ b/chef/cookbooks/mysql/metadata.json @@ -0,0 +1,167 @@ +{ + "name": "mysql", + "version": "4.1.2", + "description": "Installs and configures mysql for client or server", + "long_description": "mysql Cookbook\n==============\n[![Build Status](https://secure.travis-ci.org/opscode-cookbooks/mysql.png?branch=master)](http://travis-ci.org/opscode-cookbooks/mysql)\n\nInstalls and configures MySQL client or server.\n\nRequirements\n------------\nChef 0.10.10+.\n\nPlatform\n--------\n- Debian, Ubuntu\n- CentOS, Red Hat, Fedora\n- Mac OS X (Using homebrew)\n\nTested on:\n\n- Ubuntu 10.04, 12.04\n- CentOS 5.9, 6.5\n\nSee TESTING.md for information about running tests in Opscode's Test Kitchen.\n\n\nCookbooks\n---------\nRequires Opscode's openssl cookbook for secure password generation. See _Attributes_ and _Usage_ for more information.\n\nThe RubyGem installation in the `mysql::ruby` recipe requires a C compiler and Ruby development headers to be installed in order to build the mysql gem.\n\nRequires `homebrew` [cookbook](http://community.opscode.com/cookbooks/homebrew) on Mac OS X.\n\n\nResources and Providers\n-----------------------\nThe LWRP that used to ship as part of this cookbook has been refactored into the\n[database](http://community.opscode.com/cookbooks/database) cookbook. Please see the README for details on updated usage.\n\n\nAttributes\n----------\nSee the `attributes/server.rb` or `attributes/client.rb` for default values. Several attributes have values that vary based on the node's platform and version.\n\n* `node['mysql']['port']` - Listen port for MySQLd\n* `node['mysql']['data_dir']` - Location for mysql data directory. `WARNING` This will only on initial converge. It will not move data around if you change it.\n\n* `node['mysql']['client']['packages']` - An array of package names\n that should be installed on \"client\" systems. This can be modified,\n e.g., to specify packages for Percona.\n* `node['mysql']['server']['packages']` - An array of package names\n that should be installed on \"server\" systems. This can be modified,\n e.g., to specify packages for Percona.\n* `node['mysql']['auto-increment-increment']` -\n auto-increment-increment value in my.cnf\n* `node['mysql']['auto-increment-offset]` - auto-increment-offset value in my.cnf\n* `node['mysql']['server']['basedir']` - Base directory where MySQL is installed\n* `node['mysql']['bind_address']` - Listen address for MySQLd\n* `node['mysql']['ec2_path']` - location of mysql data_dir on EC2 nodes\n* `node['mysql']['grants_path']` - Path where the grants.sql should be written\n* `node['mysql']['mysqladmin_bin']` - Path to the mysqladmin binary\n* `node['mysql']['server']['old_passwords']` - Sets the `old_passwords` value in my.cnf.\n* `node['mysql']['server']['pid_file']` - Path to the mysqld.pid file\n\n* `node['mysql']['server']['reload_action']` - Action to take when mysql conf\n files are modified. Also allows \"reload\" and \"none\".\n* `node['mysql']['server']['root_group']` - The default group of the \"root\" user\n* `node['mysql']['server']['service_name']` - The name of the mysqld service\n* `node['mysql']['server']['socket']` - Path to the mysqld.sock file\n* `mysql['root_network_acl']` - Set define the network the root user will be able to login from, default is nil\n\nPerformance and other \"tunable\" attributes are under the `node['mysql']['tunable']` attribute, corresponding to the same-named parameter in my.cnf, and the default values are used. See `attributes/server.rb`.\n\nBy default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment.\n\n* `node['mysql']['remove_anonymous_users']` - Remove anonymous users\n\nNormally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network.\n\n* `node['mysql']['allow_remote_root']` - If true Sets root access from '%'. If false deletes any non-localhost root users.\n\nBy default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. This will also drop any user privileges to the test database and any DB named test_% .\n\n* `node['mysql']['remove_test_database']` - Delete the test database and access to it.\n\nThe following attributes are randomly generated passwords handled in the `mysql::server` recipe, using the OpenSSL cookbook's `secure_password` helper method. These are set using the `set_unless` node attribute method, which allows them to be easily overridden e.g.\nin a role.\n\n* `node['mysql']['server_root_password']` - Set the server's root\n password\n* `node['mysql']['server_repl_password']` - Set the replication user\n 'repl' password\n* `node['mysql']['server_debian_password']` - Set the debian-sys-maint\n user password\n\n### Windows Specific\n\nThe following attributes are specific to Windows platforms.\n\n* `node['mysql']['client']['version']` - The version of MySQL\n connector to install.\n* `node['mysql']['client']['arch']` - Force 32 bit to work with the\n mysql gem\n* `node['mysql']['client']['package_file']` - The MSI file for the\n mysql connector.\n* `node['mysql']['client']['url']` - URL to download the mysql\n connector.\n* `node['mysql']['client']['packages']` - Similar to other platforms,\n this is the name of the client package.\n* `node['mysql']['client']['basedir']` - Base installation location\n* `node['mysql']['client']['lib_dir']` - Libraries under the base location\n* `node['mysql']['client']['bin_dir']` - binary directory under base location\n* `node['mysql']['client']['ruby_dir']` - location where the Ruby\n binaries will be\n\n## Security Options\n\nFurther information is already available at [Symantec](http://www.symantec.com/connect/articles/securing-mysql-step-step) and [Deutsche Telekom (German)](http://www.telekom.com/static/-/155996/7/technische-sicherheitsanforderungen-si)\n\n* default['mysql']['security']['chroot'] - [chroot](http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_chroot)\n* default['mysql']['security']['safe_user_create'] - [safe-user-create](http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_safe-user-create)\n* default['mysql']['security']['secure_auth'] - [secure-auth](http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-auth)\n* default['mysql']['security']['skip_symbolic_links'] - [skip-symbolic-links](http://dev.mysql.com/doc/refman/5.7/en/server-\noptions.html#option_mysqld_symbolic-links)\n* default['mysql']['security']['skip_show_database'] - [skip-show-database](http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_skip-show-database)\n* default['mysql']['security']['local_infile'] - [local-infile](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_local_infile)\n\nUsage\n-----\nOn client nodes, use the client (or default) recipe:\n\n```javascript\n{ \"run_list\": [\"recipe[mysql::client]\"] }\n```\n\nThis will install the MySQL client libraries and development headers on the system.\n\nOn nodes which may use the `database` cookbook's mysql resources, also use the ruby recipe. This installs the mysql RubyGem in the Ruby environment Chef is using via `chef_gem`.\n\n```javascript\n{ \"run_list\": [\"recipe[mysql::client]\", \"recipe[mysql::ruby]\"] }\n```\n\nIf you need to install the mysql Ruby library as a package for your system, override the client packages attribute in your node or role. For example, on an Ubuntu system:\n\n```javascript\n{\n \"mysql\": {\n \"client\": {\n \"packages\": [\"mysql-client\", \"libmysqlclient-dev\",\"ruby-mysql\"]\n }\n }\n}\n```\n\nThis creates a resource object for the package and does the installation before other recipes are parsed. You'll need to have the C compiler and such (ie, build-essential on Ubuntu) before running the recipes, but we already do that when installing Chef :-).\n\nOn server nodes, use the server recipe:\n\n```javascript\n{ \"run_list\": [\"recipe[mysql::server]\"] }\n```\n\nOn Debian and Ubuntu, this will preseed the mysql-server package with the randomly generated root password in the recipe file. On other platforms, it simply installs the required packages. It will also create an SQL file, `/etc/mysql/grants.sql`, that will be used to set up grants for the root, repl and debian-sys-maint users.\n\nThe recipe will perform a `node.save` unless it is run under `chef-solo` after the password attributes are used to ensure that in the event of a failed run, the saved attributes would be used.\n\nOn EC2 nodes, use the `server_ec2` recipe and the mysql data dir will be set up in the ephmeral storage.\n\n```javascript\n{ \"run_list\": [\"recipe[mysql::server_ec2]\"] }\n```\n\nWhen the `ec2_path` doesn't exist we look for a mounted filesystem (eg, EBS) and move the data_dir there.\n\nThe client recipe is already included by server and 'default' recipes.\n\nFor more infromation on the compile vs execution phase of a Chef run:\n\n- http://wiki.opscode.com/display/chef/Anatomy+of+a+Chef+Run\n\n\nChef Solo Note\n--------------\nThese node attributes are stored on the Chef server when using `chef-client`. Because `chef-solo` does not connect to a server or save the node object at all, to have the same passwords persist across `chef-solo` runs, you must specify them in the `json_attribs` file used. For example:\n\n```javascript\n{\n \"mysql\": {\n \"server_root_password\": \"iloverandompasswordsbutthiswilldo\",\n \"server_repl_password\": \"iloverandompasswordsbutthiswilldo\",\n \"server_debian_password\": \"iloverandompasswordsbutthiswilldo\"\n },\n \"run_list\":[\"recipe[mysql::server]\"]\n}\n```\n\n\nLicense & Authors\n-----------------\n- Author:: Joshua Timberman ()\n- Author:: AJ Christensen ()\n- Author:: Seth Chisamore ()\n- Author:: Brian Bianco ()\n- Author:: Jesse Howarth ()\n- Author:: Andrew Crump ()\n- Author:: Christoph Hartmann ()\n- Author:: Sean OMeara ()\n\n```text\nCopyright:: 2009-2013 Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "redhat": ">= 0.0.0", + "amazon": ">= 0.0.0", + "centos": ">= 0.0.0", + "debian": ">= 0.0.0", + "ubuntu": ">= 0.0.0", + "freebsd": ">= 0.0.0", + "mac_os_x": ">= 0.0.0", + "scientific": ">= 0.0.0", + "suse": ">= 0.0.0", + "windows": ">= 0.0.0" + }, + "dependencies": { + "openssl": "~> 1.1", + "build-essential": "~> 1.4", + "homebrew": ">= 0.0.0", + "windows": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + "mysql/server_root_password": { + "display_name": "MySQL Server Root Password", + "description": "Randomly generated password for the mysqld root user", + "default": "randomly generated" + }, + "mysql/bind_address": { + "display_name": "MySQL Bind Address", + "description": "Address that mysqld should listen on", + "default": "ipaddress" + }, + "mysql/data_dir": { + "display_name": "MySQL Data Directory", + "description": "Location of mysql databases", + "default": "/var/lib/mysql" + }, + "mysql/conf_dir": { + "display_name": "MySQL Conf Directory", + "description": "Location of mysql conf files", + "default": "/etc/mysql" + }, + "mysql/ec2_path": { + "display_name": "MySQL EC2 Path", + "description": "Location of mysql directory on EC2 instance EBS volumes", + "default": "/mnt/mysql" + }, + "mysql/reload_action": { + "display_name": "MySQL conf file reload action", + "description": "Action to take when mysql conf files are modified", + "default": "reload" + }, + "mysql/tunable": { + "display_name": "MySQL Tunables", + "description": "Hash of MySQL tunable attributes", + "type": "hash" + }, + "mysql/tunable/key_buffer": { + "display_name": "MySQL Tuntable Key Buffer", + "default": "250M" + }, + "mysql/tunable/max_connections": { + "display_name": "MySQL Tunable Max Connections", + "default": "800" + }, + "mysql/tunable/wait_timeout": { + "display_name": "MySQL Tunable Wait Timeout", + "default": "180" + }, + "mysql/tunable/net_read_timeout": { + "display_name": "MySQL Tunable Net Read Timeout", + "default": "30" + }, + "mysql/tunable/net_write_timeout": { + "display_name": "MySQL Tunable Net Write Timeout", + "default": "30" + }, + "mysql/tunable/back_log": { + "display_name": "MySQL Tunable Back Log", + "default": "128" + }, + "mysql/tunable/table_cache": { + "display_name": "MySQL Tunable Table Cache for MySQL < 5.1.3", + "default": "128" + }, + "mysql/tunable/table_open_cache": { + "display_name": "MySQL Tunable Table Cache for MySQL >= 5.1.3", + "default": "128" + }, + "mysql/tunable/max_heap_table_size": { + "display_name": "MySQL Tunable Max Heap Table Size", + "default": "32M" + }, + "mysql/tunable/expire_logs_days": { + "display_name": "MySQL Exipre Log Days", + "default": "10" + }, + "mysql/tunable/max_binlog_size": { + "display_name": "MySQL Max Binlog Size", + "default": "100M" + }, + "mysql/client": { + "display_name": "MySQL Connector/C Client", + "description": "Hash of MySQL client attributes", + "type": "hash" + }, + "mysql/client/version": { + "display_name": "MySQL Connector/C Version", + "default": "6.0.2" + }, + "mysql/client/arch": { + "display_name": "MySQL Connector/C Architecture", + "default": "win32" + }, + "mysql/client/package_file": { + "display_name": "MySQL Connector/C Package File Name", + "default": "mysql-connector-c-6.0.2-win32.msi" + }, + "mysql/client/url": { + "display_name": "MySQL Connector/C Download URL", + "default": "http://www.mysql.com/get/Downloads/Connector-C/mysql-connector-c-6.0.2-win32.msi/from/http://mysql.mirrors.pair.com/" + }, + "mysql/client/package_name": { + "display_name": "MySQL Connector/C Registry DisplayName", + "default": "MySQL Connector C 6.0.2" + }, + "mysql/client/basedir": { + "display_name": "MySQL Connector/C Base Install Directory", + "default": "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2" + }, + "mysql/client/lib_dir": { + "display_name": "MySQL Connector/C Library Directory (containing libmysql.dll)", + "default": "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\lib\\opt" + }, + "mysql/client/bin_dir": { + "display_name": "MySQL Connector/C Executable Directory", + "default": "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\bin" + }, + "mysql/client/ruby_dir": { + "display_name": "Ruby Executable Directory which should gain MySQL support", + "default": "system ruby" + } + }, + "groupings": { + }, + "recipes": { + "mysql": "Includes the client recipe to configure a client", + "mysql::client": "Installs packages required for mysql clients using run_action magic", + "mysql::server": "Installs packages required for mysql servers w/o manual intervention", + "mysql::server_ec2": "Performs EC2-specific mountpoint manipulation" + } +} \ No newline at end of file diff --git a/chef/cookbooks/mysql/metadata.rb b/chef/cookbooks/mysql/metadata.rb index e47c693..81937eb 100644 --- a/chef/cookbooks/mysql/metadata.rb +++ b/chef/cookbooks/mysql/metadata.rb @@ -1,140 +1,153 @@ -name "mysql" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Installs and configures mysql for client or server" +name 'mysql' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Installs and configures mysql for client or server' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "3.0.5" -recipe "mysql", "Includes the client recipe to configure a client" -recipe "mysql::client", "Installs packages required for mysql clients using run_action magic" -recipe "mysql::server", "Installs packages required for mysql servers w/o manual intervention" -recipe "mysql::server_ec2", "Performs EC2-specific mountpoint manipulation" +version '4.1.2' +recipe 'mysql', 'Includes the client recipe to configure a client' +recipe 'mysql::client', 'Installs packages required for mysql clients using run_action magic' +recipe 'mysql::server', 'Installs packages required for mysql servers w/o manual intervention' +recipe 'mysql::server_ec2', 'Performs EC2-specific mountpoint manipulation' -%w{ debian ubuntu centos suse fedora redhat scientific amazon freebsd windows mac_os_x }.each do |os| - supports os -end +# actually tested on +supports 'redhat' +supports 'amazon' +supports 'centos' +supports 'debian' +supports 'ubuntu' -depends "openssl" -depends "build-essential", "> 1.1.0" -suggests "homebrew" -suggests "windows" +# code bits around, untested. remove? +supports 'freebsd' +supports 'mac_os_x' +supports 'scientific' +supports 'suse' +supports 'windows' -attribute "mysql/server_root_password", - :display_name => "MySQL Server Root Password", - :description => "Randomly generated password for the mysqld root user", - :default => "randomly generated" +depends 'openssl', '~> 1.1' +depends 'build-essential', '~> 1.4' -attribute "mysql/bind_address", - :display_name => "MySQL Bind Address", - :description => "Address that mysqld should listen on", - :default => "ipaddress" +# wat +depends 'homebrew' +depends 'windows' -attribute "mysql/data_dir", - :display_name => "MySQL Data Directory", - :description => "Location of mysql databases", - :default => "/var/lib/mysql" +# remove all these attributes from metadata? +attribute 'mysql/server_root_password', + :display_name => 'MySQL Server Root Password', + :description => 'Randomly generated password for the mysqld root user', + :default => 'randomly generated' -attribute "mysql/conf_dir", - :display_name => "MySQL Conf Directory", - :description => "Location of mysql conf files", - :default => "/etc/mysql" +attribute 'mysql/bind_address', + :display_name => 'MySQL Bind Address', + :description => 'Address that mysqld should listen on', + :default => 'ipaddress' -attribute "mysql/ec2_path", - :display_name => "MySQL EC2 Path", - :description => "Location of mysql directory on EC2 instance EBS volumes", - :default => "/mnt/mysql" +attribute 'mysql/data_dir', + :display_name => 'MySQL Data Directory', + :description => 'Location of mysql databases', + :default => '/var/lib/mysql' -attribute "mysql/reload_action", - :display_name => "MySQL conf file reload action", - :description => "Action to take when mysql conf files are modified", - :default => "reload" +attribute 'mysql/conf_dir', + :display_name => 'MySQL Conf Directory', + :description => 'Location of mysql conf files', + :default => '/etc/mysql' -attribute "mysql/tunable", - :display_name => "MySQL Tunables", - :description => "Hash of MySQL tunable attributes", - :type => "hash" +attribute 'mysql/ec2_path', + :display_name => 'MySQL EC2 Path', + :description => 'Location of mysql directory on EC2 instance EBS volumes', + :default => '/mnt/mysql' -attribute "mysql/tunable/key_buffer", - :display_name => "MySQL Tuntable Key Buffer", - :default => "250M" +attribute 'mysql/reload_action', + :display_name => 'MySQL conf file reload action', + :description => 'Action to take when mysql conf files are modified', + :default => 'reload' -attribute "mysql/tunable/max_connections", - :display_name => "MySQL Tunable Max Connections", - :default => "800" +attribute 'mysql/tunable', + :display_name => 'MySQL Tunables', + :description => 'Hash of MySQL tunable attributes', + :type => 'hash' -attribute "mysql/tunable/wait_timeout", - :display_name => "MySQL Tunable Wait Timeout", - :default => "180" +attribute 'mysql/tunable/key_buffer', + :display_name => 'MySQL Tuntable Key Buffer', + :default => '250M' -attribute "mysql/tunable/net_read_timeout", - :display_name => "MySQL Tunable Net Read Timeout", - :default => "30" +attribute 'mysql/tunable/max_connections', + :display_name => 'MySQL Tunable Max Connections', + :default => '800' -attribute "mysql/tunable/net_write_timeout", - :display_name => "MySQL Tunable Net Write Timeout", - :default => "30" +attribute 'mysql/tunable/wait_timeout', + :display_name => 'MySQL Tunable Wait Timeout', + :default => '180' -attribute "mysql/tunable/back_log", - :display_name => "MySQL Tunable Back Log", - :default => "128" +attribute 'mysql/tunable/net_read_timeout', + :display_name => 'MySQL Tunable Net Read Timeout', + :default => '30' -attribute "mysql/tunable/table_cache", - :display_name => "MySQL Tunable Table Cache for MySQL < 5.1.3", - :default => "128" +attribute 'mysql/tunable/net_write_timeout', + :display_name => 'MySQL Tunable Net Write Timeout', + :default => '30' -attribute "mysql/tunable/table_open_cache", - :display_name => "MySQL Tunable Table Cache for MySQL >= 5.1.3", - :default => "128" +attribute 'mysql/tunable/back_log', + :display_name => 'MySQL Tunable Back Log', + :default => '128' -attribute "mysql/tunable/max_heap_table_size", - :display_name => "MySQL Tunable Max Heap Table Size", - :default => "32M" +attribute 'mysql/tunable/table_cache', + :display_name => 'MySQL Tunable Table Cache for MySQL < 5.1.3', + :default => '128' -attribute "mysql/tunable/expire_logs_days", - :display_name => "MySQL Exipre Log Days", - :default => "10" +attribute 'mysql/tunable/table_open_cache', + :display_name => 'MySQL Tunable Table Cache for MySQL >= 5.1.3', + :default => '128' -attribute "mysql/tunable/max_binlog_size", - :display_name => "MySQL Max Binlog Size", - :default => "100M" +attribute 'mysql/tunable/max_heap_table_size', + :display_name => 'MySQL Tunable Max Heap Table Size', + :default => '32M' -attribute "mysql/client", - :display_name => "MySQL Connector/C Client", - :description => "Hash of MySQL client attributes", - :type => "hash" +attribute 'mysql/tunable/expire_logs_days', + :display_name => 'MySQL Exipre Log Days', + :default => '10' -attribute "mysql/client/version", - :display_name => "MySQL Connector/C Version", - :default => "6.0.2" +attribute 'mysql/tunable/max_binlog_size', + :display_name => 'MySQL Max Binlog Size', + :default => '100M' -attribute "mysql/client/arch", - :display_name => "MySQL Connector/C Architecture", - :default => "win32" +attribute 'mysql/client', + :display_name => 'MySQL Connector/C Client', + :description => 'Hash of MySQL client attributes', + :type => 'hash' -attribute "mysql/client/package_file", - :display_name => "MySQL Connector/C Package File Name", - :default => "mysql-connector-c-6.0.2-win32.msi" +attribute 'mysql/client/version', + :display_name => 'MySQL Connector/C Version', + :default => '6.0.2' -attribute "mysql/client/url", - :display_name => "MySQL Connector/C Download URL", - :default => "http://www.mysql.com/get/Downloads/Connector-C/mysql-connector-c-6.0.2-win32.msi/from/http://mysql.mirrors.pair.com/" +attribute 'mysql/client/arch', + :display_name => 'MySQL Connector/C Architecture', + :default => 'win32' -attribute "mysql/client/package_name", - :display_name => "MySQL Connector/C Registry DisplayName", - :default => "MySQL Connector C 6.0.2" +attribute 'mysql/client/package_file', + :display_name => 'MySQL Connector/C Package File Name', + :default => 'mysql-connector-c-6.0.2-win32.msi' -attribute "mysql/client/basedir", - :display_name => "MySQL Connector/C Base Install Directory", - :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2" +attribute 'mysql/client/url', + :display_name => 'MySQL Connector/C Download URL', + :default => 'http://www.mysql.com/get/Downloads/Connector-C/mysql-connector-c-6.0.2-win32.msi/from/http://mysql.mirrors.pair.com/' -attribute "mysql/client/lib_dir", - :display_name => "MySQL Connector/C Library Directory (containing libmysql.dll)", - :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\lib\\opt" +attribute 'mysql/client/package_name', + :display_name => 'MySQL Connector/C Registry DisplayName', + :default => 'MySQL Connector C 6.0.2' -attribute "mysql/client/bin_dir", - :display_name => "MySQL Connector/C Executable Directory", - :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\bin" +attribute 'mysql/client/basedir', + :display_name => 'MySQL Connector/C Base Install Directory', + :default => 'C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2' -attribute "mysql/client/ruby_dir", - :display_name => "Ruby Executable Directory which should gain MySQL support", - :default => "system ruby" +attribute 'mysql/client/lib_dir', + :display_name => 'MySQL Connector/C Library Directory (containing libmysql.dll)', + :default => 'C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\lib\\opt' + +attribute 'mysql/client/bin_dir', + :display_name => 'MySQL Connector/C Executable Directory', + :default => 'C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\bin' + +attribute 'mysql/client/ruby_dir', + :display_name => 'Ruby Executable Directory which should gain MySQL support', + :default => 'system ruby' diff --git a/chef/cookbooks/mysql/recipes/_server_debian.rb b/chef/cookbooks/mysql/recipes/_server_debian.rb new file mode 100644 index 0000000..596e44b --- /dev/null +++ b/chef/cookbooks/mysql/recipes/_server_debian.rb @@ -0,0 +1,134 @@ +#---- +# Set up preseeding data for debian packages +#--- +directory '/var/cache/local/preseeding' do + owner 'root' + group 'root' + mode '0755' + recursive true +end + +template '/var/cache/local/preseeding/mysql-server.seed' do + source 'mysql-server.seed.erb' + owner 'root' + group 'root' + mode '0600' + notifies :run, 'execute[preseed mysql-server]', :immediately +end + +execute 'preseed mysql-server' do + command '/usr/bin/debconf-set-selections /var/cache/local/preseeding/mysql-server.seed' + action :nothing +end + +#---- +# Install software +#---- +node['mysql']['server']['packages'].each do |name| + package name do + action :install + end +end + +node['mysql']['server']['directories'].each do |key, value| + directory value do + owner 'mysql' + group 'mysql' + mode '0775' + action :create + recursive true + end +end + +#---- +# Grants +#---- +template '/etc/mysql_grants.sql' do + source 'grants.sql.erb' + owner 'root' + group 'root' + mode '0600' + notifies :run, 'execute[install-grants]', :immediately +end + +cmd = install_grants_cmd +execute 'install-grants' do + command cmd + action :nothing +end + +template '/etc/mysql/debian.cnf' do + source 'debian.cnf.erb' + owner 'root' + group 'root' + mode '0600' + notifies :reload, 'service[mysql]' +end + +#---- +# data_dir +#---- + +# DRAGONS! +# Setting up data_dir will only work on initial node converge... +# Data will NOT be moved around the filesystem when you change data_dir +# To do that, we'll need to stash the data_dir of the last chef-client +# run somewhere and read it. Implementing that will come in "The Future" + +directory node['mysql']['data_dir'] do + owner 'mysql' + group 'mysql' + action :create + recursive true +end + +template '/etc/init/mysql.conf' do + source 'init-mysql.conf.erb' + only_if { node['platform'] == 'ubuntu' } +end + +template '/etc/apparmor.d/usr.sbin.mysqld' do + source 'usr.sbin.mysqld.erb' + action :create + notifies :reload, 'service[apparmor-mysql]', :immediately +end + +service 'apparmor-mysql' do + service_name 'apparmor' + action :nothing + supports :reload => true +end + +template '/etc/mysql/my.cnf' do + source 'my.cnf.erb' + owner 'root' + group 'root' + mode '0644' + notifies :run, 'bash[move mysql data to datadir]', :immediately + notifies :reload, 'service[mysql]' +end + +# don't try this at home +# http://ubuntuforums.org/showthread.php?t=804126 +bash 'move mysql data to datadir' do + user 'root' + code <<-EOH + /usr/sbin/service mysql stop && + mv /var/lib/mysql/* #{node['mysql']['data_dir']} && + /usr/sbin/service mysql start + EOH + action :nothing + only_if "[ '/var/lib/mysql' != #{node['mysql']['data_dir']} ]" + only_if "[ `stat -c %h #{node['mysql']['data_dir']}` -eq 2 ]" + not_if '[ `stat -c %h /var/lib/mysql/` -eq 2 ]' +end + +service_provider = Chef::Provider::Service::Upstart if 'ubuntu' == node['platform'] && + Chef::VersionConstraint.new('>= 13.10').include?(node['platform_version']) + +service 'mysql' do + provider service_provider + service_name 'mysql' + supports :status => true, :restart => true, :reload => true + action [:enable, :start] +end diff --git a/chef/cookbooks/mysql/recipes/_server_mac_os_x.rb b/chef/cookbooks/mysql/recipes/_server_mac_os_x.rb new file mode 100644 index 0000000..e14632c --- /dev/null +++ b/chef/cookbooks/mysql/recipes/_server_mac_os_x.rb @@ -0,0 +1,40 @@ + +#---- + +group 'mysql' do + action :create +end + +user 'mysql' do + comment 'MySQL Server' + gid 'mysql' + system true + home node['mysql']['data_dir'] + shell '/sbin/nologin' +end + +node['mysql']['server']['packages'].each do |name| + package name do + action :install + notifies :start, 'service[mysql]', :immediately + end +end + +#---- + +execute 'mysql-install-db' do + command "mysql_install_db --verbose --user=`whoami` --basedir=\"$(brew --prefix mysql)\" --datadir=#{node['mysql']['data_dir']} --tmpdir=/tmp" + environment('TMPDIR' => nil) + action :run + creates "#{node['mysql']['data_dir']}/mysql" +end + +# set the root password for situations that don't support pre-seeding. +# (eg. platforms other than debian/ubuntu & drop-in mysql replacements) +execute 'assign-root-password mac_os_x' do + command %Q["#{node['mysql']['mysqladmin_bin']}" -u root password '#{node['mysql']['server_root_password']}'] + action :run + only_if %Q["#{node['mysql']['mysql_bin']}" -u root -e 'show databases;'] +end + +#---- diff --git a/chef/cookbooks/mysql/recipes/_server_rhel.rb b/chef/cookbooks/mysql/recipes/_server_rhel.rb new file mode 100644 index 0000000..b4d215a --- /dev/null +++ b/chef/cookbooks/mysql/recipes/_server_rhel.rb @@ -0,0 +1,86 @@ +# require 'pry' + +node['mysql']['server']['packages'].each do |name| + package name do + action :install + end +end + +#---- +node['mysql']['server']['directories'].each do |key, value| + directory value do + owner 'mysql' + group 'mysql' + mode '0755' + action :create + recursive true + end +end + +directory node['mysql']['data_dir'] do + owner 'mysql' + group 'mysql' + action :create + recursive true +end + +#---- +template 'initial-my.cnf' do + path '/etc/my.cnf' + source 'my.cnf.erb' + owner 'root' + group 'root' + mode '0644' + notifies :start, 'service[mysql-start]', :immediately +end + +# hax +service 'mysql-start' do + service_name node['mysql']['server']['service_name'] + action :nothing +end + +execute '/usr/bin/mysql_install_db' do + action :run + creates '/var/lib/mysql/user.frm' + only_if { node['platform_version'].to_i < 6 } +end + +cmd = assign_root_password_cmd +execute 'assign-root-password' do + command cmd + action :run + only_if "/usr/bin/mysql -u root -e 'show databases;'" +end + +template '/etc/mysql_grants.sql' do + source 'grants.sql.erb' + owner 'root' + group 'root' + mode '0600' + action :create + notifies :run, 'execute[install-grants]', :immediately +end + +cmd = install_grants_cmd +execute 'install-grants' do + command cmd + action :nothing + notifies :restart, 'service[mysql]', :immediately +end + +#---- +template 'final-my.cnf' do + path '/etc/my.cnf' + source 'my.cnf.erb' + owner 'root' + group 'root' + mode '0644' + notifies :reload, 'service[mysql]', :immediately +end + +service 'mysql' do + service_name node['mysql']['server']['service_name'] + supports :status => true, :restart => true, :reload => true + action [:enable, :start] +end diff --git a/chef/cookbooks/mysql/recipes/_server_windows.rb b/chef/cookbooks/mysql/recipes/_server_windows.rb new file mode 100644 index 0000000..46f48e0 --- /dev/null +++ b/chef/cookbooks/mysql/recipes/_server_windows.rb @@ -0,0 +1,64 @@ +require 'win32/service' + +ENV['PATH'] += ";#{node['mysql']['windows']['bin_dir']}" +package_file = Chef::Config[:file_cache_path] + File::ALT_SEPARATOR + node['mysql']['windows']['package_file'] +install_dir = win_friendly_path(node['mysql']['windows']['basedir']) + +windows_path node['mysql']['windows']['bin_dir'] do + action :add +end + +remote_file package_file do + source node['mysql']['windows']['url'] + not_if { ::File.exists?(package_file) } +end + +windows_package node['mysql']['windows']['packages'].first do + source package_file + options "INSTALLDIR=\"#{install_dir}\"" + notifies :run, 'execute[install mysql service]', :immediately +end + +template 'my.ini' do + path "#{node['mysql']['windows']['bin_dir']}\\my.ini" + source 'my.ini.erb' + notifies :restart, 'service[mysql]' +end + +execute 'install mysql service' do + command %Q["#{node['mysql']['windows']['bin_dir']}\\mysqld.exe" --install "#{node['mysql']['server']['service_name']}" --defaults-file="#{node['mysql']['windows']['bin_dir']}\\my.ini"] + not_if { ::Win32::Service.exists?(node['mysql']['server']['service_name']) } +end + +service 'mysql' do + service_name node['mysql']['server']['service_name'] + action [:enable, :start] +end + +execute 'assign-root-password' do + command %Q["#{node['mysql']['windows']['mysqladmin_bin']}" -u root password #{node['mysql']['server_root_password']}] + action :run + # only_if %Q["#{node['mysql']['windows']['mysql_bin']}" -u root -e 'show databases;'] # unreliable due to CHEF-4783; always returns 0 when run in Chef + not_if { node['mysql']['root_password_set'] } + notifies :run, 'ruby_block[root-password-set]', :immediately +end + +ruby_block 'root-password-set' do + block do + node.set['mysql']['root_password_set'] = true + end + action :nothing +end + +grants_path = node['mysql']['windows']['grants_path'] + +template grants_path do + source 'grants.sql.erb' + action :create + notifies :run, 'execute[mysql-install-privileges]', :immediately +end + +execute 'mysql-install-privileges' do + command "\"#{node['mysql']['windows']['mysql_bin']}\" -u root #{node['mysql']['server_root_password'].empty? ? '' : '-p' }\"#{node['mysql']['server_root_password']}\" < \"#{grants_path}\"" + action :nothing +end diff --git a/chef/cookbooks/mysql/recipes/client.rb b/chef/cookbooks/mysql/recipes/client.rb index 91787e7..7223036 100644 --- a/chef/cookbooks/mysql/recipes/client.rb +++ b/chef/cookbooks/mysql/recipes/client.rb @@ -2,7 +2,7 @@ # Cookbook Name:: mysql # Recipe:: client # -# Copyright 2008-2011, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ ::Chef::Recipe.send(:include, Opscode::Mysql::Helpers) case node['platform'] -when "windows" +when 'windows' package_file = node['mysql']['client']['package_file'] remote_file "#{Chef::Config[:file_cache_path]}/#{package_file}" do source node['mysql']['client']['url'] @@ -32,24 +32,23 @@ when "windows" windows_package node['mysql']['client']['packages'].first do source "#{Chef::Config[:file_cache_path]}/#{package_file}" end + ENV['PATH'] += ";#{node['mysql']['client']['bin_dir']}" windows_path node['mysql']['client']['bin_dir'] do action :add end def package(*args, &blk) windows_package(*args, &blk) end -when "mac_os_x" - include_recipe 'homebrew' +when 'mac_os_x' + include_recipe 'homebrew::default' end -node['mysql']['client']['packages'].each do |mysql_pack| - package mysql_pack do - action :install - end +node['mysql']['client']['packages'].each do |name| + package name end -if platform? 'windows' - ruby_block "copy libmysql.dll into ruby path" do +if platform_family?('windows') + ruby_block 'copy libmysql.dll into ruby path' do block do require 'fileutils' FileUtils.cp "#{node['mysql']['client']['lib_dir']}\\libmysql.dll", node['mysql']['client']['ruby_dir'] diff --git a/chef/cookbooks/mysql/recipes/default.rb b/chef/cookbooks/mysql/recipes/default.rb index 9ff90d6..dfb1f72 100644 --- a/chef/cookbooks/mysql/recipes/default.rb +++ b/chef/cookbooks/mysql/recipes/default.rb @@ -2,7 +2,7 @@ # Cookbook Name:: mysql # Recipe:: default # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,5 +16,3 @@ # See the License for the specific language governing permissions and # limitations under the License. # - -include_recipe "mysql::client" diff --git a/chef/cookbooks/mysql/recipes/percona_repo.rb b/chef/cookbooks/mysql/recipes/percona_repo.rb deleted file mode 100644 index 0051d68..0000000 --- a/chef/cookbooks/mysql/recipes/percona_repo.rb +++ /dev/null @@ -1,48 +0,0 @@ -# -# Cookbook Name:: mysql -# Recipe:: percona_repo -# -# Copyright 2008-2009, Opscode, Inc. -# -# 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. -# - - -case node['platform'] -when "ubuntu", "debian" - include_recipe "apt" - apt_repository "percona" do - uri node['mysql']['percona']['apt_uri'] - distribution node['lsb']['codename'] - components [ "main" ] - keyserver node['mysql']['percona']['apt_keyserver'] - key node['mysql']['percona']['apt_key_id'] - action :add - end -when "centos", "amazon", "redhat" - include_recipe "yum" - yum_key "RPM-GPG-KEY-percona" do - url "http://www.percona.com/downloads/RPM-GPG-KEY-percona" - action :add - end - arch = node['kernel']['machine'] - arch = "i386" unless arch == "x86_64" - pversion = node['platform_version'].split('.').first - yum_repository "percona" do - repo_name "Percona" - description "Percona Repo" - url "http://repo.percona.com/centos/#{pversion}/os/#{arch}/" - key "RPM-GPG-KEY-percona" - action :add - end -end diff --git a/chef/cookbooks/mysql/recipes/ruby.rb b/chef/cookbooks/mysql/recipes/ruby.rb index 8c8470d..9f0d9ad 100644 --- a/chef/cookbooks/mysql/recipes/ruby.rb +++ b/chef/cookbooks/mysql/recipes/ruby.rb @@ -5,7 +5,7 @@ # Author:: Jesse Howarth () # Author:: Jamie Winsor () # -# Copyright 2008-2012, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,11 +21,35 @@ # node.set['build_essential']['compiletime'] = true -include_recipe "build-essential" -include_recipe "mysql::client" +include_recipe 'build-essential::default' +include_recipe 'mysql::client' -node['mysql']['client']['packages'].each do |mysql_pack| - resources("package[#{mysql_pack}]").run_action(:install) +loaded_recipes = if run_context.respond_to?(:loaded_recipes) + run_context.loaded_recipes + else + node.run_state[:seen_recipes] + end + +if loaded_recipes.include?('mysql::percona_repo') + case node['platform_family'] + when 'debian' + resources('apt_repository[percona]').run_action(:add) + when 'rhel' + resources('yum_key[RPM-GPG-KEY-percona]').run_action(:add) + resources('yum_repository[percona]').run_action(:add) + end end -chef_gem "mysql" +node['mysql']['client']['packages'].each do |name| + resources("package[#{name}]").run_action(:install) +end + +if node['local_repo'].nil? or node['local_repo'].empty? + chef_gem 'mysql' +else + gem_package 'mysql' do + options("--clear-sources --source #{node['local_repo']}/gem_repo/") + action :install + version '2.9.1' + end +end diff --git a/chef/cookbooks/mysql/recipes/server.rb b/chef/cookbooks/mysql/recipes/server.rb index 8a285e8..8a57638 100644 --- a/chef/cookbooks/mysql/recipes/server.rb +++ b/chef/cookbooks/mysql/recipes/server.rb @@ -2,7 +2,7 @@ # Cookbook Name:: mysql # Recipe:: default # -# Copyright 2008-2011, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,211 +18,34 @@ # ::Chef::Recipe.send(:include, Opscode::OpenSSL::Password) - -include_recipe "mysql::client" +::Chef::Recipe.send(:include, Opscode::Mysql::Helpers) if Chef::Config[:solo] - missing_attrs = %w{ - server_debian_password server_root_password server_repl_password - }.select do |attr| - node["mysql"][attr].nil? - end.map { |attr| "node['mysql']['#{attr}']" } + missing_attrs = %w[ + server_debian_password + server_root_password + server_repl_password + ].select { |attr| node['mysql'][attr].nil? }.map { |attr| %Q{node['mysql']['#{attr}']} } - if !missing_attrs.empty? - Chef::Application.fatal!([ - "You must set #{missing_attrs.join(', ')} in chef-solo mode.", - "For more information, see https://github.com/opscode-cookbooks/mysql#chef-solo-note" - ].join(' ')) + unless missing_attrs.empty? + Chef::Application.fatal! "You must set #{missing_attrs.join(', ')} in chef-solo mode." \ + " For more information, see https://github.com/opscode-cookbooks/mysql#chef-solo-note" end else # generate all passwords - # node.override['mysql']['server_debian_password'] = mydata['credential']['mysql']['super']['password'] - # node.override['mysql']['server_root_password'] = mydata['credential']['mysql']['super']['password'] - # node.override['mysql']['server_repl_password'] = mydata['credential']['mysql']['super']['password'] - # node.set_unless['mysql']['server_debian_password'] = secure_password - # node.set_unless['mysql']['server_root_password'] = item['credential']['service_credential']['password'] - # node.set_unless['mysql']['server_repl_password'] = item['credential']['service_credential']['password'] - # node.save - # puts "---------------else generate all passwords-----------------------------" - # puts "['mysql']['server_debian_password']= #{node.['mysql']['server_debian_password']}" - # puts "['mysql']['server_root_password']=#{node.['mysql']['server_root_password']}" - # puts "-----------------------------------------------------------------------" + node.set_unless['mysql']['server_debian_password'] = secure_password + node.set_unless['mysql']['server_root_password'] = secure_password + node.set_unless['mysql']['server_repl_password'] = secure_password + node.save end -if platform_family?(%w{debian}) - - directory "/var/cache/local/preseeding" do - owner "root" - group node['mysql']['root_group'] - mode 0755 - recursive true - end - - execute "preseed mysql-server" do - command "debconf-set-selections /var/cache/local/preseeding/mysql-server.seed" - action :nothing - end - - template "/var/cache/local/preseeding/mysql-server.seed" do - source "mysql-server.seed.erb" - owner "root" - group node['mysql']['root_group'] - mode "0600" - notifies :run, "execute[preseed mysql-server]", :immediately - end - - template "#{node['mysql']['conf_dir']}/debian.cnf" do - source "debian.cnf.erb" - owner "root" - group node['mysql']['root_group'] - mode "0600" - end - -end - -if platform_family?('windows') - package_file = node['mysql']['package_file'] - - remote_file "#{Chef::Config[:file_cache_path]}/#{package_file}" do - source node['mysql']['url'] - not_if { File.exists? "#{Chef::Config[:file_cache_path]}/#{package_file}" } - end - - windows_package node['mysql']['server']['packages'].first do - source "#{Chef::Config[:file_cache_path]}/#{package_file}" - end - - def package(*args, &blk) - windows_package(*args, &blk) - end -end - -node['mysql']['server']['packages'].each do |package_name| - package package_name do - action :install - notifies :start, "service[mysql]", :immediately - end -end - -unless platform_family?(%w{mac_os_x}) - - [File.dirname(node['mysql']['pid_file']), - File.dirname(node['mysql']['tunable']['slow_query_log']), - node['mysql']['conf_dir'], - node['mysql']['confd_dir'], - node['mysql']['log_dir'], - node['mysql']['data_dir']].each do |directory_path| - directory directory_path do - owner "mysql" unless platform? 'windows' - group "mysql" unless platform? 'windows' - action :create - recursive true - end - end - - if platform_family? 'windows' - require 'win32/service' - - windows_path node['mysql']['bin_dir'] do - action :add - end - - windows_batch "install mysql service" do - command "\"#{node['mysql']['bin_dir']}\\mysqld.exe\" --install #{node['mysql']['service_name']}" - not_if { Win32::Service.exists?(node['mysql']['service_name']) } - end - end - - skip_federated = case node['platform'] - when 'fedora', 'ubuntu', 'amazon' - true - when 'centos', 'redhat', 'scientific' - node['platform_version'].to_f < 6.0 - else - false - end -end - -# Homebrew has its own way to do databases -if platform_family?(%w{mac_os_x}) - execute "mysql-install-db" do - command "mysql_install_db --verbose --user=`whoami` --basedir=\"$(brew --prefix mysql)\" --datadir=#{node['mysql']['data_dir']} --tmpdir=/tmp" - environment('TMPDIR' => nil) - action :run - creates "#{node['mysql']['data_dir']}/mysql" - end -else - execute 'mysql-install-db' do - command "mysql_install_db" - action :run - not_if { File.exists?(node['mysql']['data_dir'] + '/mysql/user.frm') } - end - - service "mysql" do - service_name node['mysql']['service_name'] - if node['mysql']['use_upstart'] - provider Chef::Provider::Service::Upstart - end - supports :status => true, :restart => true, :reload => true - action :enable - end -end - -# set the root password for situations that don't support pre-seeding. -# (eg. platforms other than debian/ubuntu & drop-in mysql replacements) -execute "assign-root-password" do - command %Q["#{node['mysql']['mysqladmin_bin']}" -u root password '#{node['mysql']['server_root_password']}'] - action :run - only_if %Q["#{node['mysql']['mysql_bin']}" -u root -e 'show databases;'] -end - -unless platform_family?(%w{mac_os_x}) - grants_path = node['mysql']['grants_path'] - - begin - t = resources("template[#{grants_path}]") - rescue - Chef::Log.info("Could not find previously defined grants.sql resource") - t = template grants_path do - source "grants.sql.erb" - owner "root" unless platform_family? 'windows' - group node['mysql']['root_group'] unless platform_family? 'windows' - mode "0600" - action :create - end - end - - if platform_family? 'windows' - windows_batch "mysql-install-privileges" do - command "\"#{node['mysql']['mysql_bin']}\" -u root #{node['mysql']['server_root_password'].empty? ? '' : '-p' }\"#{node['mysql']['server_root_password']}\" < \"#{grants_path}\"" - action :nothing - subscribes :run, resources("template[#{grants_path}]"), :immediately - end - else - execute "mysql-install-privileges" do - command %Q["#{node['mysql']['mysql_bin']}" -u root #{node['mysql']['server_root_password'].empty? ? '' : '-p' }'#{node['mysql']['server_root_password']}' < "#{grants_path}"] - action :nothing - subscribes :run, resources("template[#{grants_path}]"), :immediately - end - end - - template "#{node['mysql']['conf_dir']}/my.cnf" do - source "my.cnf.erb" - owner "root" unless platform? 'windows' - group node['mysql']['root_group'] unless platform? 'windows' - mode "0644" - case node['mysql']['reload_action'] - when 'restart' - notifies :restart, "service[mysql]", :immediately - when 'reload' - notifies :reload, "service[mysql]", :immediately - else - Chef::Log.info "my.cnf updated but mysql.reload_action is #{node['mysql']['reload_action']}. No action taken." - end - variables :skip_federated => skip_federated - end - - service "mysql" do - action :start - end +case node['platform_family'] +when 'rhel' + include_recipe 'mysql::_server_rhel' +when 'debian' + include_recipe 'mysql::_server_debian' +when 'mac_os_x' + include_recipe 'mysql::_server_mac_os_x' +when 'windows' + include_recipe 'mysql::_server_windows' end diff --git a/chef/cookbooks/mysql/recipes/server_ec2.rb b/chef/cookbooks/mysql/recipes/server_ec2.rb index 6033ef4..726a751 100644 --- a/chef/cookbooks/mysql/recipes/server_ec2.rb +++ b/chef/cookbooks/mysql/recipes/server_ec2.rb @@ -2,7 +2,7 @@ # Cookbook Name:: mysql # Recipe:: default # -# Copyright 2008-2009, Opscode, Inc. +# Copyright 2008-2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,35 +17,31 @@ # limitations under the License. # - -if (node.attribute?('ec2') && ! FileTest.directory?(node['mysql']['ec2_path'])) - - service "mysql" do +if node.attribute?('ec2') && !FileTest.directory?(node['mysql']['ec2_path']) + service 'mysql' do action :stop end - execute "install-mysql" do + execute 'install-mysql' do command "mv #{node['mysql']['data_dir']} #{node['mysql']['ec2_path']}" - not_if do FileTest.directory?(node['mysql']['ec2_path']) end + not_if { FileTest.directory?(node['mysql']['ec2_path']) } end [node['mysql']['ec2_path'], node['mysql']['data_dir']].each do |dir| directory dir do - owner "mysql" - group "mysql" + owner 'mysql' + group 'mysql' end end mount node['mysql']['data_dir'] do - device node['mysql']['ec2_path'] - fstype "none" - options "bind,rw" - action [:mount, :enable] + device node['mysql']['ec2_path'] + fstype 'none' + options 'bind,rw' + action [:mount, :enable] end - service "mysql" do + service 'mysql' do action :start end - end - diff --git a/chef/cookbooks/mysql/templates/default/debian.cnf.erb b/chef/cookbooks/mysql/templates/default/debian.cnf.erb index 989b125..0708c6b 100644 --- a/chef/cookbooks/mysql/templates/default/debian.cnf.erb +++ b/chef/cookbooks/mysql/templates/default/debian.cnf.erb @@ -2,11 +2,11 @@ host = localhost user = debian-sys-maint password = <%= node['mysql']['server_debian_password'] %> -socket = <%= node['mysql']['socket'] %> +socket = <%= node['mysql']['server']['socket'] %> [mysql_upgrade] host = localhost user = debian-sys-maint password = <%= node['mysql']['server_debian_password'] %> -socket = <%= node['mysql']['socket'] %> +socket = <%= node['mysql']['server']['socket'] %> basedir = /usr diff --git a/chef/cookbooks/mysql/templates/default/grants.sql.erb b/chef/cookbooks/mysql/templates/default/grants.sql.erb index c07d9eb..5101b7d 100644 --- a/chef/cookbooks/mysql/templates/default/grants.sql.erb +++ b/chef/cookbooks/mysql/templates/default/grants.sql.erb @@ -17,23 +17,23 @@ GRANT ALL ON *.* TO 'root'@'%' IDENTIFIED BY '<%= node['mysql']['server_root_pas # remove remote access for root user and set password for local root user DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1'); -<% end %> UPDATE mysql.user SET Password=PASSWORD('<%= node['mysql']['server_root_password'] %>') WHERE User='root'; - +<% end %> <% if node['mysql']['remove_anonymous_users'] -%> # Remove anonymous users DELETE FROM mysql.user WHERE User=''; <% end %> -<% if node['mysql']['remove_test_database'] -%> # Remove test database and access to it +<% if node['mysql']['remove_test_database'] -%> DROP DATABASE IF EXISTS test; DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%'; <% end %> # Set the password for root@localhost SET PASSWORD FOR 'root'@'localhost' = PASSWORD('<%= node['mysql']['server_root_password'] %>'); +SET PASSWORD FOR 'root'@'127.0.0.1' = PASSWORD('<%= node['mysql']['server_root_password'] %>'); <% if node['mysql']['root_network_acl'] -%> # allow root to connect from a remote network if root_network_acl is not nil diff --git a/chef/cookbooks/mysql/templates/default/init-mysql.conf.erb b/chef/cookbooks/mysql/templates/default/init-mysql.conf.erb new file mode 100644 index 0000000..aac6564 --- /dev/null +++ b/chef/cookbooks/mysql/templates/default/init-mysql.conf.erb @@ -0,0 +1,44 @@ +# MySQL Service + +description "MySQL Server" +author "Mario Limonciello " + +#start on runlevel [2345] +stop on starting rc RUNLEVEL=[016] + +respawn +respawn limit 2 5 + +env HOME=/etc/mysql +umask 007 + +# The default of 5 seconds is too low for mysql which needs to flush buffers +kill timeout 300 + +pre-start script + #Sanity checks + [ -r $HOME/my.cnf ] + [ -d /var/run/mysqld ] || install -m 755 -o mysql -g root -d /var/run/mysqld + /lib/init/apparmor-profile-load usr.sbin.mysqld + LC_ALL=C BLOCKSIZE= df --portability <%= node['mysql']['data_dir'] %>/. | tail -n 1 | awk '{ exit ($4<4096) }' +end script + +exec /usr/sbin/mysqld + +post-start script + for i in `seq 1 30` ; do + /usr/bin/mysqladmin --defaults-file="${HOME}"/debian.cnf ping && { + exec "${HOME}"/debian-start + # should not reach this line + exit 2 + } + statusnow=`status` + if echo $statusnow | grep -q 'stop/' ; then + exit 0 + elif echo $statusnow | grep -q 'respawn/' ; then + exit 1 + fi + sleep 1 + done + exit 1 +end script \ No newline at end of file diff --git a/chef/cookbooks/mysql/templates/default/my.cnf.erb b/chef/cookbooks/mysql/templates/default/my.cnf.erb index 669d060..b3ecbb7 100644 --- a/chef/cookbooks/mysql/templates/default/my.cnf.erb +++ b/chef/cookbooks/mysql/templates/default/my.cnf.erb @@ -22,14 +22,14 @@ # Remember to edit /etc/mysql/debian.cnf when changing the socket location. [client] port = <%= node['mysql']['port'] %> -socket = <%= node['mysql']['socket'] %> +socket = <%= node['mysql']['server']['socket'] %> # Here is entries for some specific programs # The following values assume you have at least 32M ram # This was formally known as [safe_mysqld]. Both versions are currently parsed. [mysqld_safe] -socket = <%= node['mysql']['socket'] %> +socket = <%= node['mysql']['server']['socket'] %> nice = <%= node['mysql']['nice'] %> [mysqld] @@ -44,12 +44,12 @@ nice = <%= node['mysql']['nice'] %> # user = mysql -pid-file = <%= node['mysql']['pid_file'] %> -socket = <%= node['mysql']['socket'] %> +pid-file = <%= node['mysql']['server']['pid_file'] %> +socket = <%= node['mysql']['server']['socket'] %> port = <%= node['mysql']['port'] %> -basedir = <%= node['mysql']['basedir'] %> +basedir = <%= node['mysql']['server']['basedir'] %> datadir = <%= node['mysql']['data_dir'] %> -tmpdir = <%= node['mysql']['tmpdir'].join(':') %> +tmpdir = <%= node['mysql']['server']['tmpdir'].join(':') %> skip-external-locking <%- if node['mysql']['tunable']['skip-name-resolve'] %> skip-name-resolve @@ -104,10 +104,12 @@ wait_timeout = <%= node['mysql']['tunable']['wait_timeout'] %> net_read_timeout = <%= node['mysql']['tunable']['net_read_timeout'] %> net_write_timeout = <%= node['mysql']['tunable']['net_write_timeout'] %> back_log = <%= node['mysql']['tunable']['back_log'] %> -table_cache = <%= node['mysql']['tunable']['table_cache'] %> -<%- if node['mysql']['tunable']['table_open_cache'] %> +<%- if node['mysql']['version'].to_f >= 5.6 %> table_open_cache = <%= node['mysql']['tunable']['table_open_cache'] %> +<%- else %> +table_cache = <%= node['mysql']['tunable']['table_open_cache'] %> <%- end %> + tmp_table_size = <%= node['mysql']['tunable']['tmp_table_size'] %> max_heap_table_size = <%= node['mysql']['tunable']['max_heap_table_size'] %> bulk_insert_buffer_size = <%= node['mysql']['tunable']['bulk_insert_buffer_size'] %> @@ -144,14 +146,15 @@ log_warnings # # Here you can see queries with especially long duration -<%- if node['mysql']['version'].to_f >= 5.5 %> -slow_query_log = <%= node['mysql']['tunable']['slow_query_log'] %> -<% else %> -log_slow_queries = <%= node['mysql']['tunable']['slow_query_log'] %> -<% end %> +<%- if node['mysql']['server']['slow_query_log'] %> +slow_query_log = 1 +slow_query_log_file = <%= node['mysql']['server']['slow_query_log_file'] %> +<%- else %> +log_slow_queries = <%= node['mysql']['server']['log_slow_queries'] %> +<%- end %> long_query_time = <%= node['mysql']['tunable']['long_query_time'] %> -<%- if node['mysql']['tunable']['log_queries_not_using_index'] and node['mysql']['slow_query_log'] %> +<%- if node['mysql']['tunable']['log_queries_not_using_index'] and node['mysql']['tunable']['slow_query_log'] %> log-queries-not-using-indexes <%- end %> # @@ -181,6 +184,26 @@ relay_log = <%= node['mysql']['tunable']['relay_log'] %> relay_log_index = <%= node['mysql']['tunable']['relay_log_index'] %> <%- end %> +<%- if node['mysql']['tunable']['replicate_do_db'] %> +replicate_do_db = <%= node['mysql']['tunable']['replicate_do_db'] %> +<%- end %> +<%- if node['mysql']['tunable']['replicate_do_table'] %> +replicate_do_table = <%= node['mysql']['tunable']['replicate_do_table'] %> +<%- end %> +<%- if node['mysql']['tunable']['replicate_ignore_db'] %> +replicate_ignore_db = <%= node['mysql']['tunable']['replicate_ignore_db'] %> +<%- end %> +<%- if node['mysql']['tunable']['replicate_ignore_table'] %> +replicate_ignore_table = <%= node['mysql']['tunable']['replicate_ignore_table'] %> +<%- end %> +<%- if node['mysql']['tunable']['replicate_wild_do_table'] %> +replicate_wild_do_table = <%= node['mysql']['tunable']['replicate_wild_do_table'] %> +<%- end %> +<%- if node['mysql']['tunable']['replicate_wild_ignore_table'] %> +replicate_wild_ignore_table = <%= node['mysql']['tunable']['replicate_wild_ignore_table'] %> +<%- end %> + + sync_binlog = <%= node['mysql']['tunable']['sync_binlog'] %> <%- if node['mysql']['tunable']['skip_slave_start'] %> skip_slave_start @@ -203,6 +226,9 @@ slave_compressed_protocol = <%= node['mysql']['tunable']['slave_compressed_proto # Read the manual for more InnoDB related options. There are many! # You might want to disable InnoDB to shrink the mysqld process by circa 100MB. #skip-innodb +<%- if node["mysql"]["version"].to_f < 5.5 %> +default-storage-engine = innodb +<%- end %> <%- if node["mysql"]["version"].to_f >= 5.5 %> innodb_write_io_threads = <%= node['mysql']['tunable']['innodb_write_io_threads'] %> @@ -213,7 +239,7 @@ innodb_buffer_pool_instances = <%= node['mysql']['tunable']['innodb_buffer_po ## InnoDB Plugin Independent Settings innodb_data_home_dir = <%= node['mysql']['data_dir'] %> -innodb_log_group_home_dir = <%= node['mysql']['log_dir'] %> +innodb_log_group_home_dir = <%= node['mysql']['server']['directories']['log_dir'] %> <%- if node['mysql']['log_files_in_group'] %> innodb_log_files_in_group = <%= node['mysql']['log_files_in_group'] %> <%- end %> @@ -226,6 +252,9 @@ innodb_file_per_table <%- end %> innodb_table_locks = <%= node['mysql']['tunable']['innodb_table_locks'] %> innodb_lock_wait_timeout = <%= node['mysql']['tunable']['innodb_lock_wait_timeout'] %> +<%- if node['mysql']['tunable']['innodb_rollback_on_timeout'] %> +innodb_rollback_on_timeout +<%- end %> innodb_thread_concurrency = <%= node['mysql']['tunable']['innodb_thread_concurrency'] %> innodb_commit_concurrency = <%= node['mysql']['tunable']['innodb_commit_concurrency'] %> innodb_support_xa = <%= node['mysql']['tunable']['innodb_support_xa'] %> @@ -245,8 +274,14 @@ innodb_log_buffer_size = <%= node['mysql']['tunable']['innodb_log_buffer_size'] <%- if node['mysql']['tunable']['innodb_adaptive_flushing'] %> innodb_adaptive_flushing = <%= node['mysql']['tunable']['innodb_adaptive_flushing'] %> <%- end %> +<%- if node['mysql']['tunable']['innodb_adaptive_flushing_method'] %> +innodb_adaptive_flushing_method = <%= node['mysql']['tunable']['innodb_adaptive_flushing_method'] %> +<%- end %> +<%- if node['mysql']['tunable']['innodb_adaptive_checkpoint'] %> +innodb_adaptive_checkpoint = <%= node['mysql']['tunable']['innodb_adaptive_checkpoint'] %> +<%- end %> -<% if @skip_federated %> +<% if node['mysql']['server']['skip_federated'] %> # # * Federated # @@ -259,8 +294,35 @@ skip-federated # * Security Features # # Read the manual, too, if you want chroot! -# chroot = /var/lib/mysql/ -# + +<% if node['mysql']['security']['chroot'] -%> +chroot = <%= node['mysql']['security']['chroot'] %> +<% end %> + +<% if node['mysql']['security']['safe_user_create'] -%> +safe-user-create = <%= node['mysql']['security']['safe_user_create'] %> +<% end %> + +<% if node['mysql']['security']['secure_auth'] -%> +secure-auth = <%= node['mysql']['security']['secure_auth'] %> +<% end %> + +<% if node['mysql']['security']['skip_symbolic_links'] -%> +skip-symbolic-links = <%= node['mysql']['security']['skip_symbolic_links'] %> +<% end %> + +<% if node['mysql']['security']['secure_file_priv'] -%> +secure-file-priv = <%= node['mysql']['security']['secure_file_priv'] %> +<% end %> + +<% if node['mysql']['security']['local_infile'] -%> +local-infile = <%= node['mysql']['security']['local_infile'] %> +<% end %> + +<% if node['mysql']['security']['skip_show_database'] -%> +skip-show-database +<% end %> + # For generating SSL certificates I recommend the OpenSSL GUI "tinyca". # # ssl-ca=/etc/mysql/cacert.pem @@ -306,10 +368,11 @@ skip-bdb old_passwords = <%= node['mysql']['old_passwords'] %> <% end -%> -<% if node['mysql']['confd_dir'] -%> +<% if node['mysql']['server']['directories']['confd_dir'] -%> # # * IMPORTANT: Additional settings that can override those from this file! # The files must end with '.cnf', otherwise they'll be ignored. # -!includedir <%= node['mysql']['confd_dir'] %>/ +!includedir <%= node['mysql']['server']['directories']['confd_dir'] %>/ <% end -%> + diff --git a/chef/cookbooks/mysql/templates/default/usr.sbin.mysqld.erb b/chef/cookbooks/mysql/templates/default/usr.sbin.mysqld.erb new file mode 100644 index 0000000..78aba2c --- /dev/null +++ b/chef/cookbooks/mysql/templates/default/usr.sbin.mysqld.erb @@ -0,0 +1,40 @@ +# vim:syntax=apparmor +# Last Modified: Tue Jun 19 17:37:30 2007 +#include + +/usr/sbin/mysqld flags=(complain) { + #include + #include + #include + #include + #include + + capability dac_override, + capability sys_resource, + capability setgid, + capability setuid, + + network tcp, + + /etc/hosts.allow r, + /etc/hosts.deny r, + + /etc/mysql/*.pem r, + /etc/mysql/conf.d/ r, + /etc/mysql/conf.d/* r, + /etc/mysql/my.cnf r, + /usr/lib/mysql/plugin/ r, + /usr/lib/mysql/plugin/*.so* mr, + /usr/sbin/mysqld mr, + /usr/share/mysql/** r, + /var/log/mysql.log rw, + /var/log/mysql.err rw, + /var/lib/mysql/ r, + <%= node['mysql']['data_dir'] %>/** rwk, + /var/log/mysql/ r, + /var/log/mysql/* rw, + /var/run/mysqld/mysqld.pid w, + /var/run/mysqld/mysqld.sock w, + + /sys/devices/system/cpu/ r, +} \ No newline at end of file diff --git a/chef/cookbooks/mysql/templates/ubuntu-10/init-mysql.conf.erb b/chef/cookbooks/mysql/templates/ubuntu-10/init-mysql.conf.erb new file mode 100644 index 0000000..ceee5a1 --- /dev/null +++ b/chef/cookbooks/mysql/templates/ubuntu-10/init-mysql.conf.erb @@ -0,0 +1,42 @@ +# MySQL Service + +description "MySQL Server" +author "Mario Limonciello " + +start on (net-device-up + and local-filesystems + and runlevel [2345]) +stop on runlevel [016] + +respawn + +env HOME=/etc/mysql +umask 007 + +# The default of 5 seconds is too low for mysql which needs to flush buffers +kill timeout 300 + +pre-start script + #Sanity checks + [ -r $HOME/my.cnf ] + [ -d /var/run/mysqld ] || install -m 755 -o mysql -g root -d /var/run/mysqld + # Load AppArmor profile + if aa-status --enabled 2>/dev/null; then + apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld || true + fi + LC_ALL=C BLOCKSIZE= df --portability <%= node['mysql']['data_dir'] %>/. | tail -n 1 | awk '{ exit ($4<4096) }' +end script + +exec /usr/sbin/mysqld + +post-start script + for i in `seq 1 30` ; do + /usr/bin/mysqladmin --defaults-file="${HOME}"/debian.cnf ping && { + exec "${HOME}"/debian-start + # should not reach this line + exit 2 + } + sleep 1 + done + exit 1 +end script \ No newline at end of file diff --git a/chef/cookbooks/mysql/templates/windows/my.cnf.erb b/chef/cookbooks/mysql/templates/windows/my.cnf.erb deleted file mode 100644 index f0550c1..0000000 --- a/chef/cookbooks/mysql/templates/windows/my.cnf.erb +++ /dev/null @@ -1,61 +0,0 @@ -# -# Generated by Chef for <%= node['hostname'] %> -# -# Local modifications will be overwritten. -# -# The MySQL database server configuration file. -# -# One can use all long options that the program supports. -# Run program with --help to get a list of available options and with -# --print-defaults to see which it would actually understand and use. -# -# For explanations see -# http://dev.mysql.com/doc/mysql/en/server-system-variables.html - -# This will be passed to all mysql clients -# It has been reported that passwords should be enclosed with ticks/quotes -# escpecially if they contain "#" chars... -[client] -port = 3306 - -[mysql] -default-character-set = latin1 - -[mysqld] -# -# * Basic Settings -# -port = 3306 -basedir = <%= node['mysql']['basedir'] %> -datadir = <%= node['mysql']['data_dir'] %> -character-set-server = latin1 -default-storage-engine = INNODB -sql-mode = "STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" - -# -# * Fine Tuning -# -thread_cache_size = <%= node['mysql']['tunable']['thread_cache_size'] %> -max_connections = <%= node['mysql']['tunable']['max_connections'] %> -table_cache = <%= node['mysql']['tunable']['table_cache'] %> -query_cache_size = <%= node['mysql']['tunable']['query_cache_size'] %> -tmp_table_size = 5M -myisam_max_sort_file_size = 100G -myisam_sort_buffer_size = 8M -key_buffer_size = 8M -read_buffer_size = 64K -read_rnd_buffer_size = 256K -sort_buffer_size = 212K - -# -# * InnoDB -# -# Read the manual for more InnoDB related options. There are many! -# You might want to disable InnoDB to shrink the mysqld process by circa 100MB. -# -innodb_additional_mem_pool_size = 2M -innodb_flush_log_at_trx_commit = 1 -innodb_log_buffer_size = 1M -innodb_buffer_pool_size = <%= node['mysql']['tunable']['innodb_buffer_pool_size'] %> -innodb_log_file_size = 10M -innodb_thread_concurrency = 8 diff --git a/chef/cookbooks/mysql/templates/windows/my.ini.erb b/chef/cookbooks/mysql/templates/windows/my.ini.erb new file mode 100644 index 0000000..b148035 --- /dev/null +++ b/chef/cookbooks/mysql/templates/windows/my.ini.erb @@ -0,0 +1,85 @@ +# +# Generated by Chef for <%= node['hostname'] %> +# +# Local modifications will be overwritten. +# +# The MySQL database server configuration file. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# This will be passed to all mysql clients +# It has been reported that passwords should be enclosed with ticks/quotes +# escpecially if they contain "#" chars... +[client] +port = 3306 + +[mysql] +default-character-set = latin1 + +[mysqld] +# +# * Basic Settings +# +port = 3306 +basedir = <%= node['mysql']['windows']['basedir'] %> +datadir = <%= node['mysql']['windows']['data_dir'] %> +character-set-server = latin1 +default-storage-engine = INNODB +sql-mode = "STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" + +<%- if node['mysql']['tunable']['event_scheduler'] %> +event_scheduler = <%= node['mysql']['tunable']['event_scheduler'] %> +<%- end %> + +# +# * Fine Tuning +# +thread_cache_size = <%= node['mysql']['tunable']['thread_cache_size'] %> +max_connections = <%= node['mysql']['tunable']['max_connections'] %> +table_cache = <%= node['mysql']['tunable']['table_cache'] %> +query_cache_size = <%= node['mysql']['tunable']['query_cache_size'] %> +tmp_table_size = 5M +myisam_max_sort_file_size = 100G +myisam_sort_buffer_size = 8M +key_buffer_size = <%= node['mysql']['tunable']['key_buffer_size'] %> +read_buffer_size = <%= node['mysql']['tunable']['read_buffer_size'] %> +read_rnd_buffer_size = <%= node['mysql']['tunable']['read_rnd_buffer_size'] %> +join_buffer_size = <%= node['mysql']['tunable']['join_buffer_size'] %> +sort_buffer_size = <%= node['mysql']['tunable']['sort_buffer_size'] %> +max_allowed_packet = <%= node['mysql']['tunable']['max_allowed_packet'] %> + + +# +# Here you can see queries with especially long duration +<%- if node['mysql']['windows']['version'].to_f >= 5.5 %> +slow_query_log = <%= node['mysql']['server']['slow_query_log'] %> +<% else %> +log_slow_queries = <%= node['mysql']['server']['slow_query_log'] %> +<% end %> + +long_query_time = <%= node['mysql']['tunable']['long_query_time'] %> +<%- if node['mysql']['tunable']['log_queries_not_using_index'] and node['mysql']['slow_query_log'] %> +log-queries-not-using-indexes +<%- end %> + +# +# * InnoDB +# +# Read the manual for more InnoDB related options. There are many! +# You might want to disable InnoDB to shrink the mysqld process by circa 100MB. +# +innodb_log_file_size = <%= node['mysql']['tunable']['innodb_log_file_size'] %> +innodb_additional_mem_pool_size = <%= node['mysql']['tunable']['innodb_additional_mem_pool_size'] %> +innodb_flush_log_at_trx_commit = 1 +innodb_log_buffer_size = <%= node['mysql']['tunable']['innodb_log_buffer_size'] %> +innodb_buffer_pool_size = <%= node['mysql']['tunable']['innodb_buffer_pool_size'] %> +innodb_thread_concurrency = 8 + +<%- if node['mysql']['tunable']['innodb_file_per_table'] %> +innodb_file_per_table +<%- end %> diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/README.md b/chef/cookbooks/mysql/test/cookbooks/mysql_test/README.md deleted file mode 100644 index d710160..0000000 --- a/chef/cookbooks/mysql/test/cookbooks/mysql_test/README.md +++ /dev/null @@ -1,63 +0,0 @@ -Description -=========== - -This cookbook defines acceptance tests for MySQL. It includes: - -* A `features` sub-directory where the Cucumber features for the database - are defined. - -* Creation of a simple test database for the tests to run against. - -Usage -===== - -Set environment variable `TEST_SERVER_HOST` to specify the MySQL server to -connect to. You can optionally set `TEST_CLIENT_HOST` which will test a client -install by running the same features from a remote client. - -Requirements -============ - -## Cookbooks: - -This cookbook depends on the `mysql` cookbook. It also uses the `database` -cookbook to create the test database and relies on the `yum` cookbook in order -to add the EPEL repository on RHEL-derived distributions. - -## Platforms: - -* Ubuntu -* CentOS - -Attributes -========== - -* `node['mysql_test']['database']` - The name of the test database to create. -* `node['mysql_test']['username']` - The username of the datbase user. -* `node['mysql_test']['password']` - The password of the database user. - -Recipes -======= - -* `client` - Simply includes `mysql::client` for a vanilla mysql client install. -* `server` - Includes `mysql::server` to install the server and configures a - test database. - -License and Authors -=================== - -Author:: Andrew Crump - - Copyright:: 2012, Opscode, Inc - - 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. diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/server_test.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/server_test.rb deleted file mode 100644 index 2850708..0000000 --- a/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/server_test.rb +++ /dev/null @@ -1,36 +0,0 @@ -require File.expand_path('../support/helpers.rb', __FILE__) - -describe 'mysql::server' do - - include Helpers::Mysql - - it 'has a secure operating system password' do - assert_secure_password(:debian) - end - it 'has a secure root password' do - assert_secure_password(:root) - end - it 'has a secure replication password' do - assert_secure_password(:repl) - end - it 'installs the mysql packages' do - node['mysql']['server']['packages'].each do |package_name| - package(package_name).must_be_installed - end - end - it 'has a config directory' do - directory(node['mysql']['confd_dir']).must_exist.with(:owner, 'mysql').and(:group, 'mysql') - end - it 'runs as a daemon' do - service(node['mysql']['service_name']).must_be_running - end - it 'creates a my.cnf' do - file("#{node['mysql']['conf_dir']}/my.cnf").must_exist - end - describe 'debian' do - it 'creates a config file for service control' do - skip unless ['debian'].include?(node['platform_family']) - file("#{node['mysql']['conf_dir']}/debian.cnf").must_exist - end - end -end diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/support/helpers.rb deleted file mode 100644 index c8b3fa2..0000000 --- a/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/support/helpers.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Helpers - module Mysql - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - def assert_secure_password(type) - node["mysql"]["server_#{type}_password"].length.must_be_close_to(20, 8) - end - end -end diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/metadata.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/metadata.rb deleted file mode 100644 index 0a66cbd..0000000 --- a/chef/cookbooks/mysql/test/cookbooks/mysql_test/metadata.rb +++ /dev/null @@ -1,9 +0,0 @@ -maintainer "Andrew Crump" -maintainer_email "andrew@kotirisoftware.com" -license "Apache 2.0" -description "Acceptance tests for mysql" -long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "0.1.0" - -depends "database" -depends "yum" diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/server.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/server.rb deleted file mode 100644 index 4b792ee..0000000 --- a/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/server.rb +++ /dev/null @@ -1,62 +0,0 @@ -# -# Cookbook Name:: mysql_test -# Recipe:: server -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -node.set['mysql']['server_debian_password'] = "ilikerandompasswords" -node.set['mysql']['server_root_password'] = "ilikerandompasswords" -node.set['mysql']['server_repl_password'] = "ilikerandompasswords" - -include_recipe "mysql::ruby" -include_recipe "yum::epel" if platform_family?('rhel') - -file "/etc/sysconfig/network" do - content "NETWORKING=yes" - action :create_if_missing - only_if { platform_family?('rhel', 'fedora') } -end - -include_recipe 'mysql::server' - -mysql_connection = {:host => "localhost", :username => 'root', - :password => node['mysql']['server_root_password']} - -mysql_database node['mysql_test']['database'] do - connection mysql_connection - action :create -end - -mysql_database_user node['mysql_test']['username'] do - connection mysql_connection - password node['mysql_test']['password'] - database_name node['mysql_test']['database'] - host 'localhost' - privileges [:select,:update,:insert, :delete] - action [:create, :grant] -end - -mysql_conn_args = "--user=root --password='#{node['mysql']['server_root_password']}'" - -execute 'create-sample-data' do - command %Q{mysql #{mysql_conn_args} #{node['mysql_test']['database']} < + digitalocean_api_key: <%= ENV['DIGITAL_OCEAN_API_KEY'] %> + aws_access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %> + aws_secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %> + aws_ssh_key_id: <%= ENV['AWS_KEYPAIR_NAME'] %> + ssh_key: <%= ENV['AWS_PRIVATE_KEY_PATH'] %> + rackspace_username: <%= ENV['RACKSPACE_USERNAME'] %> + rackspace_api_key: <%= ENV['RACKSPACE_API_KEY'] %> + require_chef_omnibus: latest + +platforms: +- name: centos-5.8 + driver_plugin: digitalocean + driver_config: + image_id: 1601 + flavor_id: 63 + region_id: 1 + ssh_key_ids: <%= ENV['DIGITAL_OCEAN_SSH_KEY_IDS'] %> + +- name: centos-6.4 + driver_plugin: digitalocean + driver_config: + image_id: 562354 + flavor_id: 63 + region_id: 1 + ssh_key_ids: <%= ENV['DIGITAL_OCEAN_SSH_KEY_IDS'] %> + +- name: amazon-2013.09 + driver_plugin: ec2 + driver_config: + image_id: ami-3be4bc52 + username: ec2-user + +- name: ubuntu-1004 + driver_plugin: digitalocean + driver_config: + image_id: 14097 + flavor_id: 63 + region_id: 1 + ssh_key_ids: <%= ENV['DIGITAL_OCEAN_SSH_KEY_IDS'] %> + run_list: + - recipe[apt] + +- name: ubuntu-1204 + driver_plugin: digitalocean + driver_config: + image_id: 1505447 + flavor_id: 63 + region_id: 1 + ssh_key_ids: <%= ENV['DIGITAL_OCEAN_SSH_KEY_IDS'] %> + run_list: + - recipe[apt] + +- name: ubuntu-1310 + driver_plugin: digitalocean + driver_config: + image_id: 1505699 + flavor_id: 63 + region_id: 1 + ssh_key_ids: <%= ENV['DIGITAL_OCEAN_SSH_KEY_IDS'] %> + run_list: + - recipe[apt] + +suites: + - name: default + run_list: + - recipe[openssh::default] diff --git a/chef/cookbooks/openssh/.kitchen.yml b/chef/cookbooks/openssh/.kitchen.yml new file mode 100644 index 0000000..55065be --- /dev/null +++ b/chef/cookbooks/openssh/.kitchen.yml @@ -0,0 +1,14 @@ +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: + - name: ubuntu-12.04 + run_list: + - recipe[apt::default] + - name: centos-6.4 + +suites: + - name: default + run_list: + - recipe[openssh::default] diff --git a/chef/cookbooks/openssh/.rubocop.yml b/chef/cookbooks/openssh/.rubocop.yml new file mode 100644 index 0000000..5f5d138 --- /dev/null +++ b/chef/cookbooks/openssh/.rubocop.yml @@ -0,0 +1,11 @@ +AlignParameters: + Enabled: false + +Encoding: + Enabled: false + +LineLength: + Max: 200 + +HashSyntax: + EnforcedStyle: hash_rockets diff --git a/chef/cookbooks/openssh/.travis.yml b/chef/cookbooks/openssh/.travis.yml new file mode 100644 index 0000000..01655a9 --- /dev/null +++ b/chef/cookbooks/openssh/.travis.yml @@ -0,0 +1,9 @@ +rvm: + - 1.9.3 + - 2.0.0 +before_script: + - bundle exec berks install +script: + - bundle exec foodcritic -f any . + - bundle exec rspec --color --format progress + - bundle exec rubocop diff --git a/chef/cookbooks/xfs/Berksfile b/chef/cookbooks/openssh/Berksfile similarity index 64% rename from chef/cookbooks/xfs/Berksfile rename to chef/cookbooks/openssh/Berksfile index f08b074..3b5ff95 100644 --- a/chef/cookbooks/xfs/Berksfile +++ b/chef/cookbooks/openssh/Berksfile @@ -1,7 +1,6 @@ site :opscode - metadata group :integration do - cookbook "apt" + cookbook 'apt', '~> 2.0' end diff --git a/chef/cookbooks/openssh/CHANGELOG.md b/chef/cookbooks/openssh/CHANGELOG.md index b475a2b..4169f7c 100644 --- a/chef/cookbooks/openssh/CHANGELOG.md +++ b/chef/cookbooks/openssh/CHANGELOG.md @@ -3,6 +3,14 @@ openssh Cookbook CHANGELOG This file is used to list changes made in each version of the openssh cookbook. +v1.3.4 (2014-04-23) +------------------- +- [COOK-4576] - No way to override `AuthorizedKeysFile` +- [COOK-4584] - Use Upstart on Ubuntu 12.04 +- [COOK-4585] - skip match block in template if empty or unset +- [COOK-4586] OpenSSH Gentoo support + + v1.3.2 ------ ### Bug diff --git a/chef/cookbooks/apache2/CONTRIBUTING.md b/chef/cookbooks/openssh/CONTRIBUTING.md similarity index 100% rename from chef/cookbooks/apache2/CONTRIBUTING.md rename to chef/cookbooks/openssh/CONTRIBUTING.md diff --git a/chef/cookbooks/openssh/Gemfile b/chef/cookbooks/openssh/Gemfile new file mode 100644 index 0000000..f465c78 --- /dev/null +++ b/chef/cookbooks/openssh/Gemfile @@ -0,0 +1,13 @@ +source 'https://rubygems.org' + +gem 'berkshelf', '~> 2.0' +gem 'chefspec', '~> 3.0' +gem 'foodcritic', '~> 3.0' +gem 'rubocop' + +group :integration do + gem 'test-kitchen', '~> 1.0' + gem 'kitchen-vagrant', '~> 0.11' + gem 'kitchen-digitalocean' + gem 'kitchen-ec2' +end diff --git a/chef/cookbooks/apt/LICENSE b/chef/cookbooks/openssh/LICENSE similarity index 100% rename from chef/cookbooks/apt/LICENSE rename to chef/cookbooks/openssh/LICENSE diff --git a/chef/cookbooks/openssh/TESTING.md b/chef/cookbooks/openssh/TESTING.md new file mode 100644 index 0000000..b4102e3 --- /dev/null +++ b/chef/cookbooks/openssh/TESTING.md @@ -0,0 +1,53 @@ +This cookbook uses a variety of testing components: + +- Unit tests: [ChefSpec](https://github.com/acrmp/chefspec) +- Integration tests: [Test Kitchen](https://github.com/opscode/test-kitchen) +- Chef Style lints: [Foodcritic](https://github.com/acrmp/foodcritic) +- Ruby Style lints: [Rubocop](https://github.com/bbatsov/rubocop) + + +Prerequisites +------------- +To develop on this cookbook, you must have a sane Ruby 1.9+ environment. Given the nature of this installation process (and it's variance across multiple operating systems), we will leave this installation process to the user. + +You must also have `bundler` installed: + + $ gem install bundler + +You must also have Vagrant and VirtualBox installed: + +- [Vagrant](https://vagrantup.com) +- [VirtualBox](https://virtualbox.org) + +Once installed, you must install the `vagrant-berkshelf` plugin: + + $ vagrant plugin install vagrant-berkshelf + + +Development +----------- +1. Clone the git repository from GitHub: + + $ git clone git@github.com:opscode-cookbooks/COOKBOOK.git + +2. Install the dependencies using bundler: + + $ bundle install + +3. Create a branch for your changes: + + $ git checkout -b my_bug_fix + +4. Make any changes +5. Write tests to support those changes. It is highly recommended you write both unit and integration tests. +6. Run the tests: + - `bundle exec rspec` + - `bundle exec foodcritic .` + - `bundle exec rubocop` + - `bundle exec kitchen test` + +7. Assuming the tests pass, open a Pull Request on GitHub +8. Open a JIRA ticket for this compontent, linking the JIRA ticket to the Pull Request and visa versa. +9. Mark the JIRA ticket as "Fix Provided" + +For more information, see [Opscode's Contribution Guidelines](https://wiki.opscode.com/display/chef/How+to+Contribute). diff --git a/chef/cookbooks/openssh/attributes/default.rb b/chef/cookbooks/openssh/attributes/default.rb index 9fca9b4..d8e7dd2 100644 --- a/chef/cookbooks/openssh/attributes/default.rb +++ b/chef/cookbooks/openssh/attributes/default.rb @@ -24,7 +24,7 @@ default['openssh']['package_name'] = case node['platform_family'] when 'rhel', 'fedora' %w[openssh-clients openssh] - when 'arch', 'suse' + when 'arch', 'suse', 'gentoo' %w[openssh] when 'freebsd' %w[] @@ -33,7 +33,7 @@ default['openssh']['package_name'] = case node['platform_family'] end default['openssh']['service_name'] = case node['platform_family'] - when 'rhel', 'fedora', 'suse', 'freebsd' + when 'rhel', 'fedora', 'suse', 'freebsd', 'gentoo' 'sshd' else 'ssh' @@ -53,13 +53,19 @@ default['openssh']['rootgroup'] = case node['platform_family'] 'root' end +default['openssh']['group'] = node['openssh']['rootgroup'] +default['openssh']['user'] = 'root' +default['openssh']['shared']['private_key'] = nil +default['openssh']['shared']['public_key'] = nil +default['openssh']['shared']['authorized_key'] = nil +default['openssh']['passwordless']['role'] = 'os-compute-worker' # ssh config group default['openssh']['client']['host'] = '*' # default['openssh']['client']['forward_agent'] = 'no' # default['openssh']['client']['forward_x11'] = 'no' # default['openssh']['client']['rhosts_rsa_authentication'] = 'no' # default['openssh']['client']['rsa_authentication'] = 'yes' -# default['openssh']['client']['password_authentication'] = 'yes' +# default['openssh']['client']['password_authentication'] = 'no' # default['openssh']['client']['host_based_authentication'] = 'no' # default['openssh']['client']['gssapi_authentication'] = 'no' # default['openssh']['client']['gssapi_delegate_credentials'] = 'no' @@ -102,7 +108,7 @@ default['openssh']['client']['host'] = '*' # default['openssh']['server']['max_sessions'] = '10' # default['openssh']['server']['r_s_a_authentication'] = 'yes' # default['openssh']['server']['pubkey_authentication'] = 'yes' -default['openssh']['server']['authorized_keys_file'] = '%h/.ssh/authorized_keys' +# default['openssh']['server']['authorized_keys_file'] = '%h/.ssh/authorized_keys' # default['openssh']['server']['rhosts_r_s_a_authentication'] = 'no' # default['openssh']['server']['host_based_authentication'] = 'no' # default['openssh']['server']['ignore_user_known_hosts'] = 'no' diff --git a/chef/cookbooks/openssh/libraries/default.rb b/chef/cookbooks/openssh/libraries/default.rb new file mode 100644 index 0000000..27571ee --- /dev/null +++ b/chef/cookbooks/openssh/libraries/default.rb @@ -0,0 +1,43 @@ +# encoding: UTF-8 +# # +# # Cookbook Name:: openssh +# # libraries::master_election +# # +# # Author: sam.su@huawei.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. +# # +# +def node_election(role, tag, chef_environment = nil) + chef_environment = chef_environment || node.chef_environment + master = search(:node, "run_list:role\\[#{role}\\] AND \ + chef_environment:#{chef_environment} AND \ + tags:#{tag}") || [] + if master.empty? + nodes = search(:node, "run_list:role\\[#{role}\\] AND \ + chef_environment:#{chef_environment}") || [] + nodes = nodes.sort_by { |node| node.name } unless nodes.empty? + if nodes.empty? or node.name.eql?(nodes.first.name) + node.tags << tag unless node.tags.include?(tag) + node.save + end + if nodes.empty? + return node + end + + return nodes.first + else + return master.first + end +end + diff --git a/chef/cookbooks/openssh/metadata.json b/chef/cookbooks/openssh/metadata.json deleted file mode 100644 index b3c8cd3..0000000 --- a/chef/cookbooks/openssh/metadata.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "openssh", - "version": "1.3.2", - "description": "Installs openssh", - "long_description": "", - "maintainer": "Opscode, Inc.", - "maintainer_email": "cookbooks@opscode.com", - "license": "Apache 2.0", - "platforms": { - "arch": ">= 0.0.0", - "centos": ">= 0.0.0", - "debian": ">= 0.0.0", - "fedora": ">= 0.0.0", - "freebsd": ">= 0.0.0", - "redhat": ">= 0.0.0", - "scientific": ">= 0.0.0", - "suse": ">= 0.0.0", - "ubuntu": ">= 0.0.0" - }, - "dependencies": { - "iptables": ">= 0.0.0" - }, - "recommendations": { - }, - "suggestions": { - }, - "conflicting": { - }, - "providing": { - }, - "replacing": { - }, - "attributes": { - }, - "groupings": { - }, - "recipes": { - "openssh": "Installs openssh", - "openssh::iptables": "Set up iptables to allow SSH inbound" - } -} \ No newline at end of file diff --git a/chef/cookbooks/openssh/metadata.rb b/chef/cookbooks/openssh/metadata.rb index 6717041..ec60cd2 100644 --- a/chef/cookbooks/openssh/metadata.rb +++ b/chef/cookbooks/openssh/metadata.rb @@ -3,10 +3,11 @@ maintainer 'Opscode, Inc.' maintainer_email 'cookbooks@opscode.com' license 'Apache 2.0' description 'Installs openssh' -version '1.3.2' +version '1.3.5' recipe 'openssh', 'Installs openssh' recipe 'openssh::iptables', 'Set up iptables to allow SSH inbound' +recipe 'openssh::passwordless', 'Set up SSH login without password' supports 'arch' supports 'centos' diff --git a/chef/cookbooks/openssh/providers/key.rb b/chef/cookbooks/openssh/providers/key.rb new file mode 100644 index 0000000..d79414f --- /dev/null +++ b/chef/cookbooks/openssh/providers/key.rb @@ -0,0 +1,59 @@ +require 'chef/mixin/shell_out' +include Chef::Mixin::ShellOut + +action :create do + ssh_keygen_node = node_election(new_resource.role, 'ssh_keygen') + a = node['openssh']['shared']['private_key'] + if node.name.eql?(ssh_keygen_node.name) and node['openssh']['shared']['private_key'].nil? + unless ::File.exist?(new_resource.private_key) + cmd = "ssh-keygen -t rsa -q -f #{new_resource.private_key} -P ''" + rc = shell_out(cmd) + end + pri_key = ::File.read(new_resource.private_key) + pub_key = ::File.read(new_resource.public_key) + node.set['openssh']['shared']['private_key'] = pri_key + node.set['openssh']['shared']['public_key'] = pub_key + node.set['openssh']['shared']['authorized_key'] = pub_key + node.save + if ::File.exist?(new_resource.authorized_key) + ruby_block new_resource.authorized_key do + block do + auth_file = Chef::Util::FileEdit.new(new_resource.authorized_key) + auth_file.insert_line_if_no_match(pub_key, pub_key) + auth_file.write_file + end + end + else + file "#{new_resource.authorized_key}" do + content node['openssh']['shared']['authorized_key'] + owner new_resource.username + group new_resource.username + mode 00600 + end + end + elsif !node.name.eql?(ssh_keygen_node.name) && node['openssh']['shared']['private_key'].nil? + directory "#{new_resource.home}/.ssh for ssh keys" do + path "#{new_resource.home}/.ssh" + owner new_resource.username + group new_resource.username + mode "0700" + end + if ssh_keygen_node.attribute?('openssh') + %w{private_key public_key authorized_key}.each do |key| + unless ssh_keygen_node['openssh']['shared']["#{key}"].nil? + node.set['openssh']['shared']["#{key}"] = ssh_keygen_node['openssh']['shared']["#{key}"] + node.save + file eval("new_resource.#{key}") do + content node['openssh']['shared']["#{key}"] + owner new_resource.username + group new_resource.username + mode 00600 + end + end + end + end + #else + ## TODO: + end +end + diff --git a/chef/cookbooks/openssh/recipes/default.rb b/chef/cookbooks/openssh/recipes/default.rb index 5d8b5d5..26fa951 100644 --- a/chef/cookbooks/openssh/recipes/default.rb +++ b/chef/cookbooks/openssh/recipes/default.rb @@ -27,7 +27,7 @@ node['openssh']['package_name'].each do |name| end service_provider = Chef::Provider::Service::Upstart if 'ubuntu' == node['platform'] && - Chef::VersionConstraint.new('>= 13.10').include?(node['platform_version']) + Chef::VersionConstraint.new('>= 12.04').include?(node['platform_version']) service 'ssh' do provider service_provider diff --git a/chef/cookbooks/openssh/recipes/passwordless.rb b/chef/cookbooks/openssh/recipes/passwordless.rb new file mode 100644 index 0000000..35b1d32 --- /dev/null +++ b/chef/cookbooks/openssh/recipes/passwordless.rb @@ -0,0 +1,4 @@ +openssh_key "SSH login without password" do + role node['openssh']['passwordless']['role'] + action :create +end diff --git a/chef/cookbooks/openssh/resources/key.rb b/chef/cookbooks/openssh/resources/key.rb new file mode 100644 index 0000000..dbf6779 --- /dev/null +++ b/chef/cookbooks/openssh/resources/key.rb @@ -0,0 +1,14 @@ +actions :create, :allow, :copy + +attribute :role, :kind_of => String, :name_attribute => true +attribute :username, :kind_of => String, :default => `whoami`.delete("\n") +attribute :home, :kind_of => String, :default => `eval echo ~${SUDO_USER}`.delete("\n") +attribute :port, :kind_of => Integer, :default => 22 +attribute :private_key, :kind_of => String, :default => `eval echo ~${SUDO_USER}`.delete("\n") + "/.ssh/id_rsa" +attribute :public_key, :kind_of => String, :default => `eval echo ~${SUDO_USER}`.delete("\n") + "/.ssh/id_rsa.pub" +attribute :authorized_key, :kind_of => String, :default => `eval echo ~${SUDO_USER}`.delete("\n") + "/.ssh/authorized_keys" + +def initialize(*args) + super + @action = :create +end diff --git a/chef/cookbooks/openssh/spec/spec_helper.rb b/chef/cookbooks/openssh/spec/spec_helper.rb new file mode 100644 index 0000000..1dd5126 --- /dev/null +++ b/chef/cookbooks/openssh/spec/spec_helper.rb @@ -0,0 +1,2 @@ +require 'chefspec' +require 'chefspec/berkshelf' diff --git a/chef/cookbooks/openssh/spec/unit/recipes/default_spec.rb b/chef/cookbooks/openssh/spec/unit/recipes/default_spec.rb new file mode 100644 index 0000000..25df614 --- /dev/null +++ b/chef/cookbooks/openssh/spec/unit/recipes/default_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe 'openssh::default' do + let(:chef_run) { ChefSpec::Runner.new.converge(described_recipe) } + + it 'installs the openssh packages' do + expect(chef_run).to install_package('openssh-client') + expect(chef_run).to install_package('openssh-server') + end + + it 'starts the ssh service' do + expect(chef_run).to start_service('ssh') + expect(chef_run).to enable_service('ssh') + end + + it 'writes the ssh_config' do + template = chef_run.template('/etc/ssh/ssh_config') + expect(template).to be + expect(template.mode).to eq('0644') + expect(template.owner).to eq('root') + expect(template.group).to eq('root') + end + + describe 'sshd_config' do + + it 'writes the sshd_config' do + template = chef_run.template('/etc/ssh/sshd_config') + expect(template).to be + expect(template.mode).to eq('0644') + expect(template.owner).to eq('root') + expect(template.group).to eq('root') + end + + it 'allow legacy default AuthorizedKeysFile behavior' do + expect(chef_run).to_not render_file('/etc/ssh/sshd_config').with_content(/AuthorizedKeysFile.*/) + end + + it 'writes a match group block' do + chef_run.node.set['openssh']['server']['match'] = { 'Group admins' => { 'permit_tunnel' => 'yes' } } + chef_run.converge(described_recipe) + expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content(/Match Group admins\n\s\sPermitTunnel yes/) + end + + it 'skips match group block' do + chef_run.node.set['openssh']['server']['match'] = {} + chef_run.converge(described_recipe) + expect(chef_run).to_not render_file('/etc/ssh/sshd_config').with_content(/Match Group admins\n\s\sPermitTunnel yes/) + end + + end +end diff --git a/chef/cookbooks/openssh/templates/default/sshd_config.erb b/chef/cookbooks/openssh/templates/default/sshd_config.erb index 7bd4dc7..4cb7753 100644 --- a/chef/cookbooks/openssh/templates/default/sshd_config.erb +++ b/chef/cookbooks/openssh/templates/default/sshd_config.erb @@ -11,16 +11,17 @@ <% end -%> <% end -%> -<% node['openssh']['server']['match'].sort.map do |match_key, match_items| -%> +<% unless node['openssh']['server']['match'].empty? || !defined?(node['openssh']['server']['match']) -%> +<% node['openssh']['server']['match'].sort.map do |match_key, match_items| -%> Match <%= match_key %> -<% match_items.sort.map do |key, value| -%> -<% if value.kind_of? Array -%> -<% value.each do |item| -%> -<%= " #{key.split("_").map { |w| w.capitalize}.join} #{item}" %> -<% end -%> -<% else -%> -<%= " #{key.split("_").map { |w| w.capitalize}.join} #{value}"%> -<% end -%> -<% end -%> - -<% end -%> +<% match_items.sort.map do |key, value| -%> +<% if value.kind_of? Array -%> +<% value.each do |item| -%> +<%= " #{key.split("_").map { |w| w.capitalize}.join} #{item}" %> +<% end -%> +<% else -%> +<%= " #{key.split("_").map { |w| w.capitalize}.join} #{value}"%> +<% end -%> +<% end -%> +<% end -%> +<% end -%> diff --git a/chef/cookbooks/openssh/test/integration/default/bats/check_service.bats b/chef/cookbooks/openssh/test/integration/default/bats/check_service.bats new file mode 100644 index 0000000..7fccd3f --- /dev/null +++ b/chef/cookbooks/openssh/test/integration/default/bats/check_service.bats @@ -0,0 +1,3 @@ +@test 'check sshd service' { + ps -ef | grep -v grep |grep sshd +} \ No newline at end of file diff --git a/chef/cookbooks/openssl/CONTRIBUTING b/chef/cookbooks/openssl/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/openssl/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/openssl/LICENSE b/chef/cookbooks/openssl/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/openssl/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/openstack-block-storage/.rubocop.yml b/chef/cookbooks/openstack-block-storage/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-block-storage/.tailor b/chef/cookbooks/openstack-block-storage/.tailor deleted file mode 100644 index 99f0dcf..0000000 --- a/chef/cookbooks/openstack-block-storage/.tailor +++ /dev/null @@ -1,25 +0,0 @@ -Tailor.config do |config| - config.formatters "text" - config.file_set '**/*.rb' do |style| - style.max_line_length 80, level: :off - style.allow_camel_case_methods false, level: :error - style.allow_hard_tabs false, level: :error - style.allow_screaming_snake_case_classes false, level: :error - style.allow_trailing_line_spaces false, level: :error - style.allow_invalid_ruby false, level: :warn - style.indentation_spaces 2, level: :error - style.max_code_lines_in_class 300, level: :error - style.max_code_lines_in_method 30, level: :error - style.spaces_after_comma 1, level: :error - style.spaces_after_lbrace 1, level: :error - style.spaces_after_lbracket 0, level: :error - style.spaces_after_lparen 0, level: :error - style.spaces_before_comma 0, level: :error - style.spaces_before_lbrace 1, level: :error - style.spaces_before_rbrace 1, level: :error - style.spaces_before_rbracket 0, level: :error - style.spaces_before_rparen 0, level: :error - style.spaces_in_empty_braces 0, level: :error - style.trailing_newlines 1, level: :error - end -end diff --git a/chef/cookbooks/openstack-block-storage/Berksfile.lock b/chef/cookbooks/openstack-block-storage/Berksfile.lock deleted file mode 100644 index 308084b..0000000 --- a/chef/cookbooks/openstack-block-storage/Berksfile.lock +++ /dev/null @@ -1,68 +0,0 @@ -{ - "sha": "ba71763fac936d414bd4a63f004357f86f6e1bfb", - "sources": { - "openstack-block-storage": { - "locked_version": "7.0.1", - "constraint": "= 7.0.1", - "path": "." - }, - "openstack-image": { - "locked_version": "7.0.0", - "git": "git://github.com/stackforge/cookbook-openstack-image.git", - "ref": "c4af085fd62b542fee13d3a8a4ffdc1885ce37ed" - }, - "openstack-identity": { - "locked_version": "7.0.0", - "git": "git://github.com/stackforge/cookbook-openstack-identity.git", - "ref": "029fe8a648939f832f844562d0e18af2a951c783" - }, - "openstack-common": { - "locked_version": "0.3.0", - "git": "git://github.com/stackforge/cookbook-openstack-common.git", - "ref": "25b183f2362fa501cfee4db331491b3d984a5c05" - }, - "apt": { - "locked_version": "2.0.0" - }, - "rabbitmq": { - "locked_version": "2.1.2" - }, - "erlang": { - "locked_version": "1.3.0", - "constraint": ">= 0.9.0" - }, - "yum": { - "locked_version": "2.3.0", - "constraint": ">= 0.5.0" - }, - "build-essential": { - "locked_version": "1.4.0" - }, - "selinux": { - "locked_version": "0.5.6" - }, - "database": { - "locked_version": "1.4.0" - }, - "mysql": { - "locked_version": "3.0.2", - "constraint": ">= 1.3.0" - }, - "openssl": { - "locked_version": "1.0.2" - }, - "postgresql": { - "locked_version": "3.0.2", - "constraint": ">= 1.0.0" - }, - "aws": { - "locked_version": "0.101.2" - }, - "xfs": { - "locked_version": "1.1.0" - }, - "python": { - "locked_version": "1.3.4" - } - } -} diff --git a/chef/cookbooks/openstack-block-storage/CHANGELOG.md b/chef/cookbooks/openstack-block-storage/CHANGELOG.md index 2762a8b..e199cf0 100644 --- a/chef/cookbooks/openstack-block-storage/CHANGELOG.md +++ b/chef/cookbooks/openstack-block-storage/CHANGELOG.md @@ -1,9 +1,90 @@ openstack-block-storage Cookbook CHANGELOG ============================== This file is used to list changes made in each version of the openstack-block-storage cookbook. +## 9.2.3 +* Fix for storwize_svc_vol_rsize default +## 9.2.2 +### Bug +* Add support for miscellaneous options (like in Compute) -v7.0.1 ------- +## 9.2.1 +### Bug +* Remove output of extra config lines in cinder.conf.erb + +## 9.2.0 +### Blue print +* Get VMware vCenter password from databag + +## 9.1.1 +* Fix package action to allow updates + +## 9.1.0 +### Blue print +* Remove policy template + +## 9.0.1 +### Bug +* Fix the depends cookbook version issue in metadata.rb + +## 9.0.0 +* Upgrade to Icehouse + +## 8.4.1 +### Bug +* Fix the DB2 ODBC driver issue +* Move control_exchange outside of 'rabbit' + +## 8.4.0 +### Blue print +* Use the library method auth_uri_transform + +## 8.3.0 +* Rename openstack-metering to openstack-telemetry + +## 8.2.0 +* VMware VMDK driver support + +## 8.1.0 +* Add client recipe + +## 8.0.0 +### New version +* Upgrade to upstream Havana release +* Add support for Storwize/SVC configuration attributes + +## 7.2.2 +### Bug +* fix a bug related to qpid. + +## 7.2.1 +### Bug +* relax the dependencies to the 7.x series + +## 7.2.0 ### Improvement -- Add audit cronjob and enable control_exchange, when metering enabled +* Add qpid support for cinder. Default is rabbitmq + +## 7.1.0 +### Improvement +* Add new attributes for common rpc configuration + +## 7.0.6 +### Bug +* set auth_uri for authtoken in api-paste.ini (bug #1207504) + +## 7.0.4 +### Improvement +* Use a default log-file (/var/log/cinder/cinder.log) if syslog is disabled + +## 7.0.3 +### Bug +* change audit cronjob binary path depending on platform, refactored some tests + +## 7.0.2 +### Improvement +* ensure cronjob runs on only one node and make cronjob configurable + +## 7.0.1 +### Improvement +* Add audit cronjob and enable control_exchange, when metering enabled diff --git a/chef/cookbooks/openstack-block-storage/Gemfile b/chef/cookbooks/openstack-block-storage/Gemfile index 8702618..b5ff3e9 100644 --- a/chef/cookbooks/openstack-block-storage/Gemfile +++ b/chef/cookbooks/openstack-block-storage/Gemfile @@ -1,9 +1,11 @@ -source "https://rubygems.org" +# encoding: UTF-8 +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 2.0.3" -gem "chefspec", "~> 2.0.0" -gem "foodcritic" -gem "strainer" -gem "tailor" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' +gem 'fauxhai', '>= 2.1.0' diff --git a/chef/cookbooks/openstack-block-storage/Gemfile.lock b/chef/cookbooks/openstack-block-storage/Gemfile.lock index 4cc50ab..bbe41ab 100644 --- a/chef/cookbooks/openstack-block-storage/Gemfile.lock +++ b/chef/cookbooks/openstack-block-storage/Gemfile.lock @@ -1,167 +1,193 @@ GEM remote: https://rubygems.org/ specs: - activesupport (3.2.13) - i18n (= 0.6.1) + activesupport (3.2.17) + i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.3.4) - akami (1.2.0) + addressable (2.3.6) + akami (1.2.1) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - berkshelf (2.0.3) - activesupport (>= 3.2.0) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) + activesupport (~> 3.2.0) addressable (~> 2.3.4) - celluloid (>= 0.14.0) + buff-shell_out (~> 0.1) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) retryable (~> 1.3.3) - ridley (~> 1.0.2) - solve (>= 0.4.4) - test-kitchen (>= 1.0.0.alpha7) + ridley (~> 1.5.0) + solve (>= 0.5.0) thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) + buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.1) + buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) timers (>= 1.0.0) celluloid-io (0.14.1) celluloid (>= 0.14.1) nio4r (>= 0.4.5) - chef (11.4.4) - erubis - highline (>= 1.6.9) - json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) net-ssh (~> 2.6) - net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (1.3.1) - chef (>= 10.0) - erubis - fauxhai (>= 0.1.1, < 2.0) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.8.4) - builder (>= 2.1.2) - coderay (1.0.9) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.7) - multipart-post (~> 1.1) - fauxhai (1.1.1) - httparty + faraday (0.8.9) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) net-ssh ohai - ffi (1.9.0) - foodcritic (2.1.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) - rak (~> 1.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.0.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.1) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - method_source (0.8.1) - mime-types (1.23) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.4) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.1.0) - multi_json (1.7.6) - multi_xml (0.5.4) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) multipart-post (1.2.0) - net-http-persistent (2.8) - net-scp (1.1.1) - net-ssh (>= 2.6.5) - net-ssh (2.6.7) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - net-ssh-multi (1.1) - net-ssh (>= 2.1.4) - net-ssh-gateway (>= 0.99.0) - nio4r (0.4.6) - nokogiri (1.5.10) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.16.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) - pry (0.9.12.2) - coderay (~> 1.0.5) + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) method_source (~> 0.8) slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) - rak (1.4) + rainbow (2.0.0) + rake (10.2.2) + rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (1.0.2) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) + buff-extensions (~> 0.3) + buff-ignore (~> 1.1) + buff-shell_out (~> 0.1) celluloid (~> 0.14.0) celluloid-io (~> 0.14.0) - chozo (>= 0.6.0) erubis faraday (>= 0.8.4) hashie (>= 2.0.2) + json (>= 1.7.7) mixlib-authentication (>= 1.3.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) + varia_model (~> 0.1) winrm (~> 1.1.0) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.1) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) - safe_yaml (0.9.3) savon (0.9.5) akami (~> 1.0) builder (>= 2.1.2) @@ -170,41 +196,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - slop (3.4.5) - solve (0.4.4) - json - strainer (3.0.1) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - test-kitchen (1.0.0.alpha.7) - celluloid - mixlib-shellout - net-scp - net-ssh - pry - safe_yaml - thor - text-table (1.2.3) thor (0.18.1) - timers (1.1.0) - tins (0.8.0) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) uuidtools (2.1.4) + varia_model (0.3.2) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -214,10 +228,11 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 2.0.3) - chef (~> 11.4.4) - chefspec (~> 1.3.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + fauxhai (>= 2.1.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - tailor diff --git a/chef/cookbooks/openstack-block-storage/README.md b/chef/cookbooks/openstack-block-storage/README.md index af52c07..c145d2b 100644 --- a/chef/cookbooks/openstack-block-storage/README.md +++ b/chef/cookbooks/openstack-block-storage/README.md @@ -29,13 +29,17 @@ api - Installs the cinder-api, sets up the cinder database, and cinder service/user/endpoints in keystone +client +---- +- Install the cinder client packages + scheduler ---- - Installs the cinder-scheduler service volume ---- -- Installs the cinder-volume service and sets up the iscsi helper +- Installs the cinder-volume service, sets up the iscsi helper and create volume group when using the LVMISCSIDriver Defaults to the ISCSI (LVM) Driver. @@ -43,55 +47,150 @@ Attributes ========== * `openstack["block-storage"]["db"]["username"]` - cinder username for database -* `openstack["block-storage"]["rabbit"]["username"]` - Username for cinder rabbit access +TODO: Add DB2 support on other platforms +* `openstack["block-storage"]["platform"]["db2_python_packages"]` - Array of DB2 python packages, only available on redhat platform +* `openstack["block-storage"]["volume_name_template"]` - Template string to be used to generate volume names +* `openstack["block-storage"]["snapshot_name_template"]` - Template string to be used to generate snapshot names +* `openstack['block-storage']['api']['auth']['version']` - Select v2.0 or v3.0. Default v2.0 inherited from common cookbook. The default auth API version used to interact with identity service. + +MQ attributes +------------- +* `openstack["block-storage"]["mq"]["service_type"]` - Select qpid or rabbitmq. default rabbitmq +TODO: move rabbit parameters under openstack["block-storage"]["mq"] +* `openstack["block-storage"]["rabbit"]["username"]` - Username for nova rabbit access * `openstack["block-storage"]["rabbit"]["vhost"]` - The rabbit vhost to use * `openstack["block-storage"]["rabbit"]["port"]` - The rabbit port to use * `openstack["block-storage"]["rabbit"]["host"]` - The rabbit host to use (must set when `openstack["block-storage"]["rabbit"]["ha"]` false). * `openstack["block-storage"]["rabbit"]["ha"]` - Whether or not to use rabbit ha + +* `openstack["block-storage"]["mq"]["qpid"]["host"]` - The qpid host to use +* `openstack["block-storage"]["mq"]["qpid"]["port"]` - The qpid port to use +* `openstack["block-storage"]["mq"]["qpid"]["qpid_hosts"]` - Qpid hosts. TODO. use only when ha is specified. +* `openstack["block-storage"]["mq"]["qpid"]["username"]` - Username for qpid connection +* `openstack["block-storage"]["mq"]["qpid"]["password"]` - Password for qpid connection +* `openstack["block-storage"]["mq"]["qpid"]["sasl_mechanisms"]` - Space separated list of SASL mechanisms to use for auth +* `openstack["block-storage"]["mq"]["qpid"]["reconnect_timeout"]` - The number of seconds to wait before deciding that a reconnect attempt has failed. +* `openstack["block-storage"]["mq"]["qpid"]["reconnect_limit"]` - The limit for the number of times to reconnect before considering the connection to be failed. +* `openstack["block-storage"]["mq"]["qpid"]["reconnect_interval_min"]` - Minimum number of seconds between connection attempts. +* `openstack["block-storage"]["mq"]["qpid"]["reconnect_interval_max"]` - Maximum number of seconds between connection attempts. +* `openstack["block-storage"]["mq"]["qpid"]["reconnect_interval"]` - Equivalent to setting qpid_reconnect_interval_min and qpid_reconnect_interval_max to the same value. +* `openstack["block-storage"]["mq"]["qpid"]["heartbeat"]` - Seconds between heartbeat messages sent to ensure that the connection is still alive. +* `openstack["block-storage"]["mq"]["qpid"]["protocol"]` - Protocol to use. Default tcp. +* `openstack["block-storage"]["mq"]["qpid"]["tcp_nodelay"]` - Disable the Nagle algorithm. default disabled. + +Cinder attributes +----------------- + * `openstack["block-storage"]["service_tenant_name"]` - name of tenant to use for the cinder service account in keystone * `openstack["block-storage"]["service_user"]` - cinder service user in keystone * `openstack["block-storage"]["service_role"]` - role for the cinder service user in keystone +* `openstack["block-storage"]["notification_driver"]` - Set the notification driver to be used (default to cinder.openstack.common.notifier.rpc_notifier) * `openstack["block-storage"]["syslog"]["use"]` * `openstack["block-storage"]["syslog"]["facility"]` * `openstack["block-storage"]["syslog"]["config_facility"]` * `openstack["block-storage"]["platform"]` - hash of platform specific package/service names and options * `openstack["block-storage"]["volume"]["state_path"]` - Top-level directory for maintaining cinder's state * `openstack["block-storage"]["volume"]["driver"]` - Driver to use for volume creation +* `openstack["block-storage"]["volume"]["volume_clear"]` - Defines the method for clearing volumes on a volume delete possible options: 'zero', 'none', 'shred' (https://review.openstack.org/#/c/12521/) +* `openstack["block-storage"]["volume"]["volume_clear_size"]` - size in MB used to limit the cleared area on deleting a volume, to the first part of the volume only. (default 0 = all MB) * `openstack["block-storage"]["volume"]["volume_group"]` - Name for the VG that will contain exported volumes +* `openstack["block-storage"]["voluem"]["volume_group_size"]` - The size (GB) of volume group (default is 40) +* `openstack["block-storage"]["voluem"]["create_volume_group"]` - Create volume group or not when using the LVMISCSIDriver (default is false) * `openstack["block-storage"]["volume"]["iscsi_helper"]` - ISCSI target user-land tool to use +* `openstack["block-storage"]["volume"]["iscsi_ip_address"]` - The IP address where the iSCSI daemon is listening on +* `openstack["block-storage"]["volume"]["iscsi_port"]` - The port where the iSCSI daemon is listening on * `openstack["block-storage"]["rbd_pool"]` - RADOS Block Device pool to use * `openstack["block-storage"]["rbd_user"]` - User for Cephx Authentication * `openstack["block-storage"]["rbd_secret_uuid"]` - Secret UUID for Cephx Authentication -* `openstack["block-storage"]["policy"]["context_is_admin"]` - Define administrators -* `openstack["block-storage"]["policy"]["default"]` - default volume operations rule -* `openstack["block-storage"]["policy"]["admin_or_owner"]` - Define an admin or owner -* `openstack["block-storage"]["policy"]["admin_api"]` - Define api admin -* `openstack["block-storage"]["netapp"]["protocol"]` - how are we talking to either dfm or filer, http or https +* `openstack["block-storage"]["netapp"]["protocol"]` - How are we talking to either dfm or filer, http or https * `openstack["block-storage"]["netapp"]["dfm_hostname"]` - Host or IP of your dfm server * `openstack["block-storage"]["netapp"]["dfm_login"]` - Username for dfm -* `openstack["block-storage"]["netapp"]["dfm_password"]` - password for the dfm user -* `openstack["block-storage"]["netapp"]["dfm_port"]` - default port for dfm -* `openstack["block-storage"]["netapp"]["dfm_web_port"]` - web gui port for wsdl file download -* `openstack["block-storage"]["netapp"]["storage_service"]` - name of the service in dfpm -* `openstack["block-storage"]["netapp"]["netapp_server_port"]` - web admin port of the filer itself -* `openstack["block-storage"]["netapp"]["netapp_server_hostname"]` - hostname of your filer, needs to be resolvable +* `openstack["block-storage"]["netapp"]["dfm_password"]` - Password for the dfm user +* `openstack["block-storage"]["netapp"]["dfm_port"]` - Default port for dfm +* `openstack["block-storage"]["netapp"]["dfm_web_port"]` - Web gui port for wsdl file download +* `openstack["block-storage"]["netapp"]["storage_service"]` - Name of the service in dfpm +* `openstack["block-storage"]["netapp"]["netapp_server_port"]` - Web admin port of the filer itself +* `openstack["block-storage"]["netapp"]["netapp_server_hostname"]` - Hostname of your filer, needs to be resolvable * `openstack["block-storage"]["netapp"]["netapp_server_login"]` - Username for netapp filer -* `openstack["block-storage"]["netapp"]["netapp_server_password"]` - password for user above -* `openstack["block-storage"]["nfs"]["shares_config"]` - file containing line by line entries of server:export -* `openstack["block-storage"]["nfs"]["mount_point_base"]` - directory to mount NFS exported shares +* `openstack["block-storage"]["netapp"]["netapp_server_password"]` - Password for user above +* `openstack["block-storage"]["nfs"]["shares_config"]` - File containing line by line entries of server:export +* `openstack["block-storage"]["nfs"]["mount_point_base"]` - Directory to mount NFS exported shares +* `openstack["block-storage"]["control_exchange"]` - The AMQP exchange to connect to if using RabbitMQ or Qpid, defaults to cinder +* `openstack["block-storage"]["rpc_backend"]` - The messaging module to use, defaults to kombu. +* `openstack["block-storage"]["rpc_thread_pool_size"]` - Size of RPC thread pool +* `openstack["block-storage"]["rpc_conn_pool_size"]` - Size of RPC connection pool +* `openstack["block-storage"]["rpc_response_timeout"]` - Seconds to wait for a response from call or multicall +* `openstack["block-storage"]["misc_cinder"] - Array of strings to be added to cinder.conf for misc options, e.g. ['# Comment', 'key=value'] + +### Storwize/SVC attributes ### +* `openstack['block-storage']['san']['san_ip'] - IP address of SAN controller +* `openstack['block-storage']['san']['san_login'] - Username for SAN controller +* `openstack['block-storage']['san']['san_private_key'] - Filename of private key to use for SSH authentication +* `openstack['block-storage']['storwize']['storwize_svc_volpool_name'] - Storage system storage pool for volumes +* `openstack['block-storage']['storwize']['storwize_svc_vol_rsize'] - Storage system space-efficiency parameter for volumes +* `openstack['block-storage']['storwize']['storwize_svc_vol_warning'] - Storage system threshold for volume capacity warnings +* `openstack['block-storage']['storwize']['storwize_svc_vol_autoexpand'] - Storage system autoexpand parameter for volumes +* `openstack['block-storage']['storwize']['storwize_svc_vol_grainsize'] - Storage system grain size parameter for volumes +* `openstack['block-storage']['storwize']['storwize_svc_vol_compression'] - Storage system compression option for volumes +* `openstack['block-storage']['storwize']['storwize_svc_vol_easytier'] - Enable Easy Tier for volumes +* `openstack['block-storage']['storwize']['storwize_svc_vol_iogrp'] - The I/O group in which to allocate volumes +* `openstack['block-storage']['storwize']['storwize_svc_flashcopy_timeout'] - Maximum number of seconds to wait for FlashCopy to be prepared +* `openstack['block-storage']['storwize']['storwize_svc_connection_protocol'] - Connection protocol (iSCSI/FC) +* `openstack['block-storage']['storwize']['storwize_svc_iscsi_chap_enabled'] - Configure CHAP authentication for iSCSI connections +* `openstack['block-storage']['storwize']['storwize_svc_multipath_enabled'] - Connect with multipath (FC only; iSCSI multipath is controlled by Nova) +* `openstack['block-storage']['storwize']['storwize_svc_multihostmap_enabled'] - Allows vdisk to multi host mapping + +### VMware attributes ### +* `openstack['block-storage']['vmware']['secret_name']` - VMware databag secret name +* `openstack['block-storage']['vmware']['vmware_host_ip']` - IP address for connecting to VMware ESX/VC server. (string value) +* `openstack['block-storage']['vmware']['vmware_host_username']` - Username for authenticating with VMware ESX/VC server. (string value) +* `openstack['block-storage']['vmware']['vmware_wsdl_location']` - Optional VIM service WSDL Location e.g http:///vimService.wsdl. Optional over-ride to default location for bug work-arounds. (string value) +* `openstack['block-storage']['vmware']['vmware_api_retry_count']` - Number of times VMware ESX/VC server API must be retried upon connection related issues. (integer value, default 10) +* `openstack['block-storage']['vmware']['vmware_task_poll_interval']` - The interval (in seconds) for polling remote tasks invoked on VMware ESX/VC server. (integer value, default 5) +* `openstack['block-storage']['vmware']['vmware_volume_folder']` - Name for the folder in the VC datacenter that will contain cinder volumes. (string value, default cinder-volumes) +* `openstack['block-storage']['vmware']['vmware_image_transfer_timeout_secs']` - Timeout in seconds for VMDK volume transfer between Cinder and Glance. (integer value, default 7200) +* `openstack['block-storage']['vmware']['vmware_max_objects_retrieval']` - Max number of objects to be retrieved per batch. (integer value, default 100) + +### IBM GPFS attributes ### +* `openstack['block-storage']['gpfs']['gpfs_mount_point_base']` - Path to directory in GPFS filesystem where volume files are located (string value) +* `openstack['block-storage']['gpfs']['gpfs_images_dir']` - Path to directory in GPFS filesystem where Glance images are located (string value) +* `openstack['block-storage']['gpfs']['gpfs_images_share_mode']` - Type of image copy to use, either "copy_on_write" or "copy" (string value) +* `openstack['block-storage']['gpfs']['gpfs_sparse_volumes']` - Create volumes as sparse or fully allocated files (boolean value, default true) +* `openstack['block-storage']['gpfs']['gpfs_max_clone_depth']` - Maximum clone indirections allowed when creating volume file snapshots clones; zero indicates unlimited clone depth (integer, defalut 0) +* `openstack['block-storage']['gpfs']['gpfs_storage_pool']` - GPFS storage pool that volumes are assigned to (string value) + +### IBMNAS (SONAS/Storwize V7000 Unified) attributes ### +* `openstack['block-storage']['ibmnas']['nas_ip']` - Management IP address of IBMNAS storage +* `openstack['block-storage']['ibmnas']['nas_login']` - Username for IBMNAS storage system +* `openstack['block-storage']['ibmnas']['nas_access_ip']` - Hostname/Public IP address to access shares +* `openstack['block-storage']['ibmnas']['export']` - Storage system shares/export path parameter +* `openstack['block-storage']['ibmnas']['shares_config']` - File that contains list of IBMNAS Shares +* `openstack['block-storage']['ibmnas']['mount_point_base']` - Storage system autoexpand parameter for volumes +* `openstack['block-storage']['ibmnas']['nfs_sparsed_volumes']` - Storage system volume creation method + +The following attributes are defined in attributes/default.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack['endpoints']['block-storage-api-bind']['host']` - The IP address to bind the api service to +* `openstack['endpoints']['block-storage-api-bind']['port']` - The port to bind the api service to +* `openstack['endpoints']['block-storage-api-bind']['bind_interface']` - The interface name to bind the api service to + +If the value of the 'bind_interface' attribute is non-nil, then the block-storage service will be bound to the first IP address on that interface. If the value of the 'bind_interface' attribute is nil, then the block-storage service will be bound to the IP address specified in the host attribute. Testing ===== -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. -Tests are defined in Strainerfile. -To run tests: +Berkshelf +===== - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -111,11 +210,17 @@ License and Author | **Author** | Abel Lopez () | | **Author** | Sean Gallagher () | | **Author** | Ionut Artarisi () | +| **Author** | David Geng () | +| **Author** | Salman Baset () | +| **Author** | Chen Zhiwei () | +| **Author** | Mark Vanderwiel () | +| **Author** | Eric Zhou () | | | | | **Copyright** | Copyright (c) 2012, Rackspace US, Inc. | | **Copyright** | Copyright (c) 2012-2013, AT&T Services, Inc. | | **Copyright** | Copyright (c) 2013, Opscode, Inc. | | **Copyright** | Copyright (c) 2013, SUSE Linux GmbH | +| **Copyright** | Copyright (c) 2013-2014, IBM, Corp. | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/openstack-block-storage/Strainerfile b/chef/cookbooks/openstack-block-storage/Strainerfile index 7e292b4..44e3e14 100644 --- a/chef/cookbooks/openstack-block-storage/Strainerfile +++ b/chef/cookbooks/openstack-block-storage/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-block-storage/TESTING.md b/chef/cookbooks/openstack-block-storage/TESTING.md new file mode 100644 index 0000000..af426d1 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/TESTING.md @@ -0,0 +1,29 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run the tests: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-block-storage/attributes/default.rb b/chef/cookbooks/openstack-block-storage/attributes/default.rb index 69ebd9e..3ccc9f2 100644 --- a/chef/cookbooks/openstack-block-storage/attributes/default.rb +++ b/chef/cookbooks/openstack-block-storage/attributes/default.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-block-storage # Attributes:: default @@ -6,6 +7,7 @@ # Copyright 2012, Rackspace US, Inc. # Copyright 2012-2013, AT&T Services, Inc. # Copyright 2013, Opscode, Inc. +# Copyright 2013-2014, IBM, Corp # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,167 +24,276 @@ ######################################################################## # Toggles - These can be overridden at the environment level -default["developer_mode"] = false # we want secure passwords by default +default['developer_mode'] = false # we want secure passwords by default ######################################################################## # Set to some text value if you want templated config files # to contain a custom banner at the top of the written file -default["openstack"]["block-storage"]["custom_template_banner"] = " +default['openstack']['block-storage']['custom_template_banner'] = ' # This file autogenerated by Chef # Do not edit, changes will be overwritten -" +' -default["openstack"]["block-storage"]["verbose"] = "False" -default["openstack"]["block-storage"]["debug"] = "False" +default['openstack']['block-storage']['verbose'] = 'False' +default['openstack']['block-storage']['debug'] = 'False' + +# Specify policy.json remote file to import +default['openstack']['block-storage']['policyfile_url'] = nil # Default lock_path -default["openstack"]["block-storage"]["lock_path"] = "/var/lock/cinder" -# Availability zone/region for the Openstack"]["Block-Storage service -default["openstack"]["block-storage"]["region"] = "RegionOne" +default['openstack']['block-storage']['lock_path'] = '/var/lock/cinder' +# Default notification_driver and control exchange +default['openstack']['block-storage']['notification_driver'] = 'cinder.openstack.common.notifier.rpc_notifier' +default['openstack']['block-storage']['control_exchange'] = 'cinder' +# Availability zone/region for the OpenStack Block-Storage service +default['openstack']['block-storage']['region'] = node['openstack']['region'] +default['openstack']['block-storage']['scheduler_role'] = 'os-block-storage-scheduler' + +# Template strings to be used to generate resource names +default['openstack']['block-storage']['volume_name_template'] = 'volume-%s' +default['openstack']['block-storage']['snapshot_name_template'] = 'snapshot-%s' # The name of the Chef role that knows about the message queue server # that Cinder uses -default["openstack"]["block-storage"]["rabbit_server_chef_role"] = "os-ops-messaging" +default['openstack']['block-storage']['rabbit_server_chef_role'] = 'os-ops-messaging' # This is the name of the Chef role that will install the Keystone Service API -default["openstack"]["block-storage"]["keystone_service_chef_role"] = "keystone" +default['openstack']['block-storage']['keystone_service_chef_role'] = 'keystone' # Keystone PKI signing directory. Only written to the filter:authtoken section -# of the api-paste.ini when node["openstack"]["auth"]["strategy"] == "pki" -default["openstack"]["block-storage"]["api"]["auth"]["cache_dir"] = "/var/cache/cinder/api" +# of the api-paste.ini when node['openstack']['auth']['strategy'] == 'pki' +default['openstack']['block-storage']['api']['auth']['cache_dir'] = '/var/cache/cinder/api' + +default['openstack']['block-storage']['api']['auth']['version'] = node['openstack']['api']['auth']['version'] # Maximum allocatable gigabytes # Should equal total backend storage, default is 10TB -default["openstack"]["block-storage"]["max_gigabytes"] = "10000" +default['openstack']['block-storage']['max_gigabytes'] = '10000' # Storage availability zone # Default is nova -default["openstack"]["block-storage"]["storage_availability_zone"] = "nova" +default['openstack']['block-storage']['storage_availability_zone'] = 'nova' # Quota definitions -default["openstack"]["block-storage"]["quota_volumes"] = "10" -default["openstack"]["block-storage"]["quota_gigabytes"] = "1000" -default["openstack"]["block-storage"]["quota_driver"] = "cinder.quota.DbQuotaDriver" +default['openstack']['block-storage']['quota_volumes'] = '10' +default['openstack']['block-storage']['quota_gigabytes'] = '1000' +default['openstack']['block-storage']['quota_driver'] = 'cinder.quota.DbQuotaDriver' -# This user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# user_password routine. You are expected to create -# the user, pass, vhost in a wrapper rabbitmq cookbook. -default["openstack"]["block-storage"]["rabbit"]["username"] = "guest" -default["openstack"]["block-storage"]["rabbit"]["vhost"] = "/" -default["openstack"]["block-storage"]["rabbit"]["port"] = 5672 -default["openstack"]["block-storage"]["rabbit"]["host"] = "127.0.0.1" -default["openstack"]["block-storage"]["rabbit"]["ha"] = false +# Common rpc definitions +default['openstack']['block-storage']['rpc_thread_pool_size'] = 240 +default['openstack']['block-storage']['rpc_conn_pool_size'] = 100 +default['openstack']['block-storage']['rpc_response_timeout'] = 60 +case node['openstack']['mq']['service_type'] +when 'rabbitmq' + default['openstack']['block_storage']['rpc_backend'] = 'cinder.openstack.common.rpc.impl_kombu' +when 'qpid' + default['openstack']['block_storage']['rpc_backend'] = 'cinder.openstack.common.rpc.impl_qpid' +end -default["openstack"]["block-storage"]["db"]["username"] = "cinder" +default['openstack']['block-storage']['service_tenant_name'] = 'service' +default['openstack']['block-storage']['service_user'] = 'cinder' +default['openstack']['block-storage']['service_role'] = 'admin' -default["openstack"]["block-storage"]["service_tenant_name"] = "service" -default["openstack"]["block-storage"]["service_user"] = "cinder" -default["openstack"]["block-storage"]["service_role"] = "admin" +# SAN Support +default['openstack']['block-storage']['san']['san_ip'] = '127.0.0.1' +default['openstack']['block-storage']['san']['san_login'] = 'admin' +default['openstack']['block-storage']['san']['san_private_key'] = '/v7000_rsa' +default['openstack']['block-storage']['san']['san_password'] = 'san_password' + +# NFS support +default['openstack']['block-storage']['nfs']['nas_ip'] = '127.0.0.1' +default['openstack']['block-storage']['nfs']['nas_login'] = 'admin' +default['openstack']['block-storage']['nfs']['nas_ssh_port'] = '22' # Netapp support -default["openstack"]["block-storage"]["netapp"]["protocol"] = "http" -default["openstack"]["block-storage"]["netapp"]["dfm_hostname"] = nil -default["openstack"]["block-storage"]["netapp"]["dfm_login"] = nil -default["openstack"]["block-storage"]["netapp"]["dfm_password"] = nil -default["openstack"]["block-storage"]["netapp"]["dfm_port"] = "8088" -default["openstack"]["block-storage"]["netapp"]["dfm_web_port"] = "8080" -default["openstack"]["block-storage"]["netapp"]["storage_service"] = "storage_service" +default['openstack']['block-storage']['netapp']['protocol'] = 'http' +default['openstack']['block-storage']['netapp']['dfm_hostname'] = nil +default['openstack']['block-storage']['netapp']['dfm_login'] = nil +default['openstack']['block-storage']['netapp']['dfm_password'] = nil +default['openstack']['block-storage']['netapp']['dfm_port'] = '8088' +default['openstack']['block-storage']['netapp']['dfm_web_port'] = '8080' +default['openstack']['block-storage']['netapp']['storage_service'] = 'storage_service' # Netapp direct NFS -default["openstack"]["block-storage"]["netapp"]["netapp_server_port"] = "80" -default["openstack"]["block-storage"]["netapp"]["netapp_server_hostname"] = nil -default["openstack"]["block-storage"]["netapp"]["netapp_server_password"] = nil -default["openstack"]["block-storage"]["netapp"]["netapp_server_login"] = nil -default["openstack"]["block-storage"]["netapp"]["export"] = nil -default["openstack"]["block-storage"]["nfs"]["shares_config"] = "/etc/cinder/shares.conf" -default["openstack"]["block-storage"]["nfs"]["mount_point_base"] = "/mnt/cinder-volumes" -default["openstack"]["block-storage"]["nfs"]["nfs_disk_util"] = "df" -default["openstack"]["block-storage"]["nfs"]["nfs_sparsed_volumes"] = "true" +default['openstack']['block-storage']['netapp']['netapp_server_port'] = '80' +default['openstack']['block-storage']['netapp']['netapp_server_hostname'] = nil +default['openstack']['block-storage']['netapp']['netapp_server_password'] = nil +default['openstack']['block-storage']['netapp']['netapp_server_login'] = nil +default['openstack']['block-storage']['netapp']['export'] = nil +default['openstack']['block-storage']['nfs']['shares_config'] = '/etc/cinder/shares.conf' +default['openstack']['block-storage']['nfs']['mount_point_base'] = '/mnt/cinder-volumes' +default['openstack']['block-storage']['nfs']['nfs_disk_util'] = 'df' +default['openstack']['block-storage']['nfs']['nfs_sparsed_volumes'] = 'true' + +# Storwize/SVC Support +default['openstack']['block-storage']['storwize']['san_ip'] = node['openstack']['block-storage']['san']['san_ip'] +default['openstack']['block-storage']['storwize']['san_login'] = node['openstack']['block-storage']['san']['san_login'] +default['openstack']['block-storage']['storwize']['san_private_key'] = node['openstack']['block-storage']['san']['san_private_key'] +default['openstack']['block-storage']['storwize']['storwize_svc_volpool_name'] = 'volpool' +default['openstack']['block-storage']['storwize']['storwize_svc_vol_rsize'] = 2 +default['openstack']['block-storage']['storwize']['storwize_svc_vol_warning'] = 0 +default['openstack']['block-storage']['storwize']['storwize_svc_vol_autoexpand'] = true +default['openstack']['block-storage']['storwize']['storwize_svc_vol_grainsize'] = 256 +default['openstack']['block-storage']['storwize']['storwize_svc_vol_compression'] = false +default['openstack']['block-storage']['storwize']['storwize_svc_vol_easytier'] = true +default['openstack']['block-storage']['storwize']['storwize_svc_flashcopy_timeout'] = 120 +default['openstack']['block-storage']['storwize']['storwize_svc_vol_iogrp'] = 0 +default['openstack']['block-storage']['storwize']['storwize_svc_connection_protocol'] = 'iSCSI' +default['openstack']['block-storage']['storwize']['storwize_svc_iscsi_chap_enabled'] = true +default['openstack']['block-storage']['storwize']['storwize_svc_multipath_enabled'] = false +default['openstack']['block-storage']['storwize']['storwize_svc_multihostmap_enabled'] = true + +# SolidFire Support +default['openstack']['block-storage']['solidfire']['san_ip'] = node['openstack']['block-storage']['san']['san_ip'] +default['openstack']['block-storage']['solidfire']['san_login'] = node['openstack']['block-storage']['san']['san_login'] +default['openstack']['block-storage']['solidfire']['san_password'] = node['openstack']['block-storage']['san']['san_password'] +default['openstack']['block-storage']['solidfire']['sf_emulate'] = 'False' +default['openstack']['block-storage']['solidfire']['iscsi_ip_prefix'] = nil + +# EMC VMAX/VNX tSupport +# The EmcUserName user's password is stored in an encrypted databag and +# accessed with openstack-common cookbook library's "get_password" routeine. You +# are expected to create the user and pass in a wrapper cookbook. +default['openstack']['block-storage']['emc']['iscsi_target_prefix'] = 'iqn.1992-04.com.emc' +default['openstack']['block-storage']['emc']['cinder_emc_config_file'] = '/etc/cinder/cinder_emc_config.xml' +default['openstack']['block-storage']['emc']['StorageType'] = 0 +default['openstack']['block-storage']['emc']['EcomServerIP'] = '127.0.0.1' +default['openstack']['block-storage']['emc']['EcomServerPort'] = '5988' +default['openstack']['block-storage']['emc']['EcomUserName'] = 'admin' +default['openstack']['block-storage']['emc']['MaskingView'] = nil + +# VMware Support +default['openstack']['block-storage']['vmware']['secret_name'] = 'openstack_vmware_secret_name' +default['openstack']['block-storage']['vmware']['vmware_host_ip'] = '' +default['openstack']['block-storage']['vmware']['vmware_host_username'] = '' +default['openstack']['block-storage']['vmware']['vmware_wsdl_location'] = nil +default['openstack']['block-storage']['vmware']['vmware_api_retry_count'] = 10 +default['openstack']['block-storage']['vmware']['vmware_task_poll_interval'] = 5 +default['openstack']['block-storage']['vmware']['vmware_volume_folder'] = 'cinder-volumes' +default['openstack']['block-storage']['vmware']['vmware_image_transfer_timeout_secs'] = 7200 +default['openstack']['block-storage']['vmware']['vmware_max_objects_retrieval'] = 100 + +# IBM GPFS Support +default['openstack']['block-storage']['gpfs']['gpfs_mount_point_base'] = node['openstack']['block-storage']['gpfs']['gpfs_mount_point_base'] +default['openstack']['block-storage']['gpfs']['gpfs_images_dir'] = node['openstack']['block-storage']['gpfs']['gpfs_images_dir'] +default['openstack']['block-storage']['gpfs']['gpfs_images_share_mode'] = 'copy_on_write' +default['openstack']['block-storage']['gpfs']['gpfs_sparse_volumes'] = true +default['openstack']['block-storage']['gpfs']['gpfs_max_clone_depth'] = 8 +default['openstack']['block-storage']['gpfs']['gpfs_storage_pool'] = 'system' + +# IBMNAS (SONAS, Storwize V7000 Unified) Support +# The attribute "nas_password" is stored in databag and +# accessed with openstack-common cookbook library's "get_password" routeine. +default['openstack']['block-storage']['ibmnas']['nas_ip'] = node['openstack']['block-storage']['nfs']['nas_ip'] +default['openstack']['block-storage']['ibmnas']['nas_login'] = node['openstack']['block-storage']['nfs']['nas_login'] +default['openstack']['block-storage']['ibmnas']['shares_config'] = '/etc/cinder/nfs_shares.conf' +default['openstack']['block-storage']['ibmnas']['mount_point_base'] = '/mnt/cinder-volumes' +default['openstack']['block-storage']['ibmnas']['nfs_sparsed_volumes'] = 'true' +default['openstack']['block-storage']['ibmnas']['nas_access_ip'] = nil +default['openstack']['block-storage']['ibmnas']['export'] = nil # logging attribute -default["openstack"]["block-storage"]["syslog"]["use"] = false -default["openstack"]["block-storage"]["syslog"]["facility"] = "LOG_LOCAL2" -default["openstack"]["block-storage"]["syslog"]["config_facility"] = "local2" +default['openstack']['block-storage']['syslog']['use'] = false +default['openstack']['block-storage']['syslog']['facility'] = 'LOG_LOCAL2' +default['openstack']['block-storage']['syslog']['config_facility'] = 'local2' -default["openstack"]["block-storage"]["api"]["ratelimit"] = "True" -default["openstack"]["block-storage"]["cron"]["minute"] = '00' - -default["openstack"]["block-storage"]["volume"]["state_path"] = "/var/lib/cinder" -default["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.lvm.LVMISCSIDriver" -default["openstack"]["block-storage"]["volume"]["volume_group"] = "cinder-volumes" -default["openstack"]["block-storage"]["volume"]["iscsi_helper"] = "tgtadm" -#default["openstack"]["volume"]["mode"] = "loopfile" -default["openstack"]["volume"]["disk"] = "/dev/sdb" +default['openstack']['block-storage']['api']['ratelimit'] = 'True' +default['openstack']['block-storage']['cron']['minute'] = '00' +default['openstack']['block-storage']['cron']['audit_logfile'] = '/var/log/cinder/audit.log' +default['openstack']['block-storage']['volume']['state_path'] = '/var/lib/cinder' +default['openstack']['block-storage']['volume']['volumes_dir'] = '/var/lib/cinder/volumes' +default['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.lvm.LVMISCSIDriver' +default['openstack']['block-storage']['volume']['volume_group'] = 'cinder-volumes' +default['openstack']['block-storage']['volume']['volume_group_size'] = 40 +default['openstack']['block-storage']['volume']['volume_clear_size'] = 10 +default['openstack']['block-storage']['volume']['volume_clear'] = 'zero' +# volume disk can be loopfile or /dev/sdb +default['openstack']['block-storage']['volume']['disk'] = '/dev/sdb' +default['openstack']['block-storage']['volume']['create_volume_group'] = false +default['openstack']['block-storage']['volume']['iscsi_helper'] = 'tgtadm' +default['openstack']['block-storage']['volume']['iscsi_ip_address'] = node['ipaddress'] +default['openstack']['block-storage']['volume']['iscsi_port'] = '3260' # Ceph/RADOS options -default["openstack"]["block-storage"]["rbd_pool"] = "rbd" -default["openstack"]["block-storage"]["rbd_user"] = nil -default["openstack"]["block-storage"]["rbd_secret_uuid"] = nil +default['openstack']['block-storage']['rbd_pool'] = 'rbd' +default['openstack']['block-storage']['rbd_user'] = 'cinder' +default['openstack']['block-storage']['rbd_secret_uuid'] = nil +# make this a valid uuid for when node['openstack']['developer_mode'] = true +default['openstack']['block-storage']['rbd_secret_name'] = '00000000-0000-0000-0000-000000000000' +default['openstack']['block-storage']['rbd_key_name'] = 'openstack_image_cephx_key' -# Cinder Policy defaults -default["openstack"]["block-storage"]["policy"]["context_is_admin"] = '["role:admin"]' -default["openstack"]["block-storage"]["policy"]["default"] = '["rule:admin_or_owner"]' -default["openstack"]["block-storage"]["policy"]["admin_or_owner"] = '["is_admin:True"], ["project_id:%(project_id)s"]' -default["openstack"]["block-storage"]["policy"]["admin_api"] = '["is_admin:True"]' +# Misc option support +# Allow additional strings to be added to cinder.conf +# For example: ['# Comment', 'key=value'] +default['openstack']['block-storage']['misc_cinder'] = [] -case platform -when "fedora", "redhat", "centos" # :pragma-foodcritic: ~FC024 - won't fix this +case platform_family +when 'fedora', 'rhel' # :pragma-foodcritic: ~FC024 - won't fix this # operating system user and group names - default["openstack"]["block-storage"]["user"] = "cinder" - default["openstack"]["block-storage"]["group"] = "cinder" + default['openstack']['block-storage']['user'] = 'cinder' + default['openstack']['block-storage']['group'] = 'cinder' - default["openstack"]["block-storage"]["platform"] = { - "mysql_python_packages" => ["MySQL-python"], - "postgresql_python_packages" => ["python-psycopg2"], - "cinder_common_packages" => ["openstack-cinder"], - "cinder_api_packages" => ["python-cinderclient"], - "cinder_api_service" => "openstack-cinder-api", - "cinder_volume_packages" => [], - "cinder_volume_service" => "openstack-cinder-volume", - "cinder_scheduler_packages" => [], - "cinder_scheduler_service" => "openstack-cinder-scheduler", - "cinder_iscsitarget_packages" => ["scsi-target-utils"], - "cinder_iscsitarget_service" => "tgtd", - "cinder_nfs_packages" => ["nfs-utils", "nfs-utils-lib"], - "package_overrides" => "" + default['openstack']['block-storage']['platform'] = { + 'mysql_python_packages' => ['MySQL-python'], + 'db2_python_packages' => ['python-ibm-db', 'python-ibm-db-sa'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'cinder_common_packages' => ['openstack-cinder'], + 'cinder_api_packages' => ['python-cinderclient'], + 'cinder_api_service' => 'openstack-cinder-api', + 'cinder_client_packages' => ['python-cinderclient'], + 'cinder_volume_packages' => [], + 'cinder_volume_service' => 'openstack-cinder-volume', + 'cinder_scheduler_packages' => [], + 'cinder_scheduler_service' => 'openstack-cinder-scheduler', + 'cinder_iscsitarget_packages' => ['scsi-target-utils'], + 'cinder_iscsitarget_service' => 'tgtd', + 'cinder_ceph_packages' => ['python-ceph', 'ceph-common'], + 'cinder_nfs_packages' => ['nfs-utils', 'nfs-utils-lib'], + 'cinder_emc_packages' => ['pywbem'], + 'package_overrides' => '' } -when "suse" +when 'suse' # operating system user and group names - default["openstack"]["block-storage"]["user"] = "openstack-cinder" - default["openstack"]["block-storage"]["group"] = "openstack-cinder" - default["openstack"]["block-storage"]["platform"] = { - "mysql_python_packages" => ["python-mysql"], - "postgresql_python_packages" => ["python-psycopg2"], - "cinder_common_packages" => ["openstack-cinder"], - "cinder_api_packages" => ["openstack-cinder-api"], - "cinder_api_service" => "openstack-cinder-api", - "cinder_scheduler_packages" => ["openstack-cinder-scheduler"], - "cinder_scheduler_service" => "openstack-cinder-scheduler", - "cinder_volume_packages" => ["openstack-cinder-volume"], - "cinder_volume_service" => "openstack-cinder-volume", - "cinder_iscsitarget_packages" => ["tgt"], - "cinder_iscsitarget_service" => "tgtd", - "cinder_nfs_packages" => ["nfs-utils"] + default['openstack']['block-storage']['user'] = 'openstack-cinder' + default['openstack']['block-storage']['group'] = 'openstack-cinder' + default['openstack']['block-storage']['platform'] = { + 'mysql_python_packages' => ['python-mysql'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'cinder_common_packages' => ['openstack-cinder'], + 'cinder_api_packages' => ['openstack-cinder-api'], + 'cinder_api_service' => 'openstack-cinder-api', + 'cinder_client_packages' => ['python-cinderclient'], + 'cinder_scheduler_packages' => ['openstack-cinder-scheduler'], + 'cinder_scheduler_service' => 'openstack-cinder-scheduler', + 'cinder_volume_packages' => ['openstack-cinder-volume'], + 'cinder_volume_service' => 'openstack-cinder-volume', + 'cinder_ceph_packages' => ['python-ceph', 'ceph-common'], + 'cinder_iscsitarget_packages' => ['tgt'], + 'cinder_iscsitarget_service' => 'tgtd', + 'cinder_nfs_packages' => ['nfs-utils'], + 'cinder_emc_packages' => ['python-pywbem'] } -when "ubuntu" +when 'debian' # operating system user and group names - default["openstack"]["block-storage"]["user"] = "cinder" - default["openstack"]["block-storage"]["group"] = "cinder" - default["openstack"]["block-storage"]["platform"] = { - "mysql_python_packages" => ["python-mysqldb"], - "postgresql_python_packages" => ["python-psycopg2"], - "cinder_common_packages" => ["cinder-common"], - "cinder_api_packages" => ["cinder-api", "python-cinderclient"], - "cinder_api_service" => "cinder-api", - "cinder_volume_packages" => ["cinder-volume"], - "cinder_volume_service" => "cinder-volume", - "cinder_scheduler_packages" => ["cinder-scheduler"], - "cinder_scheduler_service" => "cinder-scheduler", - "cinder_iscsitarget_packages" => ["tgt"], - "cinder_iscsitarget_service" => "tgt", - "cinder_nfs_packages" => ["nfs-common"], - "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + default['openstack']['block-storage']['user'] = 'cinder' + default['openstack']['block-storage']['group'] = 'cinder' + default['openstack']['block-storage']['platform'] = { + 'mysql_python_packages' => ['python-mysqldb'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'cinder_common_packages' => ['cinder-common'], + 'cinder_api_packages' => ['cinder-api', 'python-cinderclient'], + 'cinder_api_service' => 'cinder-api', + 'cinder_client_packages' => ['python-cinderclient'], + 'cinder_volume_packages' => ['cinder-volume'], + 'cinder_volume_service' => 'cinder-volume', + 'cinder_scheduler_packages' => ['cinder-scheduler'], + 'cinder_scheduler_service' => 'cinder-scheduler', + 'cinder_ceph_packages' => ['python-ceph', 'ceph-common'], + 'cinder_iscsitarget_packages' => ['tgt'], + 'cinder_iscsitarget_service' => 'tgt', + 'cinder_nfs_packages' => ['nfs-common'], + 'cinder_emc_packages' => ['python-pywbem'], + 'package_overrides' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" } end diff --git a/chef/cookbooks/openstack-block-storage/files/default/cinder-volumes.sh b/chef/cookbooks/openstack-block-storage/files/default/cinder-volumes.sh deleted file mode 100644 index 045c7f1..0000000 --- a/chef/cookbooks/openstack-block-storage/files/default/cinder-volumes.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# This file is going to create a loop file as volume disk. -# - -vgdisplay |grep cinder-volumes; -if [ $? -ne 0 ]; then - dd if=/dev/zero of=/mnt/cinder-volumes bs=1 count=0 seek=2G - losetup /dev/loop0 /mnt/cinder-volumes - pvcreate /dev/loop0 - vgcreate cinder-volumes /dev/loop0 -fi diff --git a/chef/cookbooks/openstack-block-storage/metadata.rb b/chef/cookbooks/openstack-block-storage/metadata.rb index b8a4a53..2de3d69 100644 --- a/chef/cookbooks/openstack-block-storage/metadata.rb +++ b/chef/cookbooks/openstack-block-storage/metadata.rb @@ -1,24 +1,27 @@ -name "openstack-block-storage" -maintainer "AT&T Services, Inc." -maintainer_email "cookbooks@lists.tfoundry.com" -license "Apache 2.0" -description "The OpenStack Advanced Volume Management service Cinder." +# encoding: UTF-8 +name 'openstack-block-storage' +maintainer 'AT&T Services, Inc.' +maintainer_email 'cookbooks@lists.tfoundry.com' +license 'Apache 2.0' +description 'The OpenStack Advanced Volume Management service Cinder.' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "7.0.1" +version '9.2.3' -recipe "openstack-block-storage::common", "Defines the common pieces of repeated code from the other recipes" -recipe "openstack-block-storage::api", "Installs the cinder-api, sets up the cinder database, and cinder service/user/endpoints in keystone" -recipe "openstack-block-storage::keystone_registration", "Registers cinder service/user/endpoints in keystone" -recipe "openstack-block-storage::scheduler", "Installs the cinder-scheduler service" -recipe "openstack-block-storage::volume", "Installs the cinder-volume service and sets up the iscsi helper" +recipe 'openstack-block-storage::api', 'Installs the cinder-api, sets up the cinder database, and cinder service/user/endpoints in keystone' +recipe 'openstack-block-storage::client', 'Install packages required for cinder client' +recipe 'openstack-block-storage::common', 'Defines the common pieces of repeated code from the other recipes' +recipe 'openstack-block-storage::keystone_registration', 'Registers cinder service/user/endpoints in keystone' +recipe 'openstack-block-storage::scheduler', 'Installs the cinder-scheduler service' +recipe 'openstack-block-storage::volume', 'Installs the cinder-volume service and sets up the iscsi helper' %w{ ubuntu fedora redhat centos suse }.each do |os| supports os end -depends "apt" -depends "openstack-common", "~> 0.4.0" -depends "openstack-identity", "~> 7.0.0" -depends "openstack-image", "~> 7.0.0" -depends "selinux" -depends "python" +depends 'apt', '>= 2.3.8' +depends 'openstack-common', '~> 9.0' +depends 'openstack-identity', '~> 9.0' +depends 'openstack-image', '~> 9.0' +depends 'selinux', '>= 0.7.2' +depends 'python', '>= 1.4.6' +depends 'ceph', '>= 0.2.1' diff --git a/chef/cookbooks/openstack-block-storage/recipes/api.rb b/chef/cookbooks/openstack-block-storage/recipes/api.rb index 650fdfe..9bb7176 100644 --- a/chef/cookbooks/openstack-block-storage/recipes/api.rb +++ b/chef/cookbooks/openstack-block-storage/recipes/api.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-block-storage # Recipe:: api @@ -20,75 +21,72 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -include_recipe "openstack-block-storage::cinder-common" +include_recipe 'openstack-block-storage::cinder-common' -platform_options = node["openstack"]["block-storage"]["platform"] +platform_options = node['openstack']['block-storage']['platform'] -platform_options["cinder_api_packages"].each do |pkg| +platform_options['cinder_api_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] - + options platform_options['package_overrides'] action :upgrade end end -db_type = node['openstack']['db']['volume']['db_type'] +db_type = node['openstack']['db']['block-storage']['service_type'] platform_options["#{db_type}_python_packages"].each do |pkg| package pkg do action :upgrade end end -directory ::File.dirname(node["openstack"]["block-storage"]["api"]["auth"]["cache_dir"]) do - owner node["openstack"]["block-storage"]["user"] - group node["openstack"]["block-storage"]["group"] +directory ::File.dirname(node['openstack']['block-storage']['api']['auth']['cache_dir']) do + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] mode 00700 end -directory "/var/lock/cinder" do - owner node["openstack"]["block-storage"]["user"] - group node["openstack"]["block-storage"]["group"] - mode 00700 -end - -service "cinder-api" do - service_name platform_options["cinder_api_service"] - supports :status => true, :restart => true - +service 'cinder-api' do + service_name platform_options['cinder_api_service'] + supports status: true, restart: true action :enable - subscribes :restart, "template[/etc/cinder/cinder.conf]" + subscribes :restart, 'template[/etc/cinder/cinder.conf]' end -identity_admin_endpoint = endpoint "identity-admin" -service_tenant_name = node['openstack']['identity']['volume']['tenant'] -service_user = node['openstack']['identity']['volume']['username'] -service_pass = service_password node['openstack']['identity']['volume']['password'] +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' +service_pass = get_password 'service', 'openstack-block-storage' -execute "cinder-manage db sync" +auth_uri = auth_uri_transform(identity_endpoint.to_s, node['openstack']['block-storage']['api']['auth']['version']) -template "/etc/cinder/api-paste.ini" do - source "api-paste.ini.erb" - group node["openstack"]["block-storage"]["group"] - owner node["openstack"]["block-storage"]["user"] - mode 00644 +execute 'cinder-manage db sync' do + user node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] +end + +template '/etc/cinder/api-paste.ini' do + source 'api-paste.ini.erb' + group node['openstack']['block-storage']['group'] + owner node['openstack']['block-storage']['user'] + mode 00644 variables( - :identity_admin_endpoint => identity_admin_endpoint, - :service_tenant_name => service_tenant_name, - :service_user => service_user, - :service_pass => service_pass - ) + auth_uri: auth_uri, + identity_admin_endpoint: identity_admin_endpoint, + service_pass: service_pass + ) - notifies :restart, "service[cinder-api]", :immediately + notifies :restart, 'service[cinder-api]', :immediately end -template "/etc/cinder/policy.json" do - source "policy.json.erb" - owner node["openstack"]["block-storage"]["user"] - group node["openstack"]["block-storage"]["group"] - mode 00644 - notifies :restart, "service[cinder-api]" +if node['openstack']['block-storage']['policyfile_url'] + remote_file '/etc/cinder/policy.json' do + source node['openstack']['block-storage']['policyfile_url'] + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + mode 00644 + notifies :restart, 'service[cinder-api]' + end end diff --git a/chef/cookbooks/openstack-block-storage/recipes/cinder-common.rb b/chef/cookbooks/openstack-block-storage/recipes/cinder-common.rb index c4ad818..60e52f0 100644 --- a/chef/cookbooks/openstack-block-storage/recipes/cinder-common.rb +++ b/chef/cookbooks/openstack-block-storage/recipes/cinder-common.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,52 +13,85 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -if node["openstack"]["block-storage"]["syslog"]["use"] - include_recipe "openstack-common::logging" +if node['openstack']['block-storage']['syslog']['use'] + include_recipe 'openstack-common::logging' end -platform_options = node["openstack"]["block-storage"]["platform"] +platform_options = node['openstack']['block-storage']['platform'] -platform_options["cinder_common_packages"].each do |pkg| +platform_options['cinder_common_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] - + options platform_options['package_overrides'] action :upgrade end end -db_user = node["openstack"]["db"]["volume"]["username"] -db_pass = db_password node["openstack"]["db"]["volume"]["password"] -sql_connection = db_uri("volume", db_user, db_pass) +db_user = node['openstack']['db']['block-storage']['username'] +db_pass = get_password 'db', 'cinder' +sql_connection = db_uri('block-storage', db_user, db_pass) -if node["openstack"]["block-storage"]["rabbit"]["ha"] - rabbit_hosts = node['openstack']['mq']['bind_address'] +mq_service_type = node['openstack']['mq']['block-storage']['service_type'] + +if mq_service_type == 'rabbitmq' + if node['openstack']['mq']['block-storage']['rabbit']['ha'] + rabbit_hosts = rabbit_servers + end + #mq_password = get_password 'user', node['openstack']['mq']['block-storage']['rabbit']['userid'] + mq_password = get_password('user', \ + node['openstack']['mq']['user'], \ + node['openstack']['mq']['password']) +elsif mq_service_type == 'qpid' + #mq_password = get_password 'user', node['openstack']['mq']['block-storage']['qpid']['username'] + mq_password = get_password('user', \ + node['openstack']['mq']['user'], \ + node['openstack']['mq']['password']) end -rabbit_pass = user_password node['openstack']['mq']['password'] -glance_api_endpoint = endpoint "image-api" +case node['openstack']['block-storage']['volume']['driver'] +when 'cinder.volume.drivers.solidfire.SolidFire' + solidfire_pass = get_password 'user', node['openstack']['block-storage']['solidfire']['san_login'] +when 'cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver' + ibmnas_pass = get_password 'user', node['openstack']['block-storage']['ibmnas']['nas_login'] +when 'cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver' + vmware_host_pass = get_secret node['openstack']['block-storage']['vmware']['secret_name'] +end -directory "/etc/cinder" do - group node["openstack"]["block-storage"]["group"] - owner node["openstack"]["block-storage"]["user"] +glance_api_endpoint = endpoint 'image-api' +cinder_api_bind = endpoint 'block-storage-api-bind' + +directory '/etc/cinder' do + group node['openstack']['block-storage']['group'] + owner node['openstack']['block-storage']['user'] mode 00750 action :create end -template "/etc/cinder/cinder.conf" do - source "cinder.conf.erb" - group node["openstack"]["block-storage"]["group"] - owner node["openstack"]["block-storage"]["user"] - mode 00644 +template '/etc/cinder/cinder.conf' do + source 'cinder.conf.erb' + group node['openstack']['block-storage']['group'] + owner node['openstack']['block-storage']['user'] + mode 00644 variables( - :sql_connection => sql_connection, - :rabbit_password => rabbit_pass, - :rabbit_hosts => rabbit_hosts, - :glance_host => glance_api_endpoint.host, - :glance_port => glance_api_endpoint.port + sql_connection: sql_connection, + mq_service_type: mq_service_type, + mq_password: mq_password, + rabbit_hosts: rabbit_hosts, + glance_host: glance_api_endpoint.host, + glance_port: glance_api_endpoint.port, + ibmnas_pass: ibmnas_pass, + solidfire_pass: solidfire_pass, + volume_api_bind_address: cinder_api_bind.host, + volume_api_bind_port: cinder_api_bind.port, + vmware_host_pass: vmware_host_pass ) end + +directory node['openstack']['block-storage']['lock_path'] do + group node['openstack']['block-storage']['group'] + owner node['openstack']['block-storage']['user'] + mode 00700 +end diff --git a/chef/cookbooks/openstack-block-storage/recipes/cinder-config-ceph.rb b/chef/cookbooks/openstack-block-storage/recipes/cinder-config-ceph.rb new file mode 100644 index 0000000..878e010 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/recipes/cinder-config-ceph.rb @@ -0,0 +1,75 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage +# Recipe:: volume +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. +# Copyright 2013, SUSE Linux Gmbh. +# Copyright 2013, IBM, Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +if node['openstack']['block-storage']['volume']['driver'] == 'cinder.volume.drivers.rbd.RBDDriver' + + include_recipe 'ceph::_common' + include_recipe 'ceph::mon_install' + include_recipe 'ceph::conf' + cluster = 'ceph' + + platform_options = node['openstack']['block-storage']['platform'] + + platform_options['cinder_volume_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade + end + end + + rbd_user = node['openstack']['block-storage']['rbd_user'] + + if mon_nodes.empty? + rbd_key = "" + elsif !mon_master['ceph'].has_key?('cinder-secret') + rbd_key = "" + else + rbd_key = mon_master['ceph']['cinder-secret'] + end + + template "/etc/ceph/ceph.client.#{rbd_user}.keyring" do + source 'ceph.client.keyring.erb' + cookbook 'openstack-common' + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + mode '0644' + variables( + name: rbd_user, + key: rbd_key + ) + end + + include_recipe 'openstack-block-storage::cinder-common' + + service 'cinder-volume-ceph' do + service_name platform_options['cinder_volume_service'] + supports status: true, restart: true + action :restart + subscribes :restart, 'template[/etc/cinder/cinder.conf]' + end +end diff --git a/chef/cookbooks/openstack-block-storage/recipes/client.rb b/chef/cookbooks/openstack-block-storage/recipes/client.rb new file mode 100644 index 0000000..6ac5086 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/recipes/client.rb @@ -0,0 +1,32 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage +# Recipe:: client +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +platform_options = node['openstack']['block-storage']['platform'] +platform_options['cinder_client_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end diff --git a/chef/cookbooks/openstack-block-storage/recipes/identity_registration.rb b/chef/cookbooks/openstack-block-storage/recipes/identity_registration.rb index c6aef22..6f1d8f9 100644 --- a/chef/cookbooks/openstack-block-storage/recipes/identity_registration.rb +++ b/chef/cookbooks/openstack-block-storage/recipes/identity_registration.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-block-storage # Recipe:: identity_registration @@ -19,67 +20,72 @@ # limitations under the License. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -identity_admin_endpoint = endpoint "identity-admin" -bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +identity_admin_endpoint = endpoint 'identity-admin' +bootstrap_token = get_secret 'openstack_identity_bootstrap_token' auth_uri = ::URI.decode identity_admin_endpoint.to_s -cinder_api_endpoint = endpoint "volume-api" -service_pass = service_password node['openstack']['identity']['volume']['password'] -region = node["openstack"]["block-storage"]["region"] -service_tenant_name = node['openstack']['identity']['volume']['tenant'] -service_user = node['openstack']['identity']['volume']['username'] -service_role = node['openstack']['identity']['volume']['role'] +cinder_api_endpoint = endpoint 'block-storage-api' +service_pass = get_password 'service', 'openstack-block-storage' +region = node['openstack']['block-storage']['region'] +service_tenant_name = node['openstack']['block-storage']['service_tenant_name'] +service_user = node['openstack']['block-storage']['service_user'] +service_role = node['openstack']['block-storage']['service_role'] -openstack_identity_register "Register Cinder Volume Service" do +openstack_identity_register 'Register Service Tenant' do auth_uri auth_uri bootstrap_token bootstrap_token - service_name "cinder" - service_type "volume" - service_description "Cinder Volume Service" + tenant_name service_tenant_name + tenant_description 'Service Tenant' + + action :create_tenant +end + +openstack_identity_register 'Register Cinder Volume Service' do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name 'cinder' + service_type 'volume' + service_description 'Cinder Volume Service' endpoint_region region endpoint_adminurl ::URI.decode cinder_api_endpoint.to_s endpoint_internalurl ::URI.decode cinder_api_endpoint.to_s endpoint_publicurl ::URI.decode cinder_api_endpoint.to_s - action :create_service end -openstack_identity_register "Register Cinder Volume Endpoint" do +openstack_identity_register 'Register Cinder Volume Endpoint' do auth_uri auth_uri bootstrap_token bootstrap_token - service_name "cinder" - service_type "volume" - service_description "Cinder Volume Service" + service_name 'cinder' + service_type 'volume' + service_description 'Cinder Volume Service' endpoint_region region endpoint_adminurl ::URI.decode cinder_api_endpoint.to_s endpoint_internalurl ::URI.decode cinder_api_endpoint.to_s endpoint_publicurl ::URI.decode cinder_api_endpoint.to_s - action :create_endpoint end -openstack_identity_register "Register Cinder Service User" do +openstack_identity_register 'Register Cinder Service User' do auth_uri auth_uri bootstrap_token bootstrap_token tenant_name service_tenant_name user_name service_user user_pass service_pass user_enabled true # Not required as this is the default - action :create_user end -openstack_identity_register "Grant service Role to Cinder Service User for Cinder Service Tenant" do +openstack_identity_register 'Grant service Role to Cinder Service User for Cinder Service Tenant' do auth_uri auth_uri bootstrap_token bootstrap_token tenant_name service_tenant_name user_name service_user role_name service_role - action :grant_role end diff --git a/chef/cookbooks/openstack-block-storage/recipes/scheduler.rb b/chef/cookbooks/openstack-block-storage/recipes/scheduler.rb index ddd56e3..2e7375d 100644 --- a/chef/cookbooks/openstack-block-storage/recipes/scheduler.rb +++ b/chef/cookbooks/openstack-block-storage/recipes/scheduler.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-block-storage # Recipe:: scheduler @@ -20,45 +21,49 @@ # limitations under the License. # -include_recipe "openstack-block-storage::cinder-common" +include_recipe 'openstack-block-storage::cinder-common' -platform_options = node["openstack"]["block-storage"]["platform"] +platform_options = node['openstack']['block-storage']['platform'] -platform_options["cinder_scheduler_packages"].each do |pkg| +platform_options['cinder_scheduler_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end -# FIXME this can be removed if/when 1:2013.1-0ubuntu2 makes it into precise -#if platform?("ubuntu") && (node["platform_version"].to_f == 12.04) -# include_recipe "python" -# python_pip "stevedore" do -# action :upgrade -# end -#end - -db_type = node['openstack']['db']['volume']['db_type'] +db_type = node['openstack']['db']['block-storage']['service_type'] platform_options["#{db_type}_python_packages"].each do |pkg| package pkg do action :upgrade end end -service "cinder-scheduler" do - service_name platform_options["cinder_scheduler_service"] - supports :status => true, :restart => true - - action [ :enable, :start ] - subscribes :restart, "template[/etc/cinder/cinder.conf]" +service 'cinder-scheduler' do + service_name platform_options['cinder_scheduler_service'] + supports status: true, restart: true + action [:enable, :start] + subscribes :restart, 'template[/etc/cinder/cinder.conf]' end -if node["openstack"]["metering"] - cron "cinder-volume-usage-audit" do - command "cinder-volume-usage-audit > /var/log/cinder/audit.log 2>&1" - action :create - user node["openstack"]["block-storage"]["user"] +audit_bin_dir = platform_family?('debian') ? '/usr/bin' : '/usr/local/bin' +audit_log = node['openstack']['block-storage']['cron']['audit_logfile'] + +if node['openstack']['telemetry'] + scheduler_role = node['openstack']['block-storage']['scheduler_role'] + results = search(:node, "roles:#{scheduler_role}") + cron_node = results.map { |a| a.name }.sort[0] + Chef::Log.debug("Volume audit cron node: #{cron_node}") + + cron 'cinder-volume-usage-audit' do + day node['openstack']['block-storage']['cron']['day'] || '*' + hour node['openstack']['block-storage']['cron']['hour'] || '*' + minute node['openstack']['block-storage']['cron']['minute'] + month node['openstack']['block-storage']['cron']['month'] || '*' + weekday node['openstack']['block-storage']['cron']['weekday'] || '*' + command "#{audit_bin_dir}/cinder-volume-usage-audit > #{audit_log} 2>&1" + action cron_node == node.name ? :create : :delete + user node['openstack']['block-storage']['user'] end end diff --git a/chef/cookbooks/openstack-block-storage/recipes/test.rb b/chef/cookbooks/openstack-block-storage/recipes/test.rb deleted file mode 100644 index 04237ae..0000000 --- a/chef/cookbooks/openstack-block-storage/recipes/test.rb +++ /dev/null @@ -1,20 +0,0 @@ -#include_recipe "openstack-block-storage::cinder-common" -#include_recipe "parted::default" - -node.set['create_partition'] = false -node.set['partitions'] = nil -openstack_block_storage_volume "/dev/sdb" do - action :create_partition -end - -openstack_block_storage_volume "/dev/sdc" do - action :create_partition -end - -openstack_block_storage_volume "/dev/sdb" do - action :mk_cinder_vol -end - -openstack_block_storage_volume "/dev/sdc" do - action :mk_cinder_vol -end diff --git a/chef/cookbooks/openstack-block-storage/recipes/volume.rb b/chef/cookbooks/openstack-block-storage/recipes/volume.rb index 1191649..34a1149 100644 --- a/chef/cookbooks/openstack-block-storage/recipes/volume.rb +++ b/chef/cookbooks/openstack-block-storage/recipes/volume.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-block-storage # Recipe:: volume @@ -6,6 +7,7 @@ # Copyright 2012-2013, AT&T Services, Inc. # Copyright 2013, Opscode, Inc. # Copyright 2013, SUSE Linux Gmbh. +# Copyright 2013, IBM, Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,117 +22,234 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -include_recipe "openstack-block-storage::cinder-common" +include_recipe 'openstack-block-storage::cinder-common' -platform_options = node["openstack"]["block-storage"]["platform"] +platform_options = node['openstack']['block-storage']['platform'] -package "parted" do - action :upgrade -end - -platform_options["cinder_volume_packages"].each do |pkg| +platform_options['cinder_volume_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] - + options platform_options['package_overrides'] action :upgrade end end -db_type = node['openstack']['db']['volume']['db_type'] +db_type = node['openstack']['db']['block-storage']['service_type'] platform_options["#{db_type}_python_packages"].each do |pkg| package pkg do action :upgrade end end -platform_options["cinder_iscsitarget_packages"].each do |pkg| +platform_options['cinder_iscsitarget_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] - + options platform_options['package_overrides'] action :upgrade end end -directory "/var/lock/cinder" do - owner node["openstack"]["block-storage"]["user"] - group node["openstack"]["block-storage"]["group"] - mode 00700 -end +case node['openstack']['block-storage']['volume']['driver'] +when 'cinder.volume.drivers.netapp.iscsi.NetAppISCSIDriver' + node.override['openstack']['block-storage']['netapp']['dfm_password'] = get_password 'service', 'netapp' -execute "timeout 300s sh -c 'while ! cinder-manage db sync; do sleep 30; done'" +when 'cinder.volume.drivers.rbd.RBDDriver' + # this is used in the cinder.conf template + # node.override['openstack']['block-storage']['rbd_secret_uuid'] = get_secret node['openstack']['block-storage']['rbd_secret_name'] + # + # rbd_user = node['openstack']['block-storage']['rbd_user'] + # rbd_key = get_password 'service', node['openstack']['block-storage']['rbd_key_name'] + # + # include_recipe 'openstack-common::ceph_client' + # + # platform_options['cinder_ceph_packages'].each do |pkg| + # package pkg do + # options platform_options['package_overrides'] + # action :upgrade + # end + # end + # + # template "/etc/ceph/ceph.client.#{rbd_user}.keyring" do + # source 'ceph.client.keyring.erb' + # cookbook 'openstack-common' + # owner node['openstack']['block-storage']['user'] + # group node['openstack']['block-storage']['group'] + # mode '0600' + # variables( + # name: rbd_user, + # key: rbd_key + # ) + # end -case node["openstack"]["block-storage"]["volume"]["driver"] - when "cinder.volume.drivers.netapp.iscsi.NetAppISCSIDriver" - node.override["openstack"]["block-storage"]["netapp"]["dfm_password"] = service_password "netapp" +when 'cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver' + node.override['openstack']['block-storage']['netapp']['netapp_server_password'] = get_password 'service', 'netapp-filer' - when "cinder.volume.drivers.RBDDriver" - node.override["openstack"]["block-storage"]["rbd_secret_uuid"] = service_password "rbd" + directory node['openstack']['block-storage']['nfs']['mount_point_base'] do + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + action :create + end - when "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" - node.override["openstack"]["block-storage"]["netapp"]["netapp_server_password"] = service_password "netapp-filer" + template node['openstack']['block-storage']['nfs']['shares_config'] do + source 'shares.conf.erb' + mode '0600' + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + variables( + host: node['openstack']['block-storage']['netapp']['netapp_server_hostname'], + export: node['openstack']['block-storage']['netapp']['export'] + ) + notifies :restart, 'service[cinder-volume]' + end - directory node["openstack"]["block-storage"]["nfs"]["mount_point_base"] do - owner node["openstack"]["block-storage"]["user"] - group node["openstack"]["block-storage"]["group"] - action :create + platform_options['cinder_nfs_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade end + end - template node["openstack"]["block-storage"]["nfs"]["shares_config"] do - source "shares.conf.erb" - mode "0600" - owner node["openstack"]["block-storage"]["user"] - group node["openstack"]["block-storage"]["group"] - variables( - "host" => node["openstack"]["block-storage"]["netapp"]["netapp_server_hostname"], - "export" => node["openstack"]["block-storage"]["netapp"]["export"] - ) - notifies :restart, "service[cinder-volume]" +when 'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver' + file node['openstack']['block-storage']['san']['san_private_key'] do + mode '0400' + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + end + +when 'cinder.volume.drivers.gpfs.GPFSDriver' + directory node['openstack']['block-storage']['gpfs']['gpfs_mount_point_base'] do + mode '0755' + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + recursive true + end + +when 'cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver' + directory node['openstack']['block-storage']['ibmnas']['mount_point_base'] do + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + mode '0755' + recursive true + action :create + end + + platform_options['cinder_nfs_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade end + end - platform_options["cinder_nfs_packages"].each do |pkg| - package pkg do - options platform_options["package_overrides"] + template node['openstack']['block-storage']['ibmnas']['shares_config'] do + source 'nfs_shares.conf.erb' + mode '0600' + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + variables( + host: node['openstack']['block-storage']['ibmnas']['nas_access_ip'], + export: node['openstack']['block-storage']['ibmnas']['export'] + ) + notifies :restart, 'service[cinder-volume]' + end - action :upgrade - end - end - - when "cinder.volume.drivers.lvm.LVMISCSIDriver" - package "bc" do +when 'cinder.volume.drivers.lvm.LVMISCSIDriver' +# if node['openstack']['block-storage']['volume']['create_volume_group'] +# volume_size = node['openstack']['block-storage']['volume']['volume_group_size'] +# seek_count = volume_size.to_i * 1024 + # default volume group is 40G +# seek_count = 40 * 1024 if seek_count == 0 +# vg_name = node['openstack']['block-storage']['volume']['volume_group'] +# vg_file = "#{node['openstack']['block-storage']['volume']['state_path']}/#{vg_name}.img" + + # create volume group +# execute 'Create Cinder volume group' do +# command "dd if=/dev/zero of=#{vg_file} bs=1M seek=#{seek_count} count=0; vgcreate #{vg_name} $(losetup --show -f #{vg_file})" +# action :run +# not_if "vgs #{vg_name}" +# end + +# template '/etc/init.d/cinder-group-active' do +# source 'cinder-group-active.erb' +# mode '755' +# variables( +# volume_name: vg_name, +# volume_file: vg_file +# ) +# notifies :start, 'service[cinder-group-active]', :immediately +# end + +# service 'cinder-group-active' do +# service_name 'cinder-group-active' + +# action [:enable, :start] +# end + + package 'bc' do action :upgrade end - openstack_block_storage_volume node["openstack"]["volume"]["disk"] do + openstack_block_storage_volume node['openstack']['block-storage']['volume']['disk'] do action :create_partition end - openstack_block_storage_volume node["openstack"]["volume"]["disk"] do + openstack_block_storage_volume node['openstack']['block-storage']['volume']['disk'] do action :mk_cinder_vol end + +# if node['openstack']['block-storage']['volume']['disk'].eql?('loopfile') +# template '/etc/init.d/cinder-group-active' do +# source 'cinder-group-active.erb' +# mode '755' +# variables( +# volume_name: vg_name, +# volume_file: vg_file +# ) +# notifies :start, 'service[cinder-group-active]', :immediately +# end + +# service 'cinder-group-active' do +# service_name 'cinder-group-active' +# action [:enable, :start] +# end +# end + +when 'cinder.volume.drivers.emc.emc_smis_iscsi.EMCSMISISCSIDriver' + platform_options['cinder_emc_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade + end + end + + ecom_password = get_password('user', node['openstack']['block-storage']['emc']['EcomUserName']) + + template node['openstack']['block-storage']['emc']['cinder_emc_config_file'] do + source 'cinder_emc_config.xml.erb' + variables( + ecom_password: ecom_password + ) + mode 00644 + notifies :restart, 'service[iscsitarget]', :immediately + end end -service "cinder-volume" do - service_name platform_options["cinder_volume_service"] - supports :status => true, :restart => true - - action [ :enable, :restart ] - subscribes :restart, "template[/etc/cinder/cinder.conf]" +service 'cinder-volume' do + service_name platform_options['cinder_volume_service'] + supports status: true, restart: true + action [:enable, :start] + subscribes :restart, 'template[/etc/cinder/cinder.conf]' end -service "iscsitarget" do - service_name platform_options["cinder_iscsitarget_service"] - supports :status => true, :restart => true - +service 'iscsitarget' do + service_name platform_options['cinder_iscsitarget_service'] + supports status: true, restart: true action :enable end -template "/etc/tgt/targets.conf" do - source "targets.conf.erb" +template '/etc/tgt/targets.conf' do + source 'targets.conf.erb' mode 00600 - - notifies :restart, "service[iscsitarget]", :immediately + notifies :restart, 'service[iscsitarget]', :immediately end diff --git a/chef/cookbooks/openstack-block-storage/spec/api-opensuse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/api-opensuse_spec.rb index bcba515..dedca93 100644 --- a/chef/cookbooks/openstack-block-storage/spec/api-opensuse_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/api-opensuse_spec.rb @@ -1,38 +1,37 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::api" do - before { block_storage_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-block-storage::api" +require_relative 'spec_helper' + +describe 'openstack-block-storage::api' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + + it 'upgrades cinder api package' do + expect(chef_run).to upgrade_package 'openstack-cinder-api' end - it "installs cinder api packages" do - expect(@chef_run).to upgrade_package "openstack-cinder-api" + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package 'python-mysql' end - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "python-mysql" + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' + + expect(chef_run).to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'python-mysql' end - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::api" - - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "python-mysql" + it 'starts cinder api on boot' do + expect(chef_run).to enable_service 'openstack-cinder-api' end - it "starts cinder api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-cinder-api" - end - - expect_creates_policy_json( - "service[cinder-api]", "openstack-cinder", "openstack-cinder") expect_creates_cinder_conf( - "service[cinder-api]", "openstack-cinder", "openstack-cinder") + 'service[cinder-api]', 'openstack-cinder', 'openstack-cinder') end end diff --git a/chef/cookbooks/openstack-block-storage/spec/api-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/api-redhat_spec.rb index 1e38cbc..d7ad3a4 100644 --- a/chef/cookbooks/openstack-block-storage/spec/api-redhat_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/api-redhat_spec.rb @@ -1,33 +1,42 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::api" do - before { block_storage_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-block-storage::api" +require_relative 'spec_helper' + +describe 'openstack-block-storage::api' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + + it 'upgrades cinder api package' do + expect(chef_run).to upgrade_package 'python-cinderclient' end - it "installs cinder api packages" do - expect(@chef_run).to upgrade_package "python-cinderclient" + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package 'MySQL-python' end - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "MySQL-python" + it 'upgrades db2 python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'db2' + + ['python-ibm-db', 'python-ibm-db-sa'].each do |pkg| + expect(chef_run).to upgrade_package pkg + end end - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::api" + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "MySQL-python" + expect(chef_run).to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'MySQL-python' end - it "starts cinder api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-cinder-api" + it 'starts cinder api on boot' do + expect(chef_run).to enable_service 'openstack-cinder-api' end end end diff --git a/chef/cookbooks/openstack-block-storage/spec/api_spec.rb b/chef/cookbooks/openstack-block-storage/spec/api_spec.rb index c88335e..9b5739c 100644 --- a/chef/cookbooks/openstack-block-storage/spec/api_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/api_spec.rb @@ -1,138 +1,161 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::api" do - before { block_storage_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["syslog"]["use"] = true - end - @chef_run.converge "openstack-block-storage::api" +require_relative 'spec_helper' + +describe 'openstack-block-storage::api' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + include_examples 'common-logging' + + expect_creates_cinder_conf 'service[cinder-api]', 'cinder', 'cinder' + + it 'upgrades cinder api packages' do + expect(chef_run).to upgrade_package('cinder-api') + expect(chef_run).to upgrade_package('python-cinderclient') end - expect_runs_openstack_common_logging_recipe - - it "doesn't run logging recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-block-storage::api" - - expect(chef_run).not_to include_recipe "openstack-common::logging" + it 'starts cinder api on boot' do + expect(chef_run).to enable_service('cinder-api') end - it "installs cinder api packages" do - expect(@chef_run).to upgrade_package "cinder-api" - expect(@chef_run).to upgrade_package "python-cinderclient" + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package('python-mysqldb') end - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "python-mysqldb" + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' + + expect(chef_run).to upgrade_package('python-psycopg2') + expect(chef_run).not_to upgrade_package('python-mysqldb') end - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::api" + describe '/var/cache/cinder' do + let(:dir) { chef_run.directory('/var/cache/cinder') } - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "python-mysqldb" - end - - describe "/var/cache/cinder" do - before do - @dir = @chef_run.directory "/var/cache/cinder" + it 'has proper owner' do + expect(dir.owner).to eq('cinder') + expect(dir.group).to eq('cinder') end - it "has proper owner" do - expect(@dir).to be_owned_by "cinder", "cinder" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" + it 'has proper modes' do + expect(sprintf('%o', dir.mode)).to eq('700') end end - it "starts cinder api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "cinder-api" + it 'runs db migrations' do + expect(chef_run).to run_execute('cinder-manage db sync').with(user: 'cinder', group: 'cinder') end - expect_creates_cinder_conf "service[cinder-api]", "cinder", "cinder" + describe 'api-paste.ini' do + let(:file) { chef_run.template('/etc/cinder/api-paste.ini') } - describe "cinder.conf" do - before do - @file = "/etc/cinder/cinder.conf" + it 'has proper owner' do + expect(file.owner).to eq('cinder') + expect(file.group).to eq('cinder') end - it "runs logging recipe if node attributes say to" do - expect(@chef_run).to create_file_with_content @file, - "log_config = /etc/openstack/logging.conf" + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq('644') end - it "doesn't run logging recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-block-storage::api" - - expect(chef_run).not_to create_file_with_content @file, - "log_config = /etc/openstack/logging.conf" + it 'notifies cinder-api restart' do + expect(file).to notify('service[cinder-api]').to(:restart) end - it "has rbd driver settings" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["volume"] = { - "driver" => "cinder.volume.drivers.RBDDriver" - } + context 'template contents' do + it 'has signing_dir' do + node.set['openstack']['block-storage']['api']['auth']['cache_dir'] = 'auth_cache_dir' + + expect(chef_run).to render_file(file.name).with_content(/^signing_dir = auth_cache_dir$/) end - chef_run.converge "openstack-block-storage::api" - expect(chef_run).to create_file_with_content @file, - /^rbd_/ - expect(chef_run).not_to create_file_with_content @file, - /^netapp_/ - end + context 'endpoint related' do + before do + endpoint = double(port: 'port', host: 'host', scheme: 'scheme') + Chef::Recipe.any_instance.stub(:endpoint) + .with('image-api') + .and_return(endpoint) + Chef::Recipe.any_instance.stub(:endpoint) + .with('identity-admin') + .and_return(endpoint) + Chef::Recipe.any_instance.stub(:endpoint) + .with('identity-api') + .and_return(endpoint) + Chef::Recipe.any_instance.stub(:endpoint) + .with('block-storage-api-bind') + .and_return(endpoint) + Chef::Recipe.any_instance.stub(:auth_uri_transform) + .and_return('auth_uri_transform') + end - it "has netapp driver settings" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["volume"] = { - "driver" => "cinder.volume.drivers.netapp.NetAppISCSIDriver" - } + it 'has auth_uri' do + expect(chef_run).to render_file(file.name).with_content(/^auth_uri = auth_uri_transform$/) + end + + it 'has auth_host' do + expect(chef_run).to render_file(file.name).with_content(/^auth_host = host$/) + end + + it 'has auth_port' do + expect(chef_run).to render_file(file.name).with_content(/^auth_port = port$/) + end + + it 'has auth_protocol' do + expect(chef_run).to render_file(file.name).with_content(/^auth_protocol = scheme$/) + end end - chef_run.converge "openstack-block-storage::api" - expect(chef_run).to create_file_with_content @file, - /^netapp_/ - expect(chef_run).not_to create_file_with_content @file, - /^rbd_/ + it 'has no auth_version when auth_version is v2.0' do + node.set['openstack']['block-storage']['api']['auth']['version'] = 'v2.0' + + expect(chef_run).not_to render_file(file.name).with_content(/^auth_version = v2.0$/) + end + + it 'has auth_version when auth version is not v2.0' do + node.set['openstack']['block-storage']['api']['auth']['version'] = 'v3.0' + + expect(chef_run).to render_file(file.name).with_content(/^auth_version = v3.0$/) + end + + it 'has an admin tenant name' do + node.set['openstack']['block-storage']['service_tenant_name'] = 'tenant_name' + + expect(chef_run).to render_file(file.name).with_content(/^admin_tenant_name = tenant_name$/) + end + + it 'has an admin user' do + node.set['openstack']['block-storage']['service_user'] = 'username' + + expect(chef_run).to render_file(file.name).with_content(/^admin_user = username$/) + end + + it 'has an admin password' do + # (fgimenez) the get_password mocking is set in spec/spec_helper.rb + expect(chef_run).to render_file(file.name).with_content(/^admin_password = cinder-pass$/) + end end end - it "runs db migrations" do - cmd = "cinder-manage db sync" - - expect(@chef_run).to execute_command cmd - end - - expect_creates_policy_json "service[cinder-api]", "cinder", "cinder" - - describe "api-paste.ini" do - before do - @file = @chef_run.template "/etc/cinder/api-paste.ini" + describe 'policy file' do + it 'does not manage policy file unless specified' do + expect(chef_run).not_to create_remote_file('/etc/cinder/policy.json') end + describe 'policy file specified' do + before { node.set['openstack']['block-storage']['policyfile_url'] = 'http://server/mypolicy.json' } + let(:remote_policy) { chef_run.remote_file('/etc/cinder/policy.json') } - it "has proper owner" do - expect(@file).to be_owned_by "cinder", "cinder" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "has signing_dir" do - expect(@chef_run).to create_file_with_content @file.name, - "signing_dir = /var/cache/cinder/api" - end - - it "notifies cinder-api restart" do - expect(@file).to notify "service[cinder-api]", :restart + it 'manages policy file when remote file is specified' do + expect(chef_run).to create_remote_file('/etc/cinder/policy.json').with( + user: 'cinder', + group: 'cinder', + mode: 00644) + end end end end diff --git a/chef/cookbooks/openstack-block-storage/spec/cinder_common-opensuse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/cinder_common-opensuse_spec.rb deleted file mode 100644 index dcbca9c..0000000 --- a/chef/cookbooks/openstack-block-storage/spec/cinder_common-opensuse_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-block-storage::cinder-common" do - before { block_storage_stubs } - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| - n.set["openstack"]["mq"] = { - "host" => "127.0.0.1" - } - n.set["openstack"]["block-storage"]["syslog"]["use"] = true - end - @chef_run.converge "openstack-block-storage::cinder-common" - end - - it "installs the openstack-cinder package" do - expect(@chef_run).to upgrade_package "openstack-cinder" - end -end diff --git a/chef/cookbooks/openstack-block-storage/spec/cinder_common-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/cinder_common-redhat_spec.rb index ad6ce9d..fe364bd 100644 --- a/chef/cookbooks/openstack-block-storage/spec/cinder_common-redhat_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/cinder_common-redhat_spec.rb @@ -1,18 +1,19 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::cinder-common" do - before { block_storage_stubs } - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| - n.set["openstack"]["mq"] = { - "host" => "127.0.0.1" - } - n.set["openstack"]["block-storage"]["syslog"]["use"] = true +require_relative 'spec_helper' + +describe 'openstack-block-storage::cinder-common' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + + it 'upgrades the openstack-cinder package' do + expect(chef_run).to upgrade_package 'openstack-cinder' end - @chef_run.converge "openstack-block-storage::cinder-common" - end - - it "installs the openstack-cinder package" do - expect(@chef_run).to upgrade_package "openstack-cinder" end end diff --git a/chef/cookbooks/openstack-block-storage/spec/cinder_common-suse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/cinder_common-suse_spec.rb new file mode 100644 index 0000000..bfe7f86 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/cinder_common-suse_spec.rb @@ -0,0 +1,19 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage + +require_relative 'spec_helper' + +describe 'openstack-block-storage::cinder-common' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + + it 'upgrades the openstack-cinder package' do + expect(chef_run).to upgrade_package 'openstack-cinder' + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/cinder_common_spec.rb b/chef/cookbooks/openstack-block-storage/spec/cinder_common_spec.rb index 6e88b4f..28de9b7 100644 --- a/chef/cookbooks/openstack-block-storage/spec/cinder_common_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/cinder_common_spec.rb @@ -1,109 +1,578 @@ -require_relative "spec_helper" +# encoding: utf-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::cinder-common" do - before { block_storage_stubs } - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["mq"] = { - "host" => "127.0.0.1" - } - n.set["openstack"]["block-storage"]["syslog"]["use"] = true - end - @chef_run.converge "openstack-block-storage::cinder-common" - end +require_relative 'spec_helper' - it "installs the cinder-common package" do - expect(@chef_run).to upgrade_package "cinder-common" - end +describe 'openstack-block-storage::cinder-common' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['mq']['host'] = '127.0.0.1' + node.set['openstack']['mq']['block-storage']['rabbit']['notification_topic'] = 'rabbit_topic' - describe "/etc/cinder" do - before do - @dir = @chef_run.directory "/etc/cinder" + runner.converge(described_recipe) end - it "has proper owner" do - expect(@dir).to be_owned_by "cinder", "cinder" + include_context 'block-storage-stubs' + + it 'upgrades the cinder-common package' do + expect(chef_run).to upgrade_package 'cinder-common' end - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "750" - end - end + describe '/etc/cinder' do + let(:dir) { chef_run.directory('/etc/cinder') } - describe "cinder.conf" do - before do - @file = @chef_run.template "/etc/cinder/cinder.conf" + it 'has proper owner' do + expect(dir.owner).to eq('cinder') + expect(dir.group).to eq('cinder') + end + + it 'has proper modes' do + expect(sprintf('%o', dir.mode)).to eq '750' + end end - it "has proper owner" do - expect(@file).to be_owned_by "cinder", "cinder" - end + describe 'cinder.conf' do + let(:file) { chef_run.template('/etc/cinder/cinder.conf') } - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end + it 'has proper owner' do + expect(file.owner).to eq('cinder') + expect(file.group).to eq('cinder') + end - it "has rabbit_host" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_host=127.0.0.1" - end + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq '644' + end - it "does not have rabbit_hosts" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_hosts=" - end - - it "does not have rabbit_ha_queues" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_ha_queues=" - end - - it "has rabbit_port" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_port=5672" - end - - it "has rabbit_userid" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_userid=guest" - end - - it "has rabbit_password" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_password=rabbit-pass" - end - - it "has rabbit_virtual_host" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_virtual_host=/" - end - - describe "rabbit ha" do - before do - @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| - n.set["openstack"]["block-storage"]["rabbit"]["ha"] = true + context 'template contents' do + let(:test_pass) { 'test_pass' } + before do + Chef::Recipe.any_instance.stub(:endpoint) + .with('image-api') + .and_return(double(host: 'glance_host_value', port: 'glance_port_value')) + Chef::Recipe.any_instance.stub(:endpoint) + .with('block-storage-api-bind') + .and_return(double(host: 'cinder_host_value', port: 'cinder_port_value')) + Chef::Recipe.any_instance.stub(:get_password) + .with('user', anything) + .and_return(test_pass) end - @chef_run.converge "openstack-block-storage::cinder-common" + + context 'commonly named attributes' do + %w(debug verbose lock_path notification_driver + storage_availability_zone quota_volumes quota_gigabytes quota_driver + volume_name_template snapshot_name_template + control_exchange rpc_thread_pool_size rpc_conn_pool_size + rpc_response_timeout max_gigabytes).each do |attr_key| + it "has a #{attr_key} attribute" do + node.set['openstack']['block-storage'][attr_key] = "#{attr_key}_value" + + expect(chef_run).to render_file(file.name).with_content(/^#{attr_key}=#{attr_key}_value$/) + end + end + end + + context 'rdb driver' do + # FIXME(galstrom21): this block needs to check all of the default + # rdb_* configuration options + it 'has default rbd_* options set' do + node.set['openstack']['block-storage']['volume'] = { + 'driver' => 'cinder.volume.drivers.rbd.RBDDriver' + } + expect(chef_run).to render_file(file.name).with_content(/^rbd_/) + expect(chef_run).not_to render_file(file.name).with_content(/^netapp_/) + end + end + + context 'netapp driver' do + # FIXME(galstrom21): this block needs to check all of the default + # netapp_* configuration options + it 'has default netapp_* options set' do + node.set['openstack']['block-storage']['volume'] = { + 'driver' => 'cinder.volume.drivers.netapp.NetAppISCSIDriver' + } + expect(chef_run).to render_file(file.name).with_content(/^netapp_/) + expect(chef_run).not_to render_file(file.name).with_content(/^rbd_/) + end + end + + context 'syslog use' do + it 'sets the log_config value when syslog is in use' do + node.set['openstack']['block-storage']['syslog']['use'] = true + + expect(chef_run).to render_file(file.name) + .with_content(%r{^log_config = /etc/openstack/logging.conf$}) + end + + it 'sets the log_file value when syslog is not in use' do + node.set['openstack']['block-storage']['syslog']['use'] = false + + expect(chef_run).to render_file(file.name) + .with_content(%r{^log_file = /var/log/cinder/cinder.log$}) + end + end + + it 'has a sql_connection attribute' do + Chef::Recipe.any_instance.stub(:db_uri) + .with('block-storage', anything, '').and_return('sql_connection_value') + + expect(chef_run).to render_file(file.name) + .with_content(/^sql_connection=sql_connection_value$/) + end + + it 'has a volume_driver attribute' do + node.set['openstack']['block-storage']['volume']['driver'] = 'volume_driver_value' + expect(chef_run).to render_file(file.name).with_content(/^volume_driver=volume_driver_value$/) + end + + it 'has a state_path attribute' do + node.set['openstack']['block-storage']['volume']['state_path'] = 'state_path_value' + expect(chef_run).to render_file(file.name).with_content(/^state_path=state_path_value$/) + end + + context 'glance endpoint' do + %w(host port).each do |glance_attr| + it "has a glace #{glance_attr} attribute" do + expect(chef_run).to render_file(file.name).with_content(/^glance_#{glance_attr}=glance_#{glance_attr}_value$/) + end + end + end + + it 'has a api_rate_limit attribute' do + node.set['openstack']['block-storage']['api']['ratelimit'] = 'api_rate_limit_value' + expect(chef_run).to render_file(file.name).with_content(/^api_rate_limit=api_rate_limit_value$/) + end + + context 'cinder endpoint' do + it 'has osapi_volume_listen set' do + expect(chef_run).to render_file(file.name).with_content(/^osapi_volume_listen=cinder_host_value$/) + end + + it 'has osapi_volume_listen_port set' do + expect(chef_run).to render_file(file.name).with_content(/^osapi_volume_listen_port=cinder_port_value$/) + end + end + + it 'has a rpc_backend attribute' do + node.set['openstack']['block_storage']['rpc_backend'] = 'rpc_backend_value' + expect(chef_run).to render_file(file.name).with_content(/^rpc_backend=rpc_backend_value$/) + end + + context 'rabbitmq as mq service' do + before do + node.set['openstack']['mq']['block-storage']['service_type'] = 'rabbitmq' + end + + context 'ha attributes' do + before do + node.set['openstack']['mq']['block-storage']['rabbit']['ha'] = true + end + + it 'has a rabbit_hosts attribute' do + Chef::Recipe.any_instance.stub(:rabbit_servers) + .and_return('rabbit_servers_value') + + expect(chef_run).to render_file(file.name).with_content(/^rabbit_hosts=rabbit_servers_value$/) + end + + %w(host port).each do |attr| + it "does not have rabbit_#{attr} attribute" do + expect(chef_run).not_to render_file(file.name).with_content(/^rabbit_#{attr}=/) + end + end + end + + context 'non ha attributes' do + before do + node.set['openstack']['mq']['block-storage']['rabbit']['ha'] = false + end + + %w(host port).each do |attr| + it "has rabbit_#{attr} attribute" do + node.set['openstack']['mq']['block-storage']['rabbit'][attr] = "rabbit_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^rabbit_#{attr}=rabbit_#{attr}_value$/) + end + end + + it 'does not have a rabbit_hosts attribute' do + expect(chef_run).not_to render_file(file.name).with_content(/^rabbit_hosts=/) + end + end + + %w(use_ssl userid).each do |attr| + it "has rabbit_#{attr}" do + node.set['openstack']['mq']['block-storage']['rabbit'][attr] = "rabbit_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^rabbit_#{attr}=rabbit_#{attr}_value$/) + end + end + + it 'has rabbit_password' do + expect(chef_run).to render_file(file.name).with_content(/^rabbit_password=#{test_pass}$/) + end + + it 'has rabbit_virtual_host' do + node.set['openstack']['mq']['block-storage']['rabbit']['vhost'] = 'vhost_value' + expect(chef_run).to render_file(file.name).with_content(/^rabbit_virtual_host=vhost_value$/) + end + end + + context 'qpid as mq service' do + before do + node.set['openstack']['mq']['block-storage']['service_type'] = 'qpid' + end + + %w(port username sasl_mechanisms reconnect reconnect_timeout reconnect_limit + reconnect_interval_min reconnect_interval_max reconnect_interval heartbeat protocol + tcp_nodelay).each do |attr| + it "has qpid_#{attr} attribute" do + node.set['openstack']['mq']['block-storage']['qpid'][attr] = "qpid_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^qpid_#{attr}=qpid_#{attr}_value$/) + end + end + + it 'has qpid_hostname' do + node.set['openstack']['mq']['block-storage']['qpid']['host'] = 'qpid_host_value' + expect(chef_run).to render_file(file.name).with_content(/^qpid_hostname=qpid_host_value$/) + end + + it 'has qpid_password' do + expect(chef_run).to render_file(file.name).with_content(/^qpid_password=#{test_pass}$/) + end + + it 'has qpid notification_topics' do + node.set['openstack']['mq']['block-storage']['qpid']['notification_topic'] = 'qpid_notification_topic_value' + expect(chef_run).to render_file(file.name).with_content(/^notification_topics=qpid_notification_topic_value$/) + end + end + + context 'lvm settings' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.lvm.LVMISCSIDriver' + end + + %w(group clear clear_size).each do |attr| + it "has lvm volume_#{attr} attribute" do + node.set['openstack']['block-storage']['volume']["volume_#{attr}"] = "volume_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^volume_#{attr}=volume_#{attr}_value$/) + end + end + end + + context 'commonly named volume attributes' do + %w(iscsi_ip_address iscsi_port iscsi_helper volumes_dir).each do |attr| + it "has volume related #{attr} attribute" do + node.set['openstack']['block-storage']['volume'][attr] = "common_volume_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^#{attr}=common_volume_#{attr}_value$/) + end + end + end + + context 'rbd attributes' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.rbd.RBDDriver' + end + + %w(rbd_pool rbd_user rbd_secret_uuid).each do |attr| + it "has a #{attr} attribute" do + node.set['openstack']['block-storage'][attr] = "#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^#{attr}=#{attr}_value$/) + end + end + end + + it 'has volume_driver attribute' do + node.set['openstack']['block-storage']['volume']['driver'] = 'volume_driver_value' + expect(chef_run).to render_file(file.name).with_content(/^volume_driver=volume_driver_value$/) + end + + context 'netapp ISCSI settings' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.netapp.NetAppISCSIDriver' + end + + %w(login password).each do |attr| + it "has a netapp_#{attr} attribute" do + node.set['openstack']['block-storage']['netapp']["dfm_#{attr}"] = "dfm_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^netapp_#{attr}=dfm_#{attr}_value$/) + end + end + + %w(hostname port).each do |attr| + it "has a netapp_server_#{attr} attribute" do + node.set['openstack']['block-storage']['netapp']["dfm_#{attr}"] = "dfm_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^netapp_server_#{attr}=dfm_#{attr}_value$/) + end + end + + it 'has a netapp_storage_service attribute' do + node.set['openstack']['block-storage']['netapp']['storage_service'] = 'netapp_storage_service_value' + expect(chef_run).to render_file(file.name).with_content(/^netapp_storage_service=netapp_storage_service_value$/) + end + end + + context 'netapp direct7 mode nfs settings' do + let(:hostnames) { %w(hostname1 hostname2 hostname3) } + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver' + node.set['openstack']['block-storage']['netapp']['netapp_server_hostname'] = hostnames + end + + %w(mount_point_base shares_config).each do |attr_key| + it "has a nfs_#{attr_key} attribute" do + node.set['openstack']['block-storage']['nfs'][attr_key] = "netapp_nfs_#{attr_key}_value" + expect(chef_run).to render_file(file.name).with_content(/^nfs_#{attr_key}=netapp_nfs_#{attr_key}_value$/) + end + end + + it 'has netapp server_hostname attributes' do + hostnames.each do |hostname| + expect(chef_run).to render_file(file.name).with_content(/^netapp_server_hostname=#{hostname}$/) + end + end + + it 'has a netapp_server_port attribute' do + node.set['openstack']['block-storage']['netapp']['netapp_server_port'] = 'netapp_server_port_value' + expect(chef_run).to render_file(file.name).with_content(/^netapp_server_port=netapp_server_port_value$/) + end + + %w(login password).each do |attr| + it "has a netapp_#{attr} attribute" do + node.set['openstack']['block-storage']['netapp']["netapp_server_#{attr}"] = "netapp_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^netapp_#{attr}=netapp_#{attr}_value$/) + end + end + + %w(disk_util sparsed_volumes).each do |attr| + it "has a nfs_#{attr} attribute" do + node.set['openstack']['block-storage']['nfs']["nfs_#{attr}"] = "netapp_nfs_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^nfs_#{attr}=netapp_nfs_#{attr}_value$/) + end + end + end + + context 'ibmnas settings' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver' + end + + %w(mount_point_base shares_config).each do |attr| + it "has a ibmnas_#{attr} attribute" do + node.set['openstack']['block-storage']['ibmnas'][attr] = "ibmnas_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^nfs_#{attr}=ibmnas_#{attr}_value$/) + end + end + + it 'has a nfs_sparsed_volumes attribute' do + node.set['openstack']['block-storage']['ibmnas']['nfs_sparsed_volumes'] = 'ibmnas_nfs_sparsed_volumes_value' + expect(chef_run).to render_file(file.name).with_content(/^nfs_sparsed_volumes=ibmnas_nfs_sparsed_volumes_value$/) + end + + %w(nas_ip nas_login nas_ssh_port).each do |attr| + it "has a ibmnas #{attr} attribute" do + node.set['openstack']['block-storage']['ibmnas'][attr] = "ibmnas_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^#{attr}=ibmnas_#{attr}_value$/) + end + end + + it 'has a nas_password attribute' do + expect(chef_run).to render_file(file.name).with_content(/^nas_password=#{test_pass}$/) + end + end + + context 'storwize settings' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver' + end + + it 'has a default attribute' do + %w(san_ip=127.0.0.1 + san_login=admin + san_private_key=/v7000_rsa + storwize_svc_volpool_name=volpool + storwize_svc_vol_rsize=2 + storwize_svc_vol_warning=0 + storwize_svc_vol_autoexpand=true + storwize_svc_vol_grainsize=256 + storwize_svc_vol_compression=false + storwize_svc_vol_easytier=true + storwize_svc_vol_iogrp=0 + storwize_svc_flashcopy_timeout=120 + storwize_svc_connection_protocol=iSCSI + storwize_svc_iscsi_chap_enabled=true + storwize_svc_multihostmap_enabled=true).each do |attr| + expect(chef_run).to render_file(file.name).with_content(/^#{attr}$/) + end + end + + it 'has a overridden attribute' do + %w(san_ip + san_login + san_private_key + storwize_svc_volpool_name + storwize_svc_vol_rsize + storwize_svc_vol_warning + storwize_svc_vol_autoexpand + storwize_svc_vol_grainsize + storwize_svc_vol_compression + storwize_svc_vol_easytier + storwize_svc_vol_iogrp + storwize_svc_flashcopy_timeout + storwize_svc_connection_protocol + storwize_svc_multihostmap_enabled).each do |attr| + node.set['openstack']['block-storage']['storwize'][attr] = "storwize_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^#{attr}=storwize_#{attr}_value$/) + end + end + + context 'storwize with iSCSI connection protocol' do + before do + node.set['openstack']['block-storage']['storwize']['storwize_svc_connection_protocol'] = 'iSCSI' + end + + it 'has a iscsi chap enabled attribute' do + node.set['openstack']['block-storage']['storwize']['storwize_svc_iscsi_chap_enabled'] = 'storwize_svc_iscsi_chap_enabled_value' + expect(chef_run).to render_file(file.name).with_content(/^storwize_svc_iscsi_chap_enabled=storwize_svc_iscsi_chap_enabled_value$/) + end + + it 'does not have a multipath enabled attribute' do + expect(chef_run).not_to render_file(file.name).with_content(/^storwize_svc_multipath_enabled=/) + end + end + + context 'storwize without iSCSI connection protocol' do + before do + node.set['openstack']['block-storage']['storwize']['storwize_svc_connection_protocol'] = 'non-iSCSI' + end + + it 'does not have a iscsi chap enabled attribute' do + expect(chef_run).not_to render_file(file.name).with_content(/^storwize_svc_iscsi_enabled=/) + end + + it 'has a multipath enabled attribute' do + node.set['openstack']['block-storage']['storwize']['storwize_svc_multipath_enabled'] = 'storwize_svc_multipath_enabled_value' + expect(chef_run).to render_file(file.name).with_content(/^storwize_svc_multipath_enabled=storwize_svc_multipath_enabled_value$/) + end + end + end + + context 'solidfire settings' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.solidfire.SolidFire' + end + + it 'has solidfire sf_emulate set' do + node.set['openstack']['block-storage']['solidfire']['sf_emulate'] = 'test' + expect(chef_run).to render_file(file.name).with_content(/^sf_emulate_512=test$/) + end + + %w(san_login san_ip).each do |attr| + it "has solidfire #{attr} set" do + node.set['openstack']['block-storage']['solidfire'][attr] = "solidfire_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^#{attr}=solidfire_#{attr}_value$/) + end + end + + it 'does not have iscsi_ip_prefix not specified' do + node.set['openstack']['block-storage']['solidfire']['iscsi_ip_prefix'] = nil + expect(chef_run).to_not render_file(file.name).with_content(/^iscsi_ip_prefix=/) + end + + it 'does have iscsi_ip_prefix when specified' do + chef_run.node.set['openstack']['block-storage']['solidfire']['iscsi_ip_prefix'] = '203.0.113.*' + expect(chef_run).to render_file(file.name).with_content(/^iscsi_ip_prefix=203.0.113.*$/) + end + end + + context 'emc settings' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.emc.emc_smis_iscsi.EMCSMISISCSIDriver' + end + + %w(iscsi_target_prefix cinder_emc_config_file).each do |attr| + it "has emc #{attr} set" do + node.set['openstack']['block-storage']['emc'][attr] = "emc_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^#{attr}=emc_#{attr}_value$/) + end + end + end + + context 'vmware vmdk settings' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver' + %w(vmware_host_ip vmware_host_username + vmware_api_retry_count vmware_task_poll_interval vmware_volume_folder + vmware_image_transfer_timeout_secs vmware_max_objects_retrieval).each do |attr| + node.set['openstack']['block-storage']['vmware'][attr] = "vmware_#{attr}_value" + end + end + + it 'has vmware attributes set' do + node['openstack']['block-storage']['vmware'].each do |attr, val| + expect(chef_run).to render_file(file.name).with_content(/^#{attr} = #{val}$/) + end + end + + it 'has password set which is from databag' do + expect(chef_run).to render_file(file.name).with_content(/^vmware_host_password = vmware_secret_name$/) + end + + it 'has no wsdl_location line without the attribute' do + node.set['openstack']['block-storage']['vmware']['vmware_wsdl_location'] = nil + expect(chef_run).not_to render_file(file.name).with_content(/^vmware_wsdl_location = /) + end + + it 'has wsdl_location line with attribute present' do + node.set['openstack']['block-storage']['vmware']['vmware_wsdl_location'] = 'http://127.0.0.1/wsdl' + expect(chef_run).to render_file(file.name).with_content(%r(^vmware_wsdl_location = http://127.0.0.1/wsdl$)) + end + end + + context 'gpfs settings' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.gpfs.GPFSDriver' + end + + %w(gpfs_mount_point_base gpfs_images_share_mode gpfs_max_clone_depth + gpfs_sparse_volumes gpfs_storage_pool).each do |attr| + it "has gpfs #{attr} set" do + node.set['openstack']['block-storage']['gpfs'][attr] = "gpfs_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^#{attr} = gpfs_#{attr}_value$/) + end + end + + it 'has no gpfs_images_dir line without the attribute' do + node.set['openstack']['block-storage']['gpfs']['gpfs_images_dir'] = nil + expect(chef_run).not_to render_file(file.name).with_content(/^gpfs_images_dir = /) + end + + it 'has gpfs_images_dir line with attribute present' do + node.set['openstack']['block-storage']['gpfs']['gpfs_images_dir'] = 'gpfs_images_dir_value' + expect(chef_run).to render_file(file.name).with_content(/^gpfs_images_dir = gpfs_images_dir_value$/) + end + + it 'templates misc_cinder array correctly' do + node.set['openstack']['block-storage']['misc_cinder'] = ['# Comments', 'MISC=OPTION'] + expect(chef_run).to render_file(file.name).with_content( + /^# Comments$/) + expect(chef_run).to render_file(file.name).with_content( + /^MISC=OPTION$/) + end + end + end + end + + describe '/var/lock/cinder' do + let(:dir) { chef_run.directory('/var/lock/cinder') } + + it 'has proper owner' do + expect(dir.owner).to eq('cinder') + expect(dir.group).to eq('cinder') end - it "has rabbit_hosts" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_hosts=1.1.1.1:5672,2.2.2.2:5672" - end - - it "has rabbit_ha_queues" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_ha_queues=True" - end - - it "does not have rabbit_host" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_host=127.0.0.1" - end - - it "does not have rabbit_port" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_port=5672" + it 'has proper modes' do + expect(sprintf('%o', dir.mode)).to eq '700' end end end diff --git a/chef/cookbooks/openstack-block-storage/spec/client-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/client-redhat_spec.rb new file mode 100644 index 0000000..166ea62 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/client-redhat_spec.rb @@ -0,0 +1,16 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-block-storage::client' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades packages' do + expect(chef_run).to upgrade_package('python-cinderclient') + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/client_spec.rb b/chef/cookbooks/openstack-block-storage/spec/client_spec.rb new file mode 100644 index 0000000..b58ea36 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/client_spec.rb @@ -0,0 +1,16 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-block-storage::client' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades packages' do + expect(chef_run).to upgrade_package('python-cinderclient') + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/default_spec.rb b/chef/cookbooks/openstack-block-storage/spec/default_spec.rb index 3a77cb4..b97cfb7 100644 --- a/chef/cookbooks/openstack-block-storage/spec/default_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/default_spec.rb @@ -1,4 +1,8 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::default" do +require_relative 'spec_helper' + +describe 'openstack-block-storage::default' do end diff --git a/chef/cookbooks/openstack-block-storage/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-block-storage/spec/identity_registration_spec.rb index e6ed35c..0e0884d 100644 --- a/chef/cookbooks/openstack-block-storage/spec/identity_registration_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/identity_registration_spec.rb @@ -1,82 +1,92 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::identity_registration" do - before do - block_storage_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-block-storage::identity_registration" - end +require_relative 'spec_helper' - it "registers cinder volume service" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Cinder Volume Service" - ).to_hash +describe 'openstack-block-storage::identity_registration' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } - expect(resource).to include( - :auth_uri => "https://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_name => "cinder", - :service_type => "volume", - :service_description => "Cinder Volume Service", - :endpoint_region => "RegionOne", - :endpoint_adminurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", - :endpoint_internalurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", - :endpoint_publicurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", - :action => [:create_service] - ) - end + include_context 'block-storage-stubs' - it "registers cinder volume endpoint" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Cinder Volume Endpoint" - ).to_hash + it 'registers service tenant' do + expect(chef_run).to create_tenant_openstack_identity_register( + 'Register Service Tenant' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + tenant_description: 'Service Tenant' + ) + end - expect(resource).to include( - :auth_uri => "https://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_name => "cinder", - :service_type => "volume", - :service_description => "Cinder Volume Service", - :endpoint_region => "RegionOne", - :endpoint_adminurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", - :endpoint_internalurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", - :endpoint_publicurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", - :action => [:create_endpoint] - ) - end + it 'registers cinder volume service' do + expect(chef_run).to create_service_openstack_identity_register( + 'Register Cinder Volume Service' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_name: 'cinder', + service_type: 'volume', + service_description: 'Cinder Volume Service', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:8776/v1/%(tenant_id)s', + endpoint_internalurl: 'http://127.0.0.1:8776/v1/%(tenant_id)s', + endpoint_publicurl: 'http://127.0.0.1:8776/v1/%(tenant_id)s' + ) + end - it "registers service user" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Cinder Service User" - ).to_hash + context 'registers volume endpoint' do + it 'with default values' do + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Cinder Volume Endpoint' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_name: 'cinder', + service_type: 'volume', + service_description: 'Cinder Volume Service', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:8776/v1/%(tenant_id)s', + endpoint_internalurl: 'http://127.0.0.1:8776/v1/%(tenant_id)s', + endpoint_publicurl: 'http://127.0.0.1:8776/v1/%(tenant_id)s' + ) + end - expect(resource).to include( - :auth_uri => "https://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :user_name => "cinder", - :user_pass => "cinder-pass", - :user_enabled => true, - :action => [:create_user] - ) - end + it 'with custom region override' do + node.set['openstack']['block-storage']['region'] = 'volumeRegion' + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Cinder Volume Endpoint' + ).with(endpoint_region: 'volumeRegion') + end + end - it "grants admin role to service user for service tenant" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Grant service Role to Cinder Service User for Cinder Service Tenant" - ).to_hash + it 'registers service user' do + expect(chef_run).to create_user_openstack_identity_register( + 'Register Cinder Service User' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'cinder', + user_pass: 'cinder-pass', + user_enabled: true + ) + end - expect(resource).to include( - :auth_uri => "https://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :user_name => "cinder", - :role_name => "admin", - :action => [:grant_role] - ) + it 'grants admin role to service user for service tenant' do + expect(chef_run).to grant_role_openstack_identity_register( + 'Grant service Role to Cinder Service User for Cinder Service Tenant' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'cinder', + role_name: 'admin' + ) + end end end diff --git a/chef/cookbooks/openstack-block-storage/spec/scheduler-opensuse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/scheduler-opensuse_spec.rb deleted file mode 100644 index 42de173..0000000 --- a/chef/cookbooks/openstack-block-storage/spec/scheduler-opensuse_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-block-storage::scheduler" do - before { block_storage_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-block-storage::scheduler" - end - - it "installs cinder api packages" do - expect(@chef_run).to upgrade_package "openstack-cinder-scheduler" - end - - it "does not upgrade stevedore" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - chef_run.converge "openstack-block-storage::scheduler" - - expect(chef_run).not_to upgrade_python_pip "stevedore" - end - - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "python-mysql" - end - - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::scheduler" - - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "python-mysql" - end - - it "starts cinder scheduler" do - expect(@chef_run).to start_service "openstack-cinder-scheduler" - end - - it "starts cinder scheduler on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-cinder-scheduler" - end - end -end diff --git a/chef/cookbooks/openstack-block-storage/spec/scheduler-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/scheduler-redhat_spec.rb index e72d9f7..55322e0 100644 --- a/chef/cookbooks/openstack-block-storage/spec/scheduler-redhat_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/scheduler-redhat_spec.rb @@ -1,44 +1,50 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::scheduler" do - before { block_storage_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-block-storage::scheduler" +require_relative 'spec_helper' + +describe 'openstack-block-storage::scheduler' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + + it 'upgrades cinder scheduler package' do + expect(chef_run).to upgrade_package 'openstack-cinder' end - it "installs cinder api packages" do - expect(@chef_run).to upgrade_package "openstack-cinder" + it 'starts cinder scheduler' do + expect(chef_run).to start_service 'openstack-cinder-scheduler' end - it "does not upgrade stevedore" do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - chef_run.converge "openstack-block-storage::scheduler" - - expect(chef_run).not_to upgrade_python_pip "stevedore" + it 'starts cinder scheduler on boot' do + expect(chef_run).to enable_service 'openstack-cinder-scheduler' end - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "MySQL-python" + it 'does not upgrade stevedore' do + expect(chef_run).not_to upgrade_python_pip 'stevedore' end - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::scheduler" - - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "MySQL-python" + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package 'MySQL-python' end - it "starts cinder scheduler" do - expect(@chef_run).to start_service "openstack-cinder-scheduler" + it 'upgrades db2 python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'db2' + + ['python-ibm-db', 'python-ibm-db-sa'].each do |pkg| + expect(chef_run).to upgrade_package pkg + end end - it "starts cinder scheduler on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-cinder-scheduler" + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' + + expect(chef_run).to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'MySQL-python' end end end diff --git a/chef/cookbooks/openstack-block-storage/spec/scheduler-suse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/scheduler-suse_spec.rb new file mode 100644 index 0000000..c53a04d --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/scheduler-suse_spec.rb @@ -0,0 +1,42 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage + +require_relative 'spec_helper' + +describe 'openstack-block-storage::scheduler' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + + it 'upgrades cinder scheduler package' do + expect(chef_run).to upgrade_package 'openstack-cinder-scheduler' + end + + it 'starts cinder scheduler' do + expect(chef_run).to start_service 'openstack-cinder-scheduler' + end + + it 'starts cinder scheduler on boot' do + expect(chef_run).to enable_service 'openstack-cinder-scheduler' + end + + it 'does not upgrade stevedore' do + expect(chef_run).not_to upgrade_python_pip 'stevedore' + end + + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package 'python-mysql' + end + + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' + + expect(chef_run).to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'python-mysql' + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/scheduler_spec.rb b/chef/cookbooks/openstack-block-storage/spec/scheduler_spec.rb index bb4ceea..4f6ab2c 100644 --- a/chef/cookbooks/openstack-block-storage/spec/scheduler_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/scheduler_spec.rb @@ -1,62 +1,81 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::scheduler" do - before { block_storage_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["syslog"]["use"] = true +require_relative 'spec_helper' + +describe 'openstack-block-storage::scheduler' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + include_examples 'common-logging' + + expect_creates_cinder_conf 'service[cinder-scheduler]', 'cinder', 'cinder' + + it 'upgrades cinder scheduler package' do + expect(chef_run).to upgrade_package 'cinder-scheduler' + end + + it 'starts cinder scheduler' do + expect(chef_run).to start_service 'cinder-scheduler' + end + + it 'starts cinder scheduler on boot' do + expect(chef_run).to enable_service 'cinder-scheduler' + end + + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package 'python-mysqldb' + end + + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' + + expect(chef_run).to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'python-mysqldb' + end + + it 'does not setup cron when no metering' do + expect(chef_run.cron('cinder-volume-usage-audit')).to be_nil + end + + it 'creates cron metering default' do + ::Chef::Recipe.any_instance.stub(:search) + .with(:node, 'roles:os-block-storage-scheduler') + .and_return([OpenStruct.new(name: 'fauxhai.local')]) + node.set['openstack']['telemetry'] = true + + cron = chef_run.cron 'cinder-volume-usage-audit' + bin_str = '/usr/bin/cinder-volume-usage-audit > /var/log/cinder/audit.log' + expect(cron.command).to match(/#{bin_str}/) + crontests = [[:minute, '00'], [:hour, '*'], [:day, '*'], + [:weekday, '*'], [:month, '*'], [:user, 'cinder']] + crontests.each do |k, v| + expect(cron.send(k)).to eq v end - @chef_run.converge "openstack-block-storage::scheduler" + expect(cron.action).to include :create end - expect_runs_openstack_common_logging_recipe + it 'creates cron metering custom' do + crontests = [[:minute, '50'], [:hour, '23'], [:day, '6'], + [:weekday, '5'], [:month, '11'], [:user, 'foobar']] + ::Chef::Recipe.any_instance.stub(:search) + .with(:node, 'roles:os-block-storage-scheduler') + .and_return([OpenStruct.new(name: 'foobar')]) + node.set['openstack']['telemetry'] = true + crontests.each do |k, v| + node.set['openstack']['block-storage']['cron'][k.to_s] = v + end + node.set['openstack']['block-storage']['user'] = 'foobar' - it "doesn't run logging recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-block-storage::scheduler" - - expect(chef_run).not_to include_recipe "openstack-common::logging" + cron = chef_run.cron 'cinder-volume-usage-audit' + crontests.each do |k, v| + expect(cron.send(k)).to eq v + end + expect(cron.action).to include :delete end - - it "installs cinder api packages" do - expect(@chef_run).to upgrade_package "cinder-scheduler" - end - - it "upgrades stevedore" do - expect(@chef_run).to upgrade_python_pip "stevedore" - end - - it "does not upgrade stevedore" do - opts = ::UBUNTU_OPTS.merge(:version => "10.04") - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.converge "openstack-block-storage::scheduler" - - expect(chef_run).not_to upgrade_python_pip "stevedore" - end - - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "python-mysqldb" - end - - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::scheduler" - - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "python-mysqldb" - end - - it "starts cinder scheduler" do - expect(@chef_run).to start_service "cinder-scheduler" - end - - it "starts cinder scheduler on boot" do - expect(@chef_run).to set_service_to_start_on_boot "cinder-scheduler" - end - - expect_creates_cinder_conf "service[cinder-scheduler]", "cinder", "cinder" end end diff --git a/chef/cookbooks/openstack-block-storage/spec/spec_helper.rb b/chef/cookbooks/openstack-block-storage/spec/spec_helper.rb index 8b5024d..c28f60e 100644 --- a/chef/cookbooks/openstack-block-storage/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-block-storage/spec/spec_helper.rb @@ -1,81 +1,118 @@ -require "chefspec" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -::LOG_LEVEL = :fatal -::OPENSUSE_OPTS = { - :platform => "opensuse", - :version => "12.3", - :log_level => ::LOG_LEVEL +require 'chefspec' +require 'chefspec/berkshelf' + +ChefSpec::Coverage.start! { add_filter 'openstack-block-storage' } + +require 'chef/application' + +LOG_LEVEL = :fatal +SUSE_OPTS = { + platform: 'suse', + version: '11.03', + log_level: LOG_LEVEL } -::REDHAT_OPTS = { - :platform => "redhat", - :version => "6.3", - :log_level => ::LOG_LEVEL +REDHAT_OPTS = { + platform: 'redhat', + version: '6.3', + log_level: LOG_LEVEL } -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } -def block_storage_stubs - ::Chef::Recipe.any_instance.stub(:rabbit_servers). - and_return "1.1.1.1:5672,2.2.2.2:5672" - ::Chef::Recipe.any_instance.stub(:secret). - with("secrets", "#{node['openstack']['identity']['admin_token']}"). - and_return "bootstrap-token" - ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:user_password). - with("guest"). - and_return "rabbit-pass" - ::Chef::Recipe.any_instance.stub(:service_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:service_password). - with("openstack-block-storage"). - and_return "cinder-pass" +shared_context 'block-storage-stubs' do + before do + Chef::Recipe.any_instance.stub(:rabbit_servers) + .and_return('1.1.1.1:5672,2.2.2.2:5672') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', anything) + .and_return('') + Chef::Recipe.any_instance.stub(:get_password) + .with('db', anything) + .and_return('') + Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_identity_bootstrap_token') + .and_return('bootstrap-token') + Chef::Recipe.any_instance.stub(:get_secret) + .with('rbd_secret_uuid') + .and_return('b0ff3bba-e07b-49b1-beed-09a45552b1ad') + Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_vmware_secret_name') + .and_return 'vmware_secret_name' + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'guest') + .and_return('mq-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'solidfire_admin') + .and_return('solidfire_testpass') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'netapp') + .and_return 'netapp-pass' + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack-block-storage') + .and_return('cinder-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack_image_cephx_key') + .and_return('cephx-key') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'admin') + .and_return('emc_test_pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'ibmnas_admin') + .and_return('test_pass') + Chef::Application.stub(:fatal!) + end +end + +shared_examples 'common-logging' do + context 'when syslog.use is true' do + before do + node.set['openstack']['block-storage']['syslog']['use'] = true + end + + it 'runs logging recipe if node attributes say to' do + expect(chef_run).to include_recipe 'openstack-common::logging' + end + end + + context 'when syslog.use is false' do + before do + node.set['openstack']['block-storage']['syslog']['use'] = false + end + + it 'runs logging recipe if node attributes say to' do + expect(chef_run).to_not include_recipe 'openstack-common::logging' + end + end end def expect_runs_openstack_common_logging_recipe - it "runs logging recipe if node attributes say to" do - expect(@chef_run).to include_recipe "openstack-common::logging" + it 'runs logging recipe if node attributes say to' do + expect(chef_run).to include_recipe 'openstack-common::logging' end end -def expect_creates_cinder_conf service, user, group, action=:restart - describe "cinder.conf" do - before do - @file = @chef_run.template "/etc/cinder/cinder.conf" +def expect_creates_cinder_conf(service, user, group, action = :restart) # rubocop:disable MethodLength + describe 'cinder.conf' do + let(:file) { chef_run.template('/etc/cinder/cinder.conf') } + + it 'has proper owner' do + expect(file.owner).to eq(user) + expect(file.group).to eq(group) end - it "has proper owner" do - expect(@file).to be_owned_by user, group + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq '644' end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "notifies service restart" do - expect(@file).to notify service, action - end - end -end - -def expect_creates_policy_json service, user, group, action=:restart - describe "policy.json" do - before do - @file = @chef_run.template "/etc/cinder/policy.json" - end - - it "has proper owner" do - expect(@file).to be_owned_by user, group - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "notifies service restart" do - expect(@file).to notify service, action + it 'notifies service restart' do + expect(file).to notify(service).to(action) end end end diff --git a/chef/cookbooks/openstack-block-storage/spec/volume-opensuse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/volume-opensuse_spec.rb deleted file mode 100644 index e5b7537..0000000 --- a/chef/cookbooks/openstack-block-storage/spec/volume-opensuse_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-block-storage::volume" do - before { block_storage_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-block-storage::volume" - end - - it "installs cinder volume packages" do - expect(@chef_run).to upgrade_package "openstack-cinder-volume" - end - - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "python-mysql" - end - - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::volume" - - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "python-mysql" - end - - it "installs cinder iscsi packages" do - expect(@chef_run).to upgrade_package "tgt" - end - - it "starts cinder volume" do - expect(@chef_run).to start_service "openstack-cinder-volume" - end - - it "starts cinder volume on boot" do - expected = "openstack-cinder-volume" - expect(@chef_run).to set_service_to_start_on_boot expected - end - - it "starts iscsi target on boot" do - expect(@chef_run).to set_service_to_start_on_boot "tgtd" - end - - it "installs nfs packages" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| - n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" - end - chef_run.converge "openstack-block-storage::volume" - - expect(chef_run).to upgrade_package "nfs-utils" - expect(chef_run).not_to upgrade_package "nfs-utils-lib" - end - - it "has opensuse include" do - file = "/etc/tgt/targets.conf" - - expect(@chef_run).to create_file_with_content file, - "include /var/lib/cinder/volumes/*" - expect(@chef_run).not_to create_file_with_content file, - "include /etc/tgt/conf.d/*.conf" - end - end -end diff --git a/chef/cookbooks/openstack-block-storage/spec/volume-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/volume-redhat_spec.rb index 0638f82..7d3ce4a 100644 --- a/chef/cookbooks/openstack-block-storage/spec/volume-redhat_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/volume-redhat_spec.rb @@ -1,61 +1,186 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::volume" do - before { block_storage_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-block-storage::volume" +require_relative 'spec_helper' + +describe 'openstack-block-storage::volume' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package('MySQL-python') end - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "MySQL-python" - end + it 'upgrades db2 python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'db2' - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::volume" - - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "MySQL-python" - end - - it "installs cinder iscsi packages" do - expect(@chef_run).to upgrade_package "scsi-target-utils" - end - - it "starts cinder volume" do - expect(@chef_run).to start_service "openstack-cinder-volume" - end - - it "starts cinder volume on boot" do - expected = "openstack-cinder-volume" - expect(@chef_run).to set_service_to_start_on_boot expected - end - - it "starts iscsi target on boot" do - expect(@chef_run).to set_service_to_start_on_boot "tgtd" - end - - it "installs nfs packages" do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| - n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" + ['python-ibm-db', 'python-ibm-db-sa'].each do |pkg| + expect(chef_run).to upgrade_package(pkg) end - chef_run.converge "openstack-block-storage::volume" - - expect(chef_run).to upgrade_package "nfs-utils" - expect(chef_run).to upgrade_package "nfs-utils-lib" end - it "has redhat include" do - file = "/etc/tgt/targets.conf" + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' - expect(@chef_run).to create_file_with_content file, - "include /var/lib/cinder/volumes/*" - expect(@chef_run).not_to create_file_with_content file, - "include /etc/tgt/conf.d/*.conf" + expect(chef_run).to upgrade_package('python-psycopg2') + expect(chef_run).not_to upgrade_package('MySQL-python') end + + it 'upgrades cinder iscsi package' do + expect(chef_run).to upgrade_package('scsi-target-utils') + end + + it 'starts cinder volume' do + expect(chef_run).to start_service('openstack-cinder-volume') + end + + it 'starts cinder volume on boot' do + expect(chef_run).to enable_service('openstack-cinder-volume') + end + + context 'ISCSI' do + let(:file) { chef_run.template('/etc/tgt/targets.conf') } + it 'starts iscsi target on boot' do + expect(chef_run).to enable_service('tgtd') + end + + it 'has redhat include' do + node.set['openstack']['block-storage']['volume']['volumes_dir'] = 'volumes_dir_value' + expect(chef_run).to render_file(file.name).with_content( + 'include volumes_dir_value/*') + expect(chef_run).not_to render_file(file.name).with_content( + 'include /etc/tgt/conf.d/*.conf') + end + end + + context 'IBMNAS Driver' do + let(:file) { chef_run.template('/etc/cinder/nfs_shares.conf') } + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver' + node.set['openstack']['block-storage']['ibmnas']['nas_access_ip'] = '127.0.0.1' + node.set['openstack']['block-storage']['ibmnas']['export'] = '/ibm/fs/export' + end + + it 'creates IBMNAS shares_config file' do + expect(chef_run).to create_template(file.name).with( + owner: 'cinder', + group: 'cinder', + mode: '0600' + ) + expect(chef_run).to render_file(file.name).with_content('127.0.0.1:/ibm/fs/export') + end + + it 'upgrades nfs packages' do + expect(chef_run).to upgrade_package 'nfs-utils' + expect(chef_run).to upgrade_package 'nfs-utils-lib' + end + + it 'creates the nfs mount point' do + expect(chef_run).to create_directory('/mnt/cinder-volumes').with( + owner: 'cinder', + group: 'cinder', + mode: '0755' + ) + end + end + + context 'NFS Driver' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver' + end + + it 'upgrades nfs packages' do + expect(chef_run).to upgrade_package('nfs-utils') + expect(chef_run).to upgrade_package('nfs-utils-lib') + end + end + + context 'EMC ISCSI Driver' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.emc.emc_smis_iscsi.EMCSMISISCSIDriver' + end + + it 'upgrades emc package' do + expect(chef_run).to upgrade_package('pywbem') + end + end + + describe 'IBM GPFS volume driver' do + before do + @chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS do |n| + n.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.gpfs.GPFSDriver' + n.set['openstack']['block-storage']['gpfs']['gpfs_mount_point_base'] = 'volumes' + end + + @conf = '/etc/cinder/cinder.conf' + @chef_run.converge 'openstack-block-storage::volume' + end + + it 'verifies gpfs_mount_point_base' do + expect(@chef_run).to render_file(@conf).with_content( + /^gpfs_mount_point_base = volumes$/) + end + + it 'verifies gpfs_images_dir' do + @chef_run.node.set['openstack']['block-storage']['gpfs']['gpfs_images_dir'] = 'images' + expect(@chef_run).to render_file(@conf).with_content( + /^gpfs_images_dir = images$/) + end + + it 'verifies gpfs_images_share_mode is default' do + expect(@chef_run).to render_file(@conf).with_content( + /^gpfs_images_share_mode = copy_on_write$/) + end + + it 'verifies gpfs_sparse_volumes is default' do + expect(@chef_run).to render_file(@conf).with_content( + /^gpfs_sparse_volumes = true$/) + end + + it 'verifies gpfs_max_clone_depth is default' do + expect(@chef_run).to render_file(@conf).with_content( + /^gpfs_max_clone_depth = 8$/) + end + + it 'verifies gpfs_storage_pool is default' do + expect(@chef_run).to render_file(@conf).with_content( + /^gpfs_storage_pool = system$/) + end + + it 'verifies gpfs volume directory is created with owner and mode set correctly' do + expect(@chef_run).to create_directory('volumes').with( + owner: 'cinder', + group: 'cinder', + mode: '0755' + ) + end + end + + describe 'create_vg' do + let(:file) { chef_run.template('/etc/init.d/cinder-group-active') } + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.lvm.LVMISCSIDriver' + node.set['openstack']['block-storage']['volume']['create_volume_group'] = true + stub_command('vgs cinder-volumes').and_return(false) + end + + describe 'template contents' do + it 'sources /etc/rc.d/init.d/functions' do + expect(chef_run).to render_file(file.name).with_content(%r(^\s*. /etc/rc.d/init.d/functions$)) + end + + it 'calls success and echo' do + [/^\s*success$/, /^\s*echo$/].each do |cmd| + expect(chef_run).to render_file(file.name).with_content(cmd) + end + end + end + end + end end diff --git a/chef/cookbooks/openstack-block-storage/spec/volume-suse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/volume-suse_spec.rb new file mode 100644 index 0000000..88c0db3 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/volume-suse_spec.rb @@ -0,0 +1,75 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage + +require_relative 'spec_helper' + +describe 'openstack-block-storage::volume' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + + it 'upgrades cinder volume package' do + expect(chef_run).to upgrade_package('openstack-cinder-volume') + end + + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package('python-mysql') + end + + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' + + expect(chef_run).to upgrade_package('python-psycopg2') + expect(chef_run).not_to upgrade_package('python-mysql') + end + + it 'upgrades cinder iscsi package' do + expect(chef_run).to upgrade_package('tgt') + end + + it 'starts cinder volume' do + expect(chef_run).to start_service('openstack-cinder-volume') + end + + it 'starts cinder volume on boot' do + expect(chef_run).to enable_service('openstack-cinder-volume') + end + + context 'ISCSI' do + let(:file) { chef_run.template('/etc/tgt/targets.conf') } + it 'starts iscsi target on boot' do + expect(chef_run).to enable_service('tgtd') + end + + it 'has suse include' do + expect(chef_run).to render_file(file.name).with_content('include /var/lib/cinder/volumes/*') + expect(chef_run).not_to render_file(file.name).with_content('include /etc/tgt/conf.d/*.conf') + end + end + + context 'NFS Driver' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver' + end + + it 'installs nfs packages' do + expect(chef_run).to upgrade_package('nfs-utils') + expect(chef_run).not_to upgrade_package('nfs-utils-lib') + end + end + + context 'EMC ISCSI Driver' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.emc.emc_smis_iscsi.EMCSMISISCSIDriver' + end + + it 'installs emc packages' do + expect(chef_run).to upgrade_package('python-pywbem') + end + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/volume_spec.rb b/chef/cookbooks/openstack-block-storage/spec/volume_spec.rb index 7360791..ee7590a 100644 --- a/chef/cookbooks/openstack-block-storage/spec/volume_spec.rb +++ b/chef/cookbooks/openstack-block-storage/spec/volume_spec.rb @@ -1,120 +1,355 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# +# Cookbook Name:: openstack-block-storage -describe "openstack-block-storage::volume" do - before { block_storage_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["syslog"]["use"] = true - end - @chef_run.converge "openstack-block-storage::volume" +require_relative 'spec_helper' + +describe 'openstack-block-storage::volume' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'block-storage-stubs' + include_examples 'common-logging' + + expect_creates_cinder_conf('service[cinder-volume]', 'cinder', 'cinder') + + it 'upgrades cinder volume packages' do + expect(chef_run).to upgrade_package 'cinder-volume' end - expect_runs_openstack_common_logging_recipe - - it "doesn't run logging recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-block-storage::volume" - - expect(chef_run).not_to include_recipe "openstack-common::logging" + it 'starts cinder volume' do + expect(chef_run).to start_service 'cinder-volume' end - it "installs cinder volume packages" do - expect(@chef_run).to upgrade_package "cinder-volume" + it 'starts cinder volume on boot' do + expect(chef_run).to enable_service 'cinder-volume' end - it "installs mysql python packages by default" do - expect(@chef_run).to upgrade_package "python-mysqldb" + it 'starts iscsi target on boot' do + expect(chef_run).to enable_service 'tgt' end - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" - chef_run.converge "openstack-block-storage::volume" - - expect(chef_run).to upgrade_package "python-psycopg2" - expect(chef_run).not_to upgrade_package "python-mysqldb" + it 'upgrades mysql python packages by default' do + expect(chef_run).to upgrade_package 'python-mysqldb' end - it "installs cinder iscsi packages" do - expect(@chef_run).to upgrade_package "tgt" + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['block-storage']['service_type'] = 'postgresql' + + expect(chef_run).to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'python-mysqldb' end - it "installs nfs packages" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" - end - chef_run.converge "openstack-block-storage::volume" - - expect(chef_run).to upgrade_package "nfs-common" + it 'upgrades cinder iscsi package' do + expect(chef_run).to upgrade_package 'tgt' end - it "creates the nfs mount point" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" - end - chef_run.converge "openstack-block-storage::volume" + it 'upgrades emc package' do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.emc.emc_smis_iscsi.EMCSMISISCSIDriver' - expect(chef_run).to create_directory "/mnt/cinder-volumes" + expect(chef_run).to upgrade_package 'python-pywbem' end - it "configures netapp dfm password" do - ::Chef::Recipe.any_instance.stub(:service_password).with("netapp"). - and_return "netapp-pass" - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.iscsi.NetAppISCSIDriver" - end - chef_run.converge "openstack-block-storage::volume" - n = chef_run.node["openstack"]["block-storage"]["netapp"]["dfm_password"] - - expect(n).to eq "netapp-pass" - end - - it "configures rbd password" do - ::Chef::Recipe.any_instance.stub(:service_password).with("rbd"). - and_return "rbd-pass" - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.RBDDriver" - end - chef_run.converge "openstack-block-storage::volume" - n = chef_run.node["openstack"]["block-storage"]["rbd_secret_uuid"] - - expect(n).to eq "rbd-pass" - end - - it "starts cinder volume" do - expect(@chef_run).to start_service "cinder-volume" - end - - it "starts cinder volume on boot" do - expect(@chef_run).to set_service_to_start_on_boot "cinder-volume" - end - - expect_creates_cinder_conf "service[cinder-volume]", "cinder", "cinder" - - it "starts iscsi target on boot" do - expect(@chef_run).to set_service_to_start_on_boot "tgt" - end - - describe "targets.conf" do + context 'IBMNAS Driver' do + let(:file) { chef_run.template('/etc/cinder/nfs_shares.conf') } before do - @file = @chef_run.template "/etc/tgt/targets.conf" + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver' end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "600" + context 'IBMNAS shares_config file' do + it 'creates the file' do + expect(chef_run).to create_template(file.name).with( + owner: 'cinder', + group: 'cinder', + mode: '0600' + ) + end + + it 'sets the ibmnas access_ip attribute' do + node.set['openstack']['block-storage']['ibmnas']['nas_access_ip'] = '127.0.0.1' + node.set['openstack']['block-storage']['ibmnas']['export'] = '/ibm/fs/export' + expect(chef_run).to render_file(file.name).with_content('127.0.0.1:/ibm/fs/export') + end end - it "notifies iscsi restart" do - expect(@file).to notify "service[iscsitarget]", :restart + it 'upgrades nfs package' do + expect(chef_run).to upgrade_package 'nfs-common' end - it "has ubuntu include" do - expect(@chef_run).to create_file_with_content @file.name, - "include /etc/tgt/conf.d/*.conf" - expect(@chef_run).not_to create_file_with_content @file.name, - "include /var/lib/cinder/volumes/*" + it 'creates the nfs mount point' do + expect(chef_run).to create_directory('/mnt/cinder-volumes').with( + owner: 'cinder', + group: 'cinder', + mode: '0755' + ) + end + end + + context 'NetApp Driver' do + describe 'NFS' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver' + end + + it 'upgrades nfs package' do + expect(chef_run).to upgrade_package 'nfs-common' + end + + it 'creates the nfs mount point' do + expect(chef_run).to create_directory '/mnt/cinder-volumes' + end + + context 'shares config file' do + let(:shares_config_file) { 'nfs_shares_config_file' } + let(:file) { chef_run.template(shares_config_file) } + + before do + node.set['openstack']['block-storage']['nfs']['shares_config'] = shares_config_file + end + + it 'creates the file' do + node.set['openstack']['block-storage']['user'] = 'test_user' + node.set['openstack']['block-storage']['group'] = 'test_group' + + expect(chef_run).to create_template(file.name).with( + owner: 'test_user', + group: 'test_group', + mode: '0600' + ) + end + + it 'sets netapp server hostname export settings' do + netapp_server_hostname = %w(hostname1 hostname2) + node.set['openstack']['block-storage']['netapp']['netapp_server_hostname'] = netapp_server_hostname + node.set['openstack']['block-storage']['netapp']['export'] = 'netapp_export_value' + + netapp_server_hostname.each do |hostname| + expect(chef_run).to render_file(file.name).with_content(/^#{hostname}:netapp_export_value$/) + end + end + end + end + + describe 'ISCSI' do + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.netapp.iscsi.NetAppISCSIDriver' + end + + it 'configures netapp dfm password' do + n = chef_run.node['openstack']['block-storage']['netapp']['dfm_password'] + expect(n).to eq 'netapp-pass' + end + end + end + + context 'Ceph (RBD) Driver' do + let(:file) { chef_run.template('/etc/ceph/ceph.client.cinder.keyring') } + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.rbd.RBDDriver' + node.set['openstack']['block-storage']['rbd_secret_name'] = 'rbd_secret_uuid' + end + + it 'fetches the rbd_uuid_secret' do + n = chef_run.node['openstack']['block-storage']['rbd_secret_uuid'] + expect(n).to eq 'b0ff3bba-e07b-49b1-beed-09a45552b1ad' + end + + it 'includes the ceph_client recipe' do + expect(chef_run).to include_recipe('openstack-common::ceph_client') + end + + it 'upgrades the needed ceph packages by default' do + %w{ python-ceph ceph-common }.each do |pkg| + expect(chef_run).to upgrade_package(pkg) + end + end + + it 'honors package option platform overrides for python-ceph' do + node.set['openstack']['block-storage']['rbd_secret_name'] = 'rbd_secret_uuid' + node.set['openstack']['block-storage']['platform']['package_overrides'] = '--override1 --override2' + + %w{ python-ceph ceph-common }.each do |pkg| + expect(chef_run).to upgrade_package(pkg).with(options: '--override1 --override2') + end + end + + it 'honors package name platform overrides for python-ceph' do + node.set['openstack']['block-storage']['rbd_secret_name'] = 'rbd_secret_uuid' + node.set['openstack']['block-storage']['platform']['cinder_ceph_packages'] = ['my-ceph', 'my-other-ceph'] + + %w{my-ceph my-other-ceph}.each do |pkg| + expect(chef_run).to upgrade_package(pkg) + end + end + + it 'creates a cephx client keyring correctly' do + [/^\[client\.cinder\]$/, + /^ key = cephx-key$/].each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + expect(chef_run).to create_template(file.name).with(cookbook: 'openstack-common') + expect(file.owner).to eq('cinder') + expect(file.group).to eq('cinder') + expect(sprintf('%o', file.mode)).to eq '600' + end + end + + context 'Storewize Driver' do + let(:file) { chef_run.template('/etc/cinder/cinder.conf') } + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver' + end + + it 'configures storewize private key' do + san_key = chef_run.file chef_run.node['openstack']['block-storage']['san']['san_private_key'] + expect(san_key.mode).to eq('0400') + end + + context 'ISCSI' do + before do + node.set['openstack']['block-storage']['storwize']['storwize_svc_connection_protocol'] = 'iSCSI' + end + + it 'configures storewize with iscsi' do + # Test that the FC specific options are not set when connected via iSCSI + expect(chef_run).not_to render_file(file.name).with_content('storwize_svc_multipath_enabled') + end + end + + context 'FC' do + before do + node.set['openstack']['block-storage']['storwize']['storwize_svc_connection_protocol'] = 'FC' + end + + it 'configures storewize with fc' do + # Test that the iSCSI specific options are not set when connected via FC + expect(chef_run).not_to render_file(file.name).with_content('storwize_svc_iscsi_chap_enabled') + end + end + end + + describe 'targets.conf' do + let(:file) { chef_run.template('/etc/tgt/targets.conf') } + + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq '600' + end + + it 'notifies iscsi restart' do + expect(file).to notify('service[iscsitarget]').to(:restart) + end + + it 'has ubuntu include' do + node.set['openstack']['block-storage']['volume']['volumes_dir'] = 'volumes_dir_value' + + expect(chef_run).to render_file(file.name).with_content('include /etc/tgt/conf.d/*.conf') + expect(chef_run).not_to render_file(file.name).with_content('include volumes_dir_value/*') + end + end + + describe 'create_vg' do + let(:file) { chef_run.template('/etc/init.d/cinder-group-active') } + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.lvm.LVMISCSIDriver' + node.set['openstack']['block-storage']['volume']['create_volume_group'] = true + stub_command('vgs cinder-volumes').and_return(false) + end + + it 'cinder vg active' do + expect(chef_run).to enable_service 'cinder-group-active' + end + + it 'create volume group' do + volume_size = chef_run.node['openstack']['block-storage']['volume']['volume_group_size'] + seek_count = volume_size.to_i * 1024 + group_name = chef_run.node['openstack']['block-storage']['volume']['volume_group'] + path = chef_run.node['openstack']['block-storage']['volume']['state_path'] + vg_file = "#{path}/#{group_name}.img" + cmd = "dd if=/dev/zero of=#{vg_file} bs=1M seek=#{seek_count} count=0; vgcreate cinder-volumes $(losetup --show -f #{vg_file})" + expect(chef_run).to run_execute(cmd) + end + + it 'notifies cinder group active start' do + expect(file).to notify('service[cinder-group-active]').to(:start) + end + + it 'creates cinder group active template file' do + expect(chef_run).to create_template(file.name) + end + + describe 'template contents' do + let(:volume_group_value) { 'volume_group_value' } + before do + node.set['openstack']['block-storage']['volume']['volume_group'] = volume_group_value + stub_command("vgs #{volume_group_value}").and_return(true) + end + + it 'calls vgs with the volume name attribute' do + expect(chef_run).to render_file(file.name).with_content(%r(vgs #{volume_group_value} > /dev/null 2>&1)) + end + + it 'calls vgcreate with the volume name and volume file attributes' do + node.set['openstack']['block-storage']['volume']['state_path'] = 'state_path_value' + volume_file = "state_path_value/#{volume_group_value}.img" + expect(chef_run).to render_file(file.name).with_content(/vgcreate #{volume_group_value} \$\(losetup --show -f #{volume_file}\)/) + end + + it 'has ubuntu settings' do + expect(chef_run).to render_file(file.name).with_content(/^\s*echo "SUCCESS"/) + expect(chef_run).not_to render_file(file.name).with_content(/^\s*success$/) + end + end + end + + describe 'cinder_emc_config.xml' do + let(:file) { chef_run.template('/etc/cinder/cinder_emc_config.xml') } + before do + node.set['openstack']['block-storage']['volume']['driver'] = 'cinder.volume.drivers.emc.emc_smis_iscsi.EMCSMISISCSIDriver' + end + + it 'creates cinder emc config file' do + expect(chef_run).to create_template(file.name) + end + + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq('644') + end + + describe 'template contents' do + before do + Chef::Recipe.any_instance.stub(:get_password) + .with('user', anything) + .and_return('emc_test_pass') + end + + %w(StorageType EcomServerPort EcomUserName).each do |attr| + it "has an emc #{attr} setting" do + node.set['openstack']['block-storage']['emc'][attr] = "emc_#{attr}_value" + expect(chef_run).to render_file(file.name).with_content(/^<#{attr}>emc_#{attr}_value<\/#{attr}>$/) + end + end + + it 'has a EcomServerIP' do + node.set['openstack']['block-storage']['emc']['EcomServerIP'] = 'emc_EcomServerIP_value' + expect(chef_run).to render_file(file.name).with_content(/^emc_EcomServerIP_value<\/EcomServerIp>$/) + end + + it 'has EcomPassword' do + node.set['openstack']['block-storage']['emc']['EcomUserName'] = 'emc_username' + expect(chef_run).to render_file(file.name).with_content(/^emc_test_pass<\/EcomPassword>$/) + end + + it 'does not have MaskingView when not specified' do + expect(chef_run).not_to render_file(file.name).with_content(/^/) + end + + it 'has MaskingView when specified' do + node.set['openstack']['block-storage']['emc']['MaskingView'] = 'testMaskingView' + + expect(chef_run).to render_file(file.name).with_content(/^testMaskingView<\/MaskingView>$/) + end end end end diff --git a/chef/cookbooks/openstack-block-storage/templates/default/api-paste.ini.erb b/chef/cookbooks/openstack-block-storage/templates/default/api-paste.ini.erb index ac144e7..a9bb235 100644 --- a/chef/cookbooks/openstack-block-storage/templates/default/api-paste.ini.erb +++ b/chef/cookbooks/openstack-block-storage/templates/default/api-paste.ini.erb @@ -52,10 +52,14 @@ paste.filter_factory = cinder.api.middleware.auth:CinderKeystoneContext.factory [filter:authtoken] paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory +auth_uri = <%= @auth_uri %> auth_host = <%= @identity_admin_endpoint.host %> auth_port = <%= @identity_admin_endpoint.port %> auth_protocol = <%= @identity_admin_endpoint.scheme %> -admin_tenant_name = <%= @service_tenant_name %> -admin_user = <%= @service_user %> +<% if node['openstack']['block-storage']['api']['auth']['version'] != 'v2.0' %> +auth_version = <%= node['openstack']['block-storage']['api']['auth']['version'] %> +<% end %> +admin_tenant_name = <%= node["openstack"]["block-storage"]["service_tenant_name"] %> +admin_user = <%= node["openstack"]["block-storage"]["service_user"] %> admin_password = <%= @service_pass %> signing_dir = <%= node["openstack"]["block-storage"]["api"]["auth"]["cache_dir"] %> diff --git a/chef/cookbooks/openstack-block-storage/templates/default/cinder-group-active.erb b/chef/cookbooks/openstack-block-storage/templates/default/cinder-group-active.erb new file mode 100644 index 0000000..3a7eebb --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/cinder-group-active.erb @@ -0,0 +1,47 @@ +#!/bin/sh +<%= node["openstack"]["block-storage"]["custom_template_banner"] %> +# +# cinder volume group active script +# +# only support start action +# chkconfig: - 98 02 +# +### BEGIN INIT INFO +# Required-Start: $remote_fs $network $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: cinder volume group active script +### END INIT INFO + +<% if %w{rhel}.include? node.platform_family %> +. /etc/rc.d/init.d/functions +<% end %> + +start() +{ + vgs <%= @volume_name %> > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo -n "Activating cinder volume group ..." + vgcreate <%= @volume_name %> $(losetup --show -f <%= @volume_file %>) + <% if %w{rhel}.include? node.platform_family %> + success + echo + <% elsif %w{debian}.include? node.platform_family %> + echo "SUCCESS" + <% end %> + fi +} + +RETVAL=0 + +case "$1" in + start) + start + ;; + *) + echo "Usage: $0 {start}" + RETVAL=1 +esac + +exit $RETVAL diff --git a/chef/cookbooks/openstack-block-storage/templates/default/cinder.conf.erb b/chef/cookbooks/openstack-block-storage/templates/default/cinder.conf.erb index 74837ba..b0ddc35 100644 --- a/chef/cookbooks/openstack-block-storage/templates/default/cinder.conf.erb +++ b/chef/cookbooks/openstack-block-storage/templates/default/cinder.conf.erb @@ -11,8 +11,8 @@ verbose=<%= node["openstack"]["block-storage"]["verbose"] %> #### (BoolOpt) Print more verbose output # lock_path -#lock_path=<%= node["openstack"]["block-storage"]["lock_path"] %> -notification_driver=cinder.openstack.common.notifier.rpc_notifier +lock_path=<%= node["openstack"]["block-storage"]["lock_path"] %> +notification_driver=<%= node["openstack"]["block-storage"]["notification_driver"] %> # log_config= #### (StrOpt) If this option is specified, the logging configuration file @@ -41,6 +41,8 @@ notification_driver=cinder.openstack.common.notifier.rpc_notifier <% if node["openstack"]["block-storage"]["syslog"]["use"] %> log_config = /etc/openstack/logging.conf +<% else %> +log_file = /var/log/cinder/cinder.log <% end %> ######## defined in cinder.flags ######## @@ -170,13 +172,6 @@ auth_strategy=keystone #### (StrOpt) The strategy to use for auth. Supports noauth, keystone, and #### deprecated. -<% if node["openstack"]["block-storage"]["rabbit"]["control_exchange"] %> -control_exchange=<%=node["openstack"]["block-storage"]["rabbit"]["control_exchange"]%> -<% end %> -# control_exchange=cinder -#### (StrOpt) AMQP exchange to connect to if using RabbitMQ or Qpid - - ######## defined in cinder.policy ######## # policy_file=policy.json @@ -220,12 +215,11 @@ quota_driver=<%= node["openstack"]["block-storage"]["quota_driver"] %> #### periodic task scheduler to reduce stampeding. (Disable by #### setting to 0) -# osapi_volume_listen=0.0.0.0 +osapi_volume_listen=<%= @volume_api_bind_address %> #### (StrOpt) IP address for OpenStack Volume API to listen -# osapi_volume_listen_port=8776 #### (IntOpt) port for os volume api to listen - +osapi_volume_listen_port=<%= @volume_api_bind_port %> ######## defined in cinder.test ######## @@ -263,10 +257,10 @@ db_backend=sqlalchemy # enable_new_services=true #### (BoolOpt) Services to be added to the available pool on create -# volume_name_template=volume-%s +volume_name_template=<%= node["openstack"]["block-storage"]["volume_name_template"] %> #### (StrOpt) Template string to be used to generate volume names -# snapshot_name_template=snapshot-%s +snapshot_name_template=<%= node["openstack"]["block-storage"]["snapshot_name_template"] %> #### (StrOpt) Template string to be used to generate snapshot names @@ -279,7 +273,7 @@ db_backend=sqlalchemy ######## defined in cinder.openstack.common.log ######## # logdir= -#### (StrOpt) Log output to a per-service log file in named directory +### (StrOpt) Log output to a per-service log file in named directory # logfile= #### (StrOpt) Log output to a named file @@ -327,25 +321,24 @@ db_backend=sqlalchemy # default_publisher_id=$host #### (StrOpt) Default publisher_id for outgoing notifications - -######## defined in cinder.openstack.common.notifier.rabbit_notifier ######## - -# notification_topics=notifications -#### (ListOpt) AMQP topic used for openstack notifications - - ######## defined in cinder.openstack.common.rpc ######## -# rpc_backend=cinder.openstack.common.rpc.impl_kombu +rpc_backend=<%= node["openstack"]["block_storage"]["rpc_backend"] %> #### (StrOpt) The messaging module to use, defaults to kombu. -# rpc_thread_pool_size=64 +control_exchange=<%=node['openstack']['block-storage']['control_exchange']%> +#### (StrOpt) AMQP exchange to connect to if using RabbitMQ or Qpid + +rpc_thread_pool_size=<%= node["openstack"]["block-storage"]["rpc_thread_pool_size"] %> + #### (IntOpt) Size of RPC thread pool -# rpc_conn_pool_size=30 +rpc_conn_pool_size=<%= node["openstack"]["block-storage"]["rpc_conn_pool_size"] %> + #### (IntOpt) Size of RPC connection pool -# rpc_response_timeout=60 +rpc_response_timeout=<%= node["openstack"]["block-storage"]["rpc_response_timeout"] %> + #### (IntOpt) Seconds to wait for a response from call or multicall # rpc_cast_timeout=30 @@ -356,10 +349,12 @@ db_backend=sqlalchemy #### (ListOpt) Modules of exceptions that are permitted to be recreatedupon #### receiving exception data from an rpc call. + +<% if @mq_service_type == "rabbitmq" %> + # fake_rabbit=false #### (BoolOpt) If passed, use a fake RabbitMQ provider - ######## defined in cinder.openstack.common.rpc.impl_kombu ######## # kombu_ssl_version= @@ -374,7 +369,7 @@ db_backend=sqlalchemy # kombu_ssl_ca_certs= #### (StrOpt) SSL certification authority file (valid only if SSL enabled) -<% if node["openstack"]["block-storage"]["rabbit"]["ha"] -%> +<% if node["openstack"]["mq"]["block-storage"]["rabbit"]["ha"] -%> rabbit_hosts=<%= @rabbit_hosts %> #### (ListOpt) RabbitMQ HA cluster host:port pairs @@ -385,28 +380,28 @@ rabbit_ha_queues=True #### (BoolOpt) use H/A queues in RabbitMQ (x-ha-policy: all).You need to #### wipe RabbitMQ database when changing this option. <% else -%> -# rabbit_host=<%= node["openstack"]["block-storage"]["rabbit"]["host"] %> -rabbit_host=<%= node['openstack']['mq']['bind_address'] %> - +rabbit_host=<%= node["openstack"]["mq"]["block-storage"]["rabbit"]["host"] %> #### (StrOpt) The RabbitMQ broker address where a single node is used -# rabbit_port=<%= node["openstack"]["block-storage"]["rabbit"]["port"] %> -rabbit_port=<%= node['openstack']['mq']['port'] %> +rabbit_port=<%= node["openstack"]["mq"]["block-storage"]["rabbit"]["port"] %> #### (IntOpt) The RabbitMQ broker port where a single node is used <% end -%> -# rabbit_use_ssl=false +rabbit_use_ssl=<%= node["openstack"]["mq"]["block-storage"]["rabbit"]["use_ssl"] %> #### (BoolOpt) connect over SSL for RabbitMQ -rabbit_userid=<%= node["openstack"]["block-storage"]["rabbit"]["username"] %> +rabbit_userid=<%= node["openstack"]["mq"]["block-storage"]["rabbit"]["userid"] %> #### (StrOpt) the RabbitMQ userid -rabbit_password=<%= @rabbit_password %> +rabbit_password=<%= @mq_password %> #### (StrOpt) the RabbitMQ password -rabbit_virtual_host=<%= node["openstack"]["block-storage"]["rabbit"]["vhost"] %> +rabbit_virtual_host=<%= node["openstack"]["mq"]["block-storage"]["rabbit"]["vhost"] %> #### (StrOpt) the RabbitMQ virtual host +notification_topics=<%= node["openstack"]["mq"]["block-storage"]["rabbit"]["notification_topic"] %> +#### (ListOpt) AMQP topic used for openstack notifications + # rabbit_retry_interval=1 #### (IntOpt) how frequently to retry connecting with RabbitMQ @@ -418,51 +413,59 @@ rabbit_virtual_host=<%= node["openstack"]["block-storage"]["rabbit"]["vhost"] %> #### (IntOpt) maximum retries with trying to connect to RabbitMQ (the #### default of 0 implies an infinite retry count) +<% end %> + +<% if @mq_service_type == "qpid" %> + +##### QPID ##### ######## defined in cinder.openstack.common.rpc.impl_qpid ######## - -# qpid_hostname=localhost +qpid_hostname=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["host"] %> #### (StrOpt) Qpid broker hostname -# qpid_port=5672 +qpid_port=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["port"] %> #### (StrOpt) Qpid broker port -# qpid_username= +qpid_username=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["username"] %> #### (StrOpt) Username for qpid connection -# qpid_password= +qpid_password=<%= @mq_password %> #### (StrOpt) Password for qpid connection -# qpid_sasl_mechanisms= +qpid_sasl_mechanisms=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["sasl_mechanisms"] %> #### (StrOpt) Space separated list of SASL mechanisms to use for auth -# qpid_reconnect=true +qpid_reconnect=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["reconnect"] %> #### (BoolOpt) Automatically reconnect -# qpid_reconnect_timeout=0 +qpid_reconnect_timeout=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["reconnect_timeout"] %> #### (IntOpt) Reconnection timeout in seconds -# qpid_reconnect_limit=0 +qpid_reconnect_limit=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["reconnect_limit"] %> #### (IntOpt) Max reconnections before giving up -# qpid_reconnect_interval_min=0 +qpid_reconnect_interval_min=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["reconnect_interval_min"] %> #### (IntOpt) Minimum seconds between reconnection attempts -# qpid_reconnect_interval_max=0 +qpid_reconnect_interval_max=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["reconnect_interval_max"] %> #### (IntOpt) Maximum seconds between reconnection attempts -# qpid_reconnect_interval=0 +qpid_reconnect_interval=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["reconnect_interval"] %> #### (IntOpt) Equivalent to setting max and min to the same value -# qpid_heartbeat=60 +qpid_heartbeat=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["heartbeat"] %> #### (IntOpt) Seconds between connection keepalive heartbeats -# qpid_protocol=tcp +qpid_protocol=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["protocol"] %> #### (StrOpt) Transport to use, either 'tcp' or 'ssl' -# qpid_tcp_nodelay=true +qpid_tcp_nodelay=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["tcp_nodelay"] %> #### (BoolOpt) Disable Nagle algorithm +notification_topics=<%= node["openstack"]["mq"]["block-storage"]["qpid"]["notification_topic"] %> +#### (ListOpt) AMQP topic used for openstack notifications +<% end %> + ######## defined in cinder.openstack.common.rpc.impl_zmq ######## @@ -523,10 +526,13 @@ max_gigabytes=<%= node["openstack"]["block-storage"]["max_gigabytes"] %> ######## defined in cinder.volume.driver ######## - +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.lvm.LVMISCSIDriver" %> volume_group=<%= node["openstack"]["block-storage"]["volume"]["volume_group"] %> #### (StrOpt) Name for the VG that will contain exported volumes +volume_clear=<%= node["openstack"]["block-storage"]["volume"]["volume_clear"] %> +volume_clear_size=<%= node["openstack"]["block-storage"]["volume"]["volume_clear_size"] %> +<% end %> # num_shell_tries=3 #### (IntOpt) number of times to attempt to run flakey shell commands @@ -539,12 +545,13 @@ volume_group=<%= node["openstack"]["block-storage"]["volume"]["volume_group"] %> # iscsi_target_prefix=iqn.2010-10.org.openstack: #### (StrOpt) prefix for iscsi volumes -# iscsi_ip_address=$my_ip -#### (StrOpt) use this ip for iscsi +iscsi_ip_address=<%= node["openstack"]["block-storage"]["volume"]["iscsi_ip_address"] %> +#### (StrOpt) The IP address where the iSCSI daemon is listening on -# iscsi_port=3260 +iscsi_port=<%= node["openstack"]["block-storage"]["volume"]["iscsi_port"] %> #### (IntOpt) The port that the iSCSI daemon is listening on -<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.RBDDriver" %> + +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.rbd.RBDDriver" %> rbd_pool=<%= node["openstack"]["block-storage"]["rbd_pool"] %> #### (StrOpt) the RADOS pool in which rbd volumes are stored @@ -553,6 +560,15 @@ rbd_user=<%= node["openstack"]["block-storage"]["rbd_user"] %> rbd_secret_uuid=<%= node["openstack"]["block-storage"]["rbd_secret_uuid"] %> #### (StrOpt) the libvirt uuid of the secret for the rbd_uservolumes + +rbd_ceph_conf=<%= node["openstack"]["block-storage"]["rbd_ceph_conf"] %> + +rbd_flatten_volume_from_snapshot=<%= node["openstack"]["block-storage"]["rbd_flatten_volume_from_snapshot"] %> + +rbd_max_clone_depth=<%= node["openstack"]["block-storage"]["rbd_max_clone_depth"] %> + +glance_api_version=<%= node["openstack"]["block-storage"]["glance_api_version"] %> + <% end %> # volume_tmp_dir= #### (StrOpt) where to store temporary image files if the volume driver @@ -564,7 +580,7 @@ rbd_secret_uuid=<%= node["openstack"]["block-storage"]["rbd_secret_uuid"] %> iscsi_helper=<%= node["openstack"]["block-storage"]["volume"]["iscsi_helper"] %> #### (StrOpt) iscsi target user-land tool to use -# volumes_dir=$state_path/volumes +volumes_dir=<%= node['openstack']['block-storage']['volume']['volumes_dir'] %> #### (StrOpt) Volume configuration file storage directory @@ -664,6 +680,20 @@ nfs_sparsed_volumes=<%= node["openstack"]["block-storage"]["nfs"]["nfs_sparsed_v ######## defined in cinder.volume.nfs ######## +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver" %> + +nfs_shares_config=<%= node["openstack"]["block-storage"]["ibmnas"]["shares_config"] %> +#### (StrOpt) File with the list of available nfs shares + +nfs_mount_point_base=<%= node["openstack"]["block-storage"]["ibmnas"]["mount_point_base"] %> +#### (StrOpt) Base dir where nfs expected to be mounted + +nfs_sparsed_volumes=<%= node["openstack"]["block-storage"]["ibmnas"]["nfs_sparsed_volumes"] %> +#### (BoolOpt) Create volumes as sparsed files which take no space.If set +#### to False volume is created as regular file.In such case +#### volume creation takes a lot of time. + +<% else %> # nfs_shares_config= #### (StrOpt) File with the list of available nfs shares @@ -677,10 +707,23 @@ nfs_sparsed_volumes=<%= node["openstack"]["block-storage"]["nfs"]["nfs_sparsed_v #### (BoolOpt) Create volumes as sparsed files which take no space.If set #### to False volume is created as regular file.In such case #### volume creation takes a lot of time. +<% end %> ######## defined in cinder.volume.san ######## +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver" %> + +san_ip=<%= node["openstack"]["block-storage"]["storwize"]["san_ip"] %> +#### (StrOpt) IP address of SAN controller + +san_login=<%= node["openstack"]["block-storage"]["storwize"]["san_login"] %> +#### (StrOpt) Username for SAN controller + +san_private_key=<%= node["openstack"]["block-storage"]["storwize"]["san_private_key"] %> +#### (StrOpt) Filename of private key to use for SSH authentication + +<% else %> # san_thin_provision=true #### (BoolOpt) Use thin provisioning for SAN volumes? @@ -708,54 +751,102 @@ nfs_sparsed_volumes=<%= node["openstack"]["block-storage"]["nfs"]["nfs_sparsed_v # san_zfs_volume_base=rpool/ #### (StrOpt) The ZFS path under which to create zvols for volumes. +<% end %> - +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.solidfire.SolidFire" %> ######## defined in cinder.volume.solidfire ######## - -# sf_emulate_512=true +sf_emulate_512=<%= node["openstack"]["block-storage"]["solidfire"]["sf_emulate"] %> #### (BoolOpt) Set 512 byte emulation on volume creation; -# sf_mvip= -#### (StrOpt) IP address of SolidFire MVIP +san_ip=<%= node["openstack"]["block-storage"]["solidfire"]["san_ip"] %> +# #### (StrOpt) IP address of SolidFire MVIP -# sf_login=admin +san_login=<%= node["openstack"]["block-storage"]["solidfire"]["san_login"] %> #### (StrOpt) Username for SF Cluster Admin -# sf_password= +san_password=<%= @solidfire_pass %> #### (StrOpt) Password for SF Cluster Admin -# sf_allow_tenant_qos=true +<% unless node["openstack"]["block-storage"]["solidfire"]['iscsi_ip_prefix'].nil? %> +iscsi_ip_prefix=<%= node["openstack"]["block-storage"]["solidfire"]["iscsi_ip_prefix"] %> +<% end %> +<% end %> + #### (BoolOpt) Allow tenants to specify QOS on create - - +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver" %> ######## defined in cinder.volume.storwize_svc ######## -# storwize_svc_volpool_name=volpool +storwize_svc_volpool_name=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_volpool_name"] %> #### (StrOpt) Storage system storage pool for volumes -# storwize_svc_vol_rsize=2% +storwize_svc_vol_rsize=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_vol_rsize"] %> #### (StrOpt) Storage system space-efficiency parameter for volumes -# storwize_svc_vol_warning=0 +storwize_svc_vol_warning=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_vol_warning"] %> #### (StrOpt) Storage system threshold for volume capacity warnings -# storwize_svc_vol_autoexpand=true +storwize_svc_vol_autoexpand=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_vol_autoexpand"] %> #### (BoolOpt) Storage system autoexpand parameter for volumes (True/False) -# storwize_svc_vol_grainsize=256 +storwize_svc_vol_grainsize=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_vol_grainsize"] %> #### (StrOpt) Storage system grain size parameter for volumes #### (32/64/128/256) -# storwize_svc_vol_compression=false +storwize_svc_vol_compression=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_vol_compression"] %> #### (BoolOpt) Storage system compression option for volumes -# storwize_svc_vol_easytier=true +storwize_svc_vol_easytier=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_vol_easytier"] %> #### (BoolOpt) Enable Easy Tier for volumes -# storwize_svc_flashcopy_timeout=120 +# The I/O group in which to allocate volumes (integer value) +storwize_svc_vol_iogrp=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_vol_iogrp"] %> + +storwize_svc_flashcopy_timeout=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_flashcopy_timeout"] %> #### (StrOpt) Maximum number of seconds to wait for FlashCopy to be #### prepared. Maximum value is 600 seconds (10 minutes). +# Connection protocol (iSCSI/FC) (string value) +storwize_svc_connection_protocol=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_connection_protocol"] %> + +<% if node["openstack"]["block-storage"]["storwize"]["storwize_svc_connection_protocol"] == "iSCSI" %> +# Configure CHAP authentication for iSCSI connections +# (Default: Enabled) (boolean value) +storwize_svc_iscsi_chap_enabled=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_iscsi_chap_enabled"] %> + +<% else %> + +# Connect with multipath (FC only; iSCSI multipath is +# controlled by Nova) (boolean value) +storwize_svc_multipath_enabled=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_multipath_enabled"] %> + +<% end %> +# Allows vdisk to multi host mapping (boolean value) +storwize_svc_multihostmap_enabled=<%= node["openstack"]["block-storage"]["storwize"]["storwize_svc_multihostmap_enabled"] %> + +<% end %> + +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.emc.emc_smis_iscsi.EMCSMISISCSIDriver" %> + +iscsi_target_prefix=<%= node["openstack"]["block-storage"]["emc"]["iscsi_target_prefix"] %> +cinder_emc_config_file=<%= node["openstack"]["block-storage"]["emc"]["cinder_emc_config_file"] %> + +<% end %> + +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver" %> +######## defined in cinder.volume.ibm.ibmnas ######## + +nas_ip=<%= node["openstack"]["block-storage"]["ibmnas"]["nas_ip"] %> +#### (StrOpt) IP address of IBMNAS storage + +nas_login=<%= node["openstack"]["block-storage"]["ibmnas"]["nas_login"] %> +#### (StrOpt) Username for IBMNAS Cluster + +nas_password=<%= @ibmnas_pass %> +#### (StrOpt) Password for IBMNAS Cluster + +nas_ssh_port=<%= node["openstack"]["block-storage"]["ibmnas"]["nas_ssh_port"] %> +#### (StrOpt) IP address of IBMNAS storage +<% end %> ######## defined in cinder.volume.xiv ######## @@ -803,3 +894,100 @@ nfs_sparsed_volumes=<%= node["openstack"]["block-storage"]["nfs"]["nfs_sparsed_v # zadara_vpsa_allow_nonexistent_delete=true #### (BoolOpt) Don't halt on deletion of non-existing volumes + +<% if node['openstack']['block-storage']['volume']['driver'] == 'cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver' %> +# +# Options defined in cinder.volume.drivers.vmware.vmdk +# + +# IP address for connecting to VMware ESX/VC server. (string +# value) +vmware_host_ip = <%= node['openstack']['block-storage']['vmware']['vmware_host_ip'] %> + +# Username for authenticating with VMware ESX/VC server. +# (string value) +vmware_host_username = <%= node['openstack']['block-storage']['vmware']['vmware_host_username'] %> + +# Password for authenticating with VMware ESX/VC server. +# (string value) +vmware_host_password = <%= @vmware_host_pass %> + +<% if node['openstack']['block-storage']['vmware']['vmware_wsdl_location'] -%> +# Optional VIM service WSDL Location e.g +# http:///vimService.wsdl. Optional over-ride to +# default location for bug work-arounds. (string value) +vmware_wsdl_location = <%= node['openstack']['block-storage']['vmware']['vmware_wsdl_location'] %> +<% end %> + +# Number of times VMware ESX/VC server API must be retried +# upon connection related issues. (integer value) +vmware_api_retry_count = <%= node['openstack']['block-storage']['vmware']['vmware_api_retry_count'] %> + +# The interval (in seconds) for polling remote tasks invoked +# on VMware ESX/VC server. (integer value) +vmware_task_poll_interval = <%= node['openstack']['block-storage']['vmware']['vmware_task_poll_interval'] %> + +# Name for the folder in the VC datacenter that will contain +# cinder volumes. (string value) +vmware_volume_folder = <%= node['openstack']['block-storage']['vmware']['vmware_volume_folder'] %> + +# Timeout in seconds for VMDK volume transfer between Cinder +# and Glance. (integer value) +vmware_image_transfer_timeout_secs = <%= node['openstack']['block-storage']['vmware']['vmware_image_transfer_timeout_secs'] %> + +# Max number of objects to be retrieved per batch. Query +# results will be obtained in batches from the server and not +# in one shot. Server may still limit the count to something +# less than the configured value. (integer value) +vmware_max_objects_retrieval = <%= node['openstack']['block-storage']['vmware']['vmware_max_objects_retrieval'] %> +<% end %> + +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.gpfs.GPFSDriver" %> +######## defined in cinder.openstack.volume.drivers.ibm.gpfs ######## + +# Specifies the path of the GPFS directory where Block Storage +# volume and snapshot files are stored. (string value) +gpfs_mount_point_base = <%= node["openstack"]["block-storage"]["gpfs"]["gpfs_mount_point_base"] %> + +<% if node['openstack']['block-storage']['gpfs']['gpfs_images_dir'] %> +# Specifies the path of the Image service repository in GPFS. +# Leave undefined if not storing images in GPFS. (string +# value) +gpfs_images_dir = <%= node['openstack']['block-storage']['gpfs']['gpfs_images_dir'] %> +<% end %> + +# Specifies the type of image copy to be used. Set this when +# the Image service repository also uses GPFS so that image +# files can be transferred efficiently from the Image service +# to the Block Storage service. There are two valid values: +# "copy" specifies that a full copy of the image is made; +# "copy_on_write" specifies that copy-on-write optimization +# strategy is used and unmodified blocks of the image file are +# shared efficiently. (string value) +gpfs_images_share_mode = <%= node['openstack']['block-storage']['gpfs']['gpfs_images_share_mode'] %> + +# Specifies an upper limit on the number of indirections +# required to reach a specific block due to snapshots or +# clones. A lengthy chain of copy-on-write snapshots or +# clones can have a negative impact on performance, but +# improves space utilization. 0 indicates unlimited clone +# depth. (integer value) +gpfs_max_clone_depth = <%= node['openstack']['block-storage']['gpfs']['gpfs_max_clone_depth'] %> + +# Specifies that volumes are created as sparse files which +# initially consume no space. If set to False, the volume is +# created as a fully allocated file, in which case, creation +# may take a significantly longer time. (boolean value) +gpfs_sparse_volumes = <%= node['openstack']['block-storage']['gpfs']['gpfs_sparse_volumes'] %> + +# Specifies the storage pool that volumes are assigned to. By +# default, the system storage pool is used. (string value) +gpfs_storage_pool = <%= node['openstack']['block-storage']['gpfs']['gpfs_storage_pool'] %> +<% end %> + +# Misc options +<% if node["openstack"]["block-storage"]["misc_cinder"] %> +<% node["openstack"]["block-storage"]["misc_cinder"].each do |m| %> +<%= m %> +<% end %> +<% end %> diff --git a/chef/cookbooks/openstack-block-storage/templates/default/cinder_emc_config.xml.erb b/chef/cookbooks/openstack-block-storage/templates/default/cinder_emc_config.xml.erb new file mode 100644 index 0000000..9e722f7 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/cinder_emc_config.xml.erb @@ -0,0 +1,10 @@ + +<%= node["openstack"]["block-storage"]["emc"]["StorageType"] %> +<%= node["openstack"]["block-storage"]["emc"]["EcomServerIP"] %> +<%= node["openstack"]["block-storage"]["emc"]["EcomServerPort"] %> +<%= node["openstack"]["block-storage"]["emc"]["EcomUserName"] %> +<%= @ecom_password %> +<% unless node["openstack"]["block-storage"]["emc"]["MaskingView"].nil? %> +<%= node["openstack"]["block-storage"]["emc"]["MaskingView"] %> +<% end %> + diff --git a/chef/cookbooks/openstack-block-storage/templates/default/nfs_shares.conf.erb b/chef/cookbooks/openstack-block-storage/templates/default/nfs_shares.conf.erb new file mode 100644 index 0000000..e03edb7 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/nfs_shares.conf.erb @@ -0,0 +1,3 @@ +<%= node["openstack"]["block-storage"]["custom_template_banner"] %> + +<%= @host %>:<%= @export %> diff --git a/chef/cookbooks/openstack-block-storage/templates/default/policy.json.erb b/chef/cookbooks/openstack-block-storage/templates/default/policy.json.erb deleted file mode 100644 index fc07b12..0000000 --- a/chef/cookbooks/openstack-block-storage/templates/default/policy.json.erb +++ /dev/null @@ -1,34 +0,0 @@ -{ - "context_is_admin": [<%= node["openstack"]["block-storage"]["policy"]["context_is_admin"] %>], - "admin_or_owner": [<%= node["openstack"]["block-storage"]["policy"]["admin_or_owner"] %>], - "default": [<%= node["openstack"]["block-storage"]["policy"]["default"] %>], - - "admin_api": [<%= node["openstack"]["block-storage"]["policy"]["admin_api"] %>], - - "volume:create": [], - "volume:get_all": [], - "volume:get_volume_metadata": [], - "volume:get_snapshot": [], - "volume:get_all_snapshots": [], - - "volume_extension:types_manage": [["rule:admin_api"]], - "volume_extension:types_extra_specs": [["rule:admin_api"]], - "volume_extension:extended_snapshot_attributes": [], - "volume_extension:volume_image_metadata": [], - - "volume_extension:quotas:show": [], - "volume_extension:quotas:update_for_project": [["rule:admin_api"]], - "volume_extension:quotas:update_for_user": [["rule:admin_or_projectadmin"]], - "volume_extension:quota_classes": [], - - "volume_extension:volume_admin_actions:reset_status": [["rule:admin_api"]], - "volume_extension:snapshot_admin_actions:reset_status": [["rule:admin_api"]], - "volume_extension:volume_admin_actions:force_delete": [["rule:admin_api"]], - "volume_extension:snapshot_admin_actions:force_delete": [["rule:admin_api"]], - - "volume_extension:volume_host_attribute": [["rule:admin_api"]], - "volume_extension:volume_tenant_attribute": [["rule:admin_api"]], - "volume_extension:hosts": [["rule:admin_api"]], - "volume_extension:services": [["rule:admin_api"]], - "volume:services": [["rule:admin_api"]] -} diff --git a/chef/cookbooks/openstack-block-storage/templates/default/shares.conf.erb b/chef/cookbooks/openstack-block-storage/templates/default/shares.conf.erb index e8b93f2..1c46112 100644 --- a/chef/cookbooks/openstack-block-storage/templates/default/shares.conf.erb +++ b/chef/cookbooks/openstack-block-storage/templates/default/shares.conf.erb @@ -1,4 +1,5 @@ -# Automatically generated by chef, changes will be overwritten +<%= node["openstack"]["block-storage"]["custom_template_banner"] %> + <% node["openstack"]["block-storage"]["netapp"]["netapp_server_hostname"].each do |h| %> <%= h %>:<%= @export %> <% end %> diff --git a/chef/cookbooks/openstack-block-storage/templates/default/targets.conf.erb b/chef/cookbooks/openstack-block-storage/templates/default/targets.conf.erb index fedbd7f..bc4a01a 100644 --- a/chef/cookbooks/openstack-block-storage/templates/default/targets.conf.erb +++ b/chef/cookbooks/openstack-block-storage/templates/default/targets.conf.erb @@ -1,9 +1,9 @@ <%= node["openstack"]["block-storage"]["custom_template_banner"] %> -<% if %w{redhat centos fedora suse}.include?(node["platform"]) %> -include /var/lib/cinder/volumes/* +<% if %w{rhel fedora suse}.include?(node["platform_family"]) %> +include <%= node['openstack']['block-storage']['volume']['volumes_dir'] %>/* <% end %> -<% if %w{debian ubuntu}.include?(node["platform"]) %> +<% if %w{debian}.include?(node["platform_family"]) %> include /etc/tgt/conf.d/*.conf <% end %> default-driver iscsi diff --git a/chef/cookbooks/openstack-common/.rubocop.yml b/chef/cookbooks/openstack-common/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-common/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-common/.tailor b/chef/cookbooks/openstack-common/.tailor deleted file mode 100644 index 99f0dcf..0000000 --- a/chef/cookbooks/openstack-common/.tailor +++ /dev/null @@ -1,25 +0,0 @@ -Tailor.config do |config| - config.formatters "text" - config.file_set '**/*.rb' do |style| - style.max_line_length 80, level: :off - style.allow_camel_case_methods false, level: :error - style.allow_hard_tabs false, level: :error - style.allow_screaming_snake_case_classes false, level: :error - style.allow_trailing_line_spaces false, level: :error - style.allow_invalid_ruby false, level: :warn - style.indentation_spaces 2, level: :error - style.max_code_lines_in_class 300, level: :error - style.max_code_lines_in_method 30, level: :error - style.spaces_after_comma 1, level: :error - style.spaces_after_lbrace 1, level: :error - style.spaces_after_lbracket 0, level: :error - style.spaces_after_lparen 0, level: :error - style.spaces_before_comma 0, level: :error - style.spaces_before_lbrace 1, level: :error - style.spaces_before_rbrace 1, level: :error - style.spaces_before_rbracket 0, level: :error - style.spaces_before_rparen 0, level: :error - style.spaces_in_empty_braces 0, level: :error - style.trailing_newlines 1, level: :error - end -end diff --git a/chef/cookbooks/openstack-common/CHANGELOG.md b/chef/cookbooks/openstack-common/CHANGELOG.md old mode 100644 new mode 100755 index ee9e599..b11d670 --- a/chef/cookbooks/openstack-common/CHANGELOG.md +++ b/chef/cookbooks/openstack-common/CHANGELOG.md @@ -1,6 +1,105 @@ # CHANGELOG for cookbook-openstack-common This file is used to list changes made in each version of cookbook-openstack-common. +## 9.4.1 +* Fix to allow database connection options for telemetry nosql + +## 9.4.0 +* Add durable_queues, auto_delete, and qpid topology version attributes + +## 9.3.0 +* Provide an option to specify the password when dev mode equals true + +## 9.2.2 +* Fixed openrc failure on role search + +## 9.2.1 +* Fix package action to allow updates + +## 9.2.0 +* Add recipe for openrc file (moved from compute cookbook) + +## 9.1.2 +* Make PKI tokens the new default + +## 9.1.1 +* Add new library method for making cli calls and one for getting uuids + +## 9.1.0 +* Added python-openstackclient support + +## 9.0.2 +* Allow address_for family default to be overridden + +## 9.0.1 +### Bug +* Fix the depends cookbook version issue in metadata.rb + +## 9.0.0 +* Upgrading to Icehouse + +## 8.5.0 +* Add get_secret library method which allows one to specify a secrets_data_bag in attributes. + +## 8.4.2 +* Adjust image service endpoint path from /v2 to / + +## 8.4.1 +* Fix a renaming openstack-metering issue, change `metering` to `telemetry` in db_uri function. + +## 8.4.0 +* Rename openstack-metering to openstack-telemetry + +## 8.3.0 +### Blueprint +* use-data-bag-for-qpid-password: have qpid use get_password method rather than + using a password attribute + +## 8.2.1 +### Bug +* Add notification_topics attribute to network attributes + +## 8.2.0 +* Update and add new attributes for openstack-network cookbook + +## 8.1.1 +### Bug +* Adjust metering service endpoint path from /v1 to / + +## 8.1.0 +### Blueprint: +* yum-cookbook-v3-support: Update this cookbook to be compatible with version 3 of the yum cookbook. + +## 8.0.1: +### Bug +* Add sleep to search_for function, so that node can be searched +* Add CentOS/RHEL support + +## 8.0.0: +* Upgrading to Havana +* Upgrading gems + * ChefSpec -> 3.0.2 + * Foodcritic -> 3.0.3 + * Berkshelf -> 2.0.10 + +## 0.4.7: +### Bug +* Change `#db_uri` to hand out UTF8 MySQL URIs; i.e. append '?charset=utf8' to mysql URIs + +## 0.4.6: +### Bug +* Ensuring `#db_uri` returns a valid sqlite connection string + * relative path example: 'path' = 'path/to/foo.db' -- will return sqlite:///foo.db + * absolute path example: 'path' = '/path/to/foo.db' -- will return sqlite:////foo.db + +## 0.4.5: +* Added `openstack-common::set_endpoints_by_interface` to enable using + `bind_interface` with endpoints rather than hard-code the IP addresses in an + Environment. + +## 0.4.4: +* Add support for openstack-common::sysctl and managing sysctl settings via the + node['openstack']['sysctl'] hash, written out to /etc/sysctl.d/60-openstack.conf ## 0.4.3: * Corrected `#search_for` role and recipe queries. diff --git a/chef/cookbooks/openstack-common/Gemfile b/chef/cookbooks/openstack-common/Gemfile index 7de4657..ccfa456 100644 --- a/chef/cookbooks/openstack-common/Gemfile +++ b/chef/cookbooks/openstack-common/Gemfile @@ -1,9 +1,9 @@ -source "https://rubygems.org" +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 1.4.5" -gem "chefspec", "~> 1.2.0" -gem "foodcritic" -gem "strainer" -gem "tailor" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' diff --git a/chef/cookbooks/openstack-common/Gemfile.lock b/chef/cookbooks/openstack-common/Gemfile.lock index 08e2917..2954927 100644 --- a/chef/cookbooks/openstack-common/Gemfile.lock +++ b/chef/cookbooks/openstack-common/Gemfile.lock @@ -1,159 +1,192 @@ GEM remote: https://rubygems.org/ specs: - activesupport (3.2.13) - i18n (= 0.6.1) + activesupport (3.2.17) + i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.3.4) - akami (1.2.0) + addressable (2.3.6) + akami (1.2.1) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - berkshelf (1.4.5) - activesupport (>= 3.2.0) - addressable - celluloid (>= 0.14.0) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) - json (>= 1.5.0) - minitar - mixlib-config (~> 1.1) - mixlib-shellout (~> 1.1) - multi_json (~> 1.5) - retryable - ridley (~> 0.12.4) - solve (>= 0.4.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.5.0) + solve (>= 0.5.0) thor (~> 0.18.0) - yajl-ruby + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) + buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.1) + buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) timers (>= 1.0.0) - chef (11.4.4) - erubis - highline (>= 1.6.9) - json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) net-ssh (~> 2.6) - net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (1.2.0) - chef (>= 10.0) - erubis - fauxhai (>= 0.1.1, < 2.0) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.8.4) - builder (>= 2.1.2) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.7) - multipart-post (~> 1.1) - fauxhai (1.1.1) - httparty + faraday (0.8.9) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) net-ssh ohai - ffi (1.8.1) - foodcritic (2.1.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) - rak (~> 1.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.0.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.1) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - mime-types (1.23) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.4) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.1.0) - multi_json (1.7.6) - multi_xml (0.5.4) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) multipart-post (1.2.0) - net-http-persistent (2.8) - net-ssh (2.6.7) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - net-ssh-multi (1.1) - net-ssh (>= 2.1.4) - net-ssh-gateway (>= 0.99.0) - nokogiri (1.5.9) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.16.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) - rak (1.4) + rainbow (2.0.0) + rake (10.2.2) + rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (0.12.4) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) + buff-extensions (~> 0.3) + buff-ignore (~> 1.1) + buff-shell_out (~> 0.1) celluloid (~> 0.14.0) - chozo (>= 0.6.0) + celluloid-io (~> 0.14.0) erubis faraday (>= 0.8.4) hashie (>= 2.0.2) + json (>= 1.7.7) mixlib-authentication (>= 1.3.0) - mixlib-config (>= 1.1.0) - mixlib-log (>= 1.3.0) - mixlib-shellout (>= 1.1.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) + varia_model (~> 0.1) winrm (~> 1.1.0) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.1) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) savon (0.9.5) akami (~> 1.0) @@ -163,32 +196,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.4.4) - json - strainer (2.1.0) - berkshelf (~> 1.3) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) + berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - text-table (1.2.3) thor (0.18.1) - timers (1.1.0) - tins (0.8.0) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) uuidtools (2.1.4) + varia_model (0.3.2) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -198,10 +228,10 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 1.4.5) - chef (~> 11.4.4) - chefspec (~> 1.2.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - tailor diff --git a/chef/cookbooks/openstack-common/README.md b/chef/cookbooks/openstack-common/README.md old mode 100644 new mode 100755 index 641e596..bb6de36 --- a/chef/cookbooks/openstack-common/README.md +++ b/chef/cookbooks/openstack-common/README.md @@ -19,28 +19,26 @@ The following cookbooks are dependencies: Attributes ========== -Please see the extensive inline documentation in `attributes/default.rb` for descriptions +Please see the extensive inline documentation in `attributes/*.rb` for descriptions of all the settable attributes for this cookbook. Note that all attributes are in the `default["openstack"]` "namespace" -Libraries -========= +* `openstack['api']['auth']['version']` - Select v2.0 or v3.0. Default v2.0. The default auth API version used by other components to interact with identity service. -This cookbook exposes a set of default library routines: +default +------- -* `endpoint` -- Used to return a `::URI` object representing the named OpenStack endpoint -* `endpoints` -- Useful for operating on all OpenStack endpoints -* `db` -- Returns a Hash of information about a named OpenStack database -* `db_uri` -- Returns the SQLAlchemy RFC-1738 DB URI (see: http://rfc.net/rfc1738.html) for a named OpenStack database -* `db_create_with_user` -- Creates a database and database user for a named OpenStack database -* `secret` -- Returns the value of an encrypted data bag for a named OpenStack secret key and key-section -* `db_password` -- Ease-of-use helper that returns the decrypted database password for a named OpenStack database -* `service_password` -- Ease-of-use helper that returns the decrypted service password for named OpenStack service -* `user_password` -- Ease-of-use helper that returns the decrypted password for a Keystone user +Support multiple network types. Default network type is "nova" with the other option supported being "neutron". +The attribute is in the `default["openstack"]["compute"]["network"]["service_type"]`. -Usage ------ +Recipes +======= + +client +---- + +Install the common python openstack client package default ---- @@ -64,6 +62,58 @@ Installs/Configures common logging ] ``` +set_endpoints_by_interface +---- + +Iterates over the contents of the `node['openstack']['endpoints']` hash and +finds any occurrence of `bind_interface` to set the IP address +(`node['openstack']['endpoints']['identity']['bind_interface'] = 'eth0'` for +example, overriding `node['openstack']['endpoints']['identity']['host']`). If +`bind_interface` isn't set, the value of `host` is not modified. + +```json +"run_list": [ + "recipe[openstack-common::set_endpoints_by_interface]" +] +``` + +openrc +---- + +Creates an /root/openrc file. This requires the identity attributes for +admin_user and admin_tenant_name, or for the identity_service_chef_role +to be used on the identity server node. + + +sysctl +---- + +Iterates over the contents of the `node['openstack']['sysctl']` hash and writes +the entries to `/etc/sysctl.d/60-openstack.conf`. + +```json +"run_list": [ + "recipe[openstack-common::sysctl]" +] +``` + +Libraries +========= + +This cookbook exposes a set of default library routines: + +* `cli` -- Used to call openstack CLIs +* `endpoint` -- Used to return a `::URI` object representing the named OpenStack endpoint +* `endpoints` -- Useful for operating on all OpenStack endpoints +* `db` -- Returns a Hash of information about a named OpenStack database +* `db_uri` -- Returns the SQLAlchemy RFC-1738 DB URI (see: http://rfc.net/rfc1738.html) for a named OpenStack database +* `db_create_with_user` -- Creates a database and database user for a named OpenStack database +* `secret` -- Returns the value of an encrypted data bag for a named OpenStack secret key and key-section +* `get_password` -- Ease-of-use helper that returns the decrypted password for a named database, service or keystone user. + +Usage +----- + The following are code examples showing the above library routines in action. Remember when using the library routines exposed by this library to include the Openstack routines in your recipe's `::Chef::Recipe` namespace, like so: @@ -88,10 +138,10 @@ require "uri" puts ::URI.decode nova_api_ap.to_s ``` -Example of using the `db_password` and `db_uri` routine: +Example of using the `get_password` and `db_uri` routine: ```ruby -db_pass = db_password "cinder" +db_pass = get_password "db" "cinder" db_user = node["cinder"]["db"]["user"] sql_connection = db_uri "volume", db_user, db_pass @@ -150,15 +200,17 @@ in your recipe. Testing ===== -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. -Tests are defined in Strainerfile. +Berkshelf +===== -To run tests: - - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -171,11 +223,15 @@ License and Author | **Author** | Craig Tracey () | | **Author** | Sean Gallagher () | | **Author** | Ionut Artarisi () | +| **Author** | Chen Zhiwei () | +| **Author** | Brett Campbell () | | | | | **Copyright** | Copyright (c) 2012-2013, AT&T Services, Inc. | | **Copyright** | Copyright (c) 2013, Opscode, Inc. | | **Copyright** | Copyright (c) 2013, Craig Tracey | | **Copyright** | Copyright (c) 2013, SUSE Linux GmbH | +| **Copyright** | Copyright (c) 2013-2014, IBM, Corp. | +| **Copyright** | Copyright (c) 2013-2014, Rackspace US, Inc. | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/openstack-common/Strainerfile b/chef/cookbooks/openstack-common/Strainerfile index 7e292b4..44e3e14 100644 --- a/chef/cookbooks/openstack-common/Strainerfile +++ b/chef/cookbooks/openstack-common/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-common/TESTING.md b/chef/cookbooks/openstack-common/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-common/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-common/attributes/database.rb b/chef/cookbooks/openstack-common/attributes/database.rb new file mode 100644 index 0000000..9e98a1c --- /dev/null +++ b/chef/cookbooks/openstack-common/attributes/database.rb @@ -0,0 +1,188 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-common +# Attributes:: database +# +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# +# 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. +# + +# ======================== OpenStack DB Support ================================ +# +# This section of node attributes stores information about the database hosts +# used in an OpenStack deployment. +# +# There is no 'scheme' key. Instead, there is a 'service_type' key that should +# contain one of 'sqlite', 'mysql', 'db2' or 'postgresql' +# +# The ::Openstack::db() library routine allows a lookup from any recipe +# to this array, returning the host information for the server that contains +# the database for , where is one of 'compute' (Nova), +# 'image' (Glance), 'identity' (Keystone), 'network' (Neutron), or 'volume' (Cinder) +# +# The ::Openstack::db_connection(, , ) library routine +# returns the SQLAlchemy DB URI for , with the supplied user and password +# that a calling service might be using when connecting to the database. +# +# For example, let's assume that the database that is used by the OpenStack Identity +# service (Keystone) is configured as follows: +# +# host: 192.168.0.3 +# port: 3306 +# service_type: mysql +# db_name: keystone +# +# Further suppose that a node running the OpenStack Identity API service needs to +# connect to the above identity database server. It has the following in it's node +# attributes: +# +# node['openstack']['db']['identity']['username'] = 'keystone' +# +# In a 'keystone' recipe, you might find the following code: +# +# user = node['openstack']['db']['identity']['username'] +# pass = get_password 'db', 'keystone' +# +# sql_connection = ::Openstack::db_uri('identity', user, pass) +# +# The sql_connection variable would then be set to "mysql://keystone:password@192.168.0.3:keystone" +# and could then be written to the keystone.conf file in a template. +# +# Database Migrations: +# +# node['openstack']['db'][]['migrate'] +# +# The above attribute causes database migrations to be executed for the given +# service. There are cases where migrations should not be executed. For +# example when upgrading a zone, and the image or identity database are replicated +# across many zones. +# + +# ******************** Database Endpoint ************************************** +default['openstack']['endpoints']['db']['host'] = '127.0.0.1' +default['openstack']['endpoints']['db']['scheme'] = nil +default['openstack']['endpoints']['db']['port'] = '3306' +default['openstack']['endpoints']['db']['path'] = nil +default['openstack']['endpoints']['db']['bind_interface'] = nil + +# Default database attributes +default['openstack']['db']['server_role'] = 'os-ops-database' +default['openstack']['db']['service_type'] = 'mysql' +# Database connection options. Should include starting '?' +default['openstack']['db']['options'] = { + mysql: '?charset=utf8', + postgresql: '', + sqlite: '', + db2: '?charset=utf8', + nosql: '' +} + +# Database used by the OpenStack Compute (Nova) service +default['openstack']['db']['compute']['service_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['compute']['host'] = node['openstack']['endpoints']['db']['host'] +default['openstack']['db']['compute']['port'] = node['openstack']['endpoints']['db']['port'] +default['openstack']['db']['compute']['db_name'] = 'nova' +default['openstack']['db']['compute']['username'] = 'nova' +default['openstack']['db']['compute']['options'] = node['openstack']['db']['options'] + +# Database used by the OpenStack Identity (Keystone) service +default['openstack']['db']['identity']['service_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['identity']['host'] = node['openstack']['endpoints']['db']['host'] +default['openstack']['db']['identity']['port'] = node['openstack']['endpoints']['db']['port'] +default['openstack']['db']['identity']['db_name'] = 'keystone' +default['openstack']['db']['identity']['username'] = 'keystone' +default['openstack']['db']['identity']['migrate'] = true +default['openstack']['db']['identity']['options'] = node['openstack']['db']['options'] + +# Database used by the OpenStack Image (Glance) service +default['openstack']['db']['image']['service_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['image']['host'] = node['openstack']['endpoints']['db']['host'] +default['openstack']['db']['image']['port'] = node['openstack']['endpoints']['db']['port'] +default['openstack']['db']['image']['db_name'] = 'glance' +default['openstack']['db']['image']['username'] = 'glance' +default['openstack']['db']['image']['migrate'] = true +default['openstack']['db']['image']['options'] = node['openstack']['db']['options'] + +# Database used by the OpenStack Network (Neutron) service +default['openstack']['db']['network']['service_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['network']['host'] = node['openstack']['endpoints']['db']['host'] +default['openstack']['db']['network']['port'] = node['openstack']['endpoints']['db']['port'] +default['openstack']['db']['network']['db_name'] = 'neutron' +default['openstack']['db']['network']['username'] = 'neutron' +default['openstack']['db']['network']['options'] = node['openstack']['db']['options'] +# The SQLAlchemy connection string used to connect to the slave database +default['openstack']['db']['network']['slave_connection'] = '' +# Database reconnection retry times - in event connectivity is lost +default['openstack']['db']['network']['max_retries'] = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +default['openstack']['db']['network']['retry_interval'] = 10 +# Minimum number of SQL connections to keep open in a pool +default['openstack']['db']['network']['min_pool_size'] = 1 +# Maximum number of SQL connections to keep open in a pool +default['openstack']['db']['network']['max_pool_size'] = 100 +# Timeout in seconds before idle sql connections are reaped +default['openstack']['db']['network']['idle_timeout'] = 3600 +# If set, use this value for max_overflow with sqlalchemy +default['openstack']['db']['network']['max_overflow'] = 100 +# Verbosity of SQL debugging information. 0=None, 100=Everything +default['openstack']['db']['network']['connection_debug'] = 0 +# Add python stack traces to SQL as comment strings +default['openstack']['db']['network']['connection_trace'] = false +# If set, use this value for pool_timeout with sqlalchemy +default['openstack']['db']['network']['pool_timeout'] = 10 + +# Database used by the OpenStack Block Storage (Cinder) service +default['openstack']['db']['block-storage']['service_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['block-storage']['host'] = node['openstack']['endpoints']['db']['host'] +default['openstack']['db']['block-storage']['port'] = node['openstack']['endpoints']['db']['port'] +default['openstack']['db']['block-storage']['db_name'] = 'cinder' +default['openstack']['db']['block-storage']['username'] = 'cinder' +default['openstack']['db']['block-storage']['options'] = node['openstack']['db']['options'] + +# Database used by the OpenStack Dashboard (Horizon) +default['openstack']['db']['dashboard']['service_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['dashboard']['host'] = node['openstack']['endpoints']['db']['host'] +default['openstack']['db']['dashboard']['port'] = node['openstack']['endpoints']['db']['port'] +default['openstack']['db']['dashboard']['db_name'] = 'horizon' +default['openstack']['db']['dashboard']['username'] = 'dash' +default['openstack']['db']['dashboard']['migrate'] = true +default['openstack']['db']['dashboard']['options'] = node['openstack']['db']['options'] + +# Database used by OpenStack Metering (Ceilometer) +default['openstack']['db']['telemetry']['service_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['telemetry']['host'] = node['openstack']['endpoints']['db']['host'] +default['openstack']['db']['telemetry']['port'] = node['openstack']['endpoints']['db']['port'] +default['openstack']['db']['telemetry']['db_name'] = 'ceilometer' +default['openstack']['db']['telemetry']['username'] = 'ceilometer' +default['openstack']['db']['telemetry']['nosql']['used'] = false +default['openstack']['db']['telemetry']['nosql']['port'] = '27017' +default['openstack']['db']['telemetry']['options'] = node['openstack']['db']['options'] + +# Database used by OpenStack Orchestration (Heat) +default['openstack']['db']['orchestration']['service_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['orchestration']['host'] = node['openstack']['endpoints']['db']['host'] +default['openstack']['db']['orchestration']['port'] = node['openstack']['endpoints']['db']['port'] +default['openstack']['db']['orchestration']['db_name'] = 'heat' +default['openstack']['db']['orchestration']['username'] = 'heat' +default['openstack']['db']['orchestration']['options'] = node['openstack']['db']['options'] + +# Switch to store the MySQL root password in a databag instead of +# using the generated OpenSSL cookbook secure_password one. +default['openstack']['db']['root_user_use_databag'] = false + +# If above root_user_use_databag is true, the below string +# will be passed to the get_password library routine. +default['openstack']['db']['root_user_key'] = 'mysqlroot' diff --git a/chef/cookbooks/openstack-common/attributes/default.rb b/chef/cookbooks/openstack-common/attributes/default.rb index c84a1e7..4cff0f1 100644 --- a/chef/cookbooks/openstack-common/attributes/default.rb +++ b/chef/cookbooks/openstack-common/attributes/default.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-common # Attributes:: default @@ -18,6 +19,13 @@ # limitations under the License. # +# Set to some text value if you want templated config files +# to contain a custom banner at the top of the written file +default['openstack']['common']['custom_template_banner'] = ' +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +' + # Setting this to True means that database passwords and service user # passwords for Keystone will be easy-to-remember values -- they will be # the same value as the key. For instance, if a cookbook calls the @@ -26,14 +34,13 @@ # pass = secret "passwords", "nova" # # The value of pass will be "nova" -default["openstack"]["developer_mode"] = true +default['openstack']['developer_mode'] = false # The type of token signing to use (uuid or pki) -#default["openstack"]["auth"]["strategy"] = "uuid" -default["openstack"]["auth"]["strategy"] = "pki" +default['openstack']['auth']['strategy'] = 'pki' # Set to true where using self-signed certs (in testing environments) -default["openstack"]["auth"]["validate_certs"] = true +default['openstack']['auth']['validate_certs'] = true # ========================= Encrypted Databag Setup =========================== # @@ -41,21 +48,24 @@ default["openstack"]["auth"]["validate_certs"] = true # routine that looks up the value of encrypted databag values. This routine # uses the secret key file located at the following location to decrypt the # values in the data bag. -default["openstack"]["secret"]["key_path"] = "/etc/chef/openstack_data_bag_secret" +default['openstack']['secret']['key_path'] = '/etc/chef/openstack_data_bag_secret' + +# The name of the encrypted data bag that stores openstack secrets +default['openstack']['secret']['secrets_data_bag'] = 'secrets' # The name of the encrypted data bag that stores service user passwords, with # each key in the data bag corresponding to a named OpenStack service, like # "nova", "cinder", etc. -default["openstack"]["secret"]["service_passwords_data_bag"] = "service_passwords" +default['openstack']['secret']['service_passwords_data_bag'] = 'service_passwords' # The name of the encrypted data bag that stores DB passwords, with # each key in the data bag corresponding to a named OpenStack database, like # "nova", "cinder", etc. -default["openstack"]["secret"]["db_passwords_data_bag"] = "db_passwords" +default['openstack']['secret']['db_passwords_data_bag'] = 'db_passwords' # The name of the encrypted data bag that stores Keystone user passwords, with # each key in the data bag corresponding to a user (Keystone or otherwise). -default["openstack"]["secret"]["user_passwords_data_bag"] = "user_passwords" +default['openstack']['secret']['user_passwords_data_bag'] = 'user_passwords' # ========================= Package and Repository Setup ====================== # @@ -64,36 +74,26 @@ default["openstack"]["secret"]["user_passwords_data_bag"] = "user_passwords" # needs. # The coordinated release of OpenStack codename -default["openstack"]["release"] = "grizzly" +default['openstack']['release'] = 'icehouse' # The Ubuntu Cloud Archive has packages for multiple Ubuntu releases. For # more information, see: https://wiki.ubuntu.com/ServerTeam/CloudArchive. # In the component strings, %codename% will be replaced by the value of -# the node["lsb"]["codename"] Ohai value and %release% will be replaced -# by the value of node["openstack"]["release"] -default["openstack"]["apt"]["uri"] = "http://ubuntu-cloud.archive.canonical.com/ubuntu" -default["openstack"]["apt"]["components"] = [ "precise-updates/grizzly", "main" ] +# the node['lsb']['codename'] Ohai value and %release% will be replaced +# by the value of node['openstack']['release'] +default['openstack']['apt']['live_updates_enabled'] = true +default['openstack']['apt']['uri'] = 'http://ubuntu-cloud.archive.canonical.com/ubuntu' +default['openstack']['apt']['components'] = ["precise-updates/#{node['openstack']['release']}", 'main'] # For the SRU packaging, use this: -# default["openstack"]["apt"]["components"] = [ "%codename%-proposed/%release%", "main" ] - -default["openstack"]["zypp"]["repo-key"] = "05F4861F" # 32 bit key ID -default["openstack"]["zypp"]["uri"] = "http://download.opensuse.org/repositories/Cloud:/OpenStack:/%release%/%suse-release%/" - -#TODO(jaypipes): Do RHEL/Fedora platform family YUM setup -# EPEL repo -default["openstack"]["yum"]["epel"]["url"] = "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-#{node['platform_version'].to_i}&arch=$basearch" - -if node['platform_version'].to_i >= 6 - set["openstack"]["yum"]["epel"]["key"] = "RPM-GPG-KEY-EPEL-6" -else - set["openstack"]["yum"]["epel"]["key"] = "RPM-GPG-KEY-EPEL" -end -default["openstack"]["yum"]["epel"]["key_url"] = "http://download.fedoraproject.org/pub/epel/#{node['openstack']['yum']['epel']['key']}" - -# openstack repo -default["openstack"]["yum"]["openstack"]["url"]="http://repos.fedorapeople.org/repos/openstack/openstack-#{node['openstack']['release']}/epel-#{node['platform_version'].to_i}/" +# default['openstack']['apt']['components'] = [ '%codename%-proposed/%release%', 'main' ] +default['openstack']['zypp']['repo-key'] = 'd85f9316' # 32 bit key ID +default['openstack']['zypp']['uri'] = 'http://download.opensuse.org/repositories/Cloud:/OpenStack:/%release%/%suse-release%/' +default['openstack']['yum']['rdo_enabled'] = true +default['openstack']['yum']['uri'] = 'http://repos.fedorapeople.org/repos/openstack/openstack-icehouse/epel-6' +#default['openstack']['yum']['repo-key'] = 'https://raw.githubusercontent.com/redhat-openstack/rdo-release/master/RPM-GPG-KEY-RDO-Icehouse' +default['openstack']['yum']['repo-key'] = 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-RDO-Icehouse' # ======================== OpenStack Endpoints ================================ # @@ -117,298 +117,324 @@ default["openstack"]["yum"]["openstack"]["url"]="http://repos.fedorapeople.org/r # - host # - port # - path +# - bind_interface # # If the uri key is set, its value is used as the full URI for the endpoint. # If the uri key is not set, the endpoint's full URI is constructed from the # component parts. This allows setups that use some standardized DNS names for # OpenStack service endpoints in a deployment zone as well as setups that # instead assign IP addresses (for an actual node or a load balanced virtual -# IP) in a network to a particular OpenStack service endpoint. +# IP) in a network to a particular OpenStack service endpoint. If the +# bind_interface is set, it will set the host IP in the +# set_endpoints_by_interface recipe. # ******************** OpenStack Identity Endpoints *************************** +default['openstack']['endpoints']['host'] = '127.0.0.1' +default['openstack']['endpoints']['family'] = 'inet' + +# Note: The ['-bind'] for each service exist so that a user can +# have a service bind to a local IP per API node, that is different to the +# actual endpoint for that service, which may be a load balanced IP +default['openstack']['endpoints']['bind-host'] = '127.0.0.1' # The OpenStack Identity (Keystone) API endpoint. This is commonly called # the Keystone Service endpoint... -default['openstack']['endpoints']['identity-api']['host'] = "127.0.0.1" -default['openstack']['endpoints']['identity-api']['scheme'] = "http" -default['openstack']['endpoints']['identity-api']['port'] = "5000" -default['openstack']['endpoints']['identity-api']['path'] = "/v2.0" + +# NOTE(mancdaz): There is a single 'identity-bind' mash that is used +# by the identity cookbook, for both service and admin endpoint binds. +# This is because keystone presents two ports but only a single service, +# that can only be bound to a single IP. +default['openstack']['endpoints']['identity-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['identity-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['identity-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['identity-api']['scheme'] = 'http' +default['openstack']['endpoints']['identity-api']['port'] = '5000' +default['openstack']['endpoints']['identity-api']['path'] = '/v2.0' +default['openstack']['endpoints']['identity-api']['bind_interface'] = nil # The OpenStack Identity (Keystone) Admin API endpoint -default['openstack']['endpoints']['identity-admin']['host'] = "127.0.0.1" -default['openstack']['endpoints']['identity-admin']['scheme'] = "http" -default['openstack']['endpoints']['identity-admin']['port'] = "35357" -default['openstack']['endpoints']['identity-admin']['path'] = "/v2.0" +default['openstack']['endpoints']['identity-admin']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['identity-admin']['scheme'] = 'http' +default['openstack']['endpoints']['identity-admin']['port'] = '35357' +default['openstack']['endpoints']['identity-admin']['path'] = '/v2.0' +default['openstack']['endpoints']['identity-admin']['bind_interface'] = nil # ****************** OpenStack Compute Endpoints ****************************** # The OpenStack Compute (Nova) Native API endpoint -default['openstack']['endpoints']['compute-api']['host'] = "127.0.0.1" -default['openstack']['endpoints']['compute-api']['scheme'] = "http" -default['openstack']['endpoints']['compute-api']['port'] = "8774" -default['openstack']['endpoints']['compute-api']['path'] = "/v2/%(tenant_id)s" +default['openstack']['endpoints']['compute-api-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['compute-api-bind']['port'] = '8774' +default['openstack']['endpoints']['compute-api-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['compute-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['compute-api']['scheme'] = 'http' +default['openstack']['endpoints']['compute-api']['port'] = '8774' +default['openstack']['endpoints']['compute-api']['path'] = '/v2/%(tenant_id)s' +default['openstack']['endpoints']['compute-api']['bind_interface'] = nil # The OpenStack Compute (Nova) EC2 API endpoint -default['openstack']['endpoints']['compute-ec2-api']['host'] = "127.0.0.1" -default['openstack']['endpoints']['compute-ec2-api']['scheme'] = "http" -default['openstack']['endpoints']['compute-ec2-api']['port'] = "8773" -default['openstack']['endpoints']['compute-ec2-api']['path'] = "/services/Cloud" +default['openstack']['endpoints']['compute-ec2-api-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['compute-ec2-api-bind']['port'] = '8773' +default['openstack']['endpoints']['compute-ec2-api-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['compute-ec2-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['compute-ec2-api']['scheme'] = 'http' +default['openstack']['endpoints']['compute-ec2-api']['port'] = '8773' +default['openstack']['endpoints']['compute-ec2-api']['path'] = '/services/Cloud' +default['openstack']['endpoints']['compute-ec2-api']['bind_interface'] = nil # The OpenStack Compute (Nova) EC2 Admin API endpoint -default['openstack']['endpoints']['compute-ec2-admin']['host'] = "127.0.0.1" -default['openstack']['endpoints']['compute-ec2-admin']['scheme'] = "http" -default['openstack']['endpoints']['compute-ec2-admin']['port'] = "8773" -default['openstack']['endpoints']['compute-ec2-admin']['path'] = "/services/Admin" +default['openstack']['endpoints']['compute-ec2-admin-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['compute-ec2-admin-bind']['port'] = '8773' +default['openstack']['endpoints']['compute-ec2-admin-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['compute-ec2-admin']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['compute-ec2-admin']['scheme'] = 'http' +default['openstack']['endpoints']['compute-ec2-admin']['port'] = '8773' +default['openstack']['endpoints']['compute-ec2-admin']['path'] = '/services/Admin' +default['openstack']['endpoints']['compute-ec2-admin']['bind_interface'] = nil # The OpenStack Compute (Nova) XVPvnc endpoint -default['openstack']['endpoints']['compute-xvpvnc']['host'] = "127.0.0.1" -default['openstack']['endpoints']['compute-xvpvnc']['scheme'] = "http" -default['openstack']['endpoints']['compute-xvpvnc']['port'] = "6081" -default['openstack']['endpoints']['compute-xvpvnc']['path'] = "/console" +default['openstack']['endpoints']['compute-xvpvnc-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['compute-xvpvnc-bind']['port'] = '6081' +default['openstack']['endpoints']['compute-xvpvnc-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['compute-xvpvnc']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['compute-xvpvnc']['scheme'] = 'http' +default['openstack']['endpoints']['compute-xvpvnc']['port'] = '6081' +default['openstack']['endpoints']['compute-xvpvnc']['path'] = '/console' +default['openstack']['endpoints']['compute-xvpvnc']['bind_interface'] = nil # The OpenStack Compute (Nova) novnc endpoint -default['openstack']['endpoints']['compute-novnc']['host'] = "127.0.0.1" -default['openstack']['endpoints']['compute-novnc']['scheme'] = "http" -default['openstack']['endpoints']['compute-novnc']['port'] = "6080" -default['openstack']['endpoints']['compute-novnc']['path'] = "/vnc_auto.html" +default['openstack']['endpoints']['compute-novnc-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['compute-novnc-bind']['port'] = '6080' +default['openstack']['endpoints']['compute-novnc-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['compute-novnc']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['compute-novnc']['scheme'] = 'http' +default['openstack']['endpoints']['compute-novnc']['port'] = '6080' +default['openstack']['endpoints']['compute-novnc']['path'] = '/vnc_auto.html' +default['openstack']['endpoints']['compute-novnc']['bind_interface'] = nil + +# The OpenStack Compute (Nova) vnc endpoint +default['openstack']['endpoints']['compute-vnc-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['compute-vnc-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['compute-vnc']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['compute-vnc']['scheme'] = nil +default['openstack']['endpoints']['compute-vnc']['port'] = nil +default['openstack']['endpoints']['compute-vnc']['path'] = nil +default['openstack']['endpoints']['compute-vnc']['bind_interface'] = nil # ******************** OpenStack Network Endpoints **************************** -# The OpenStack Network (Quantum) API endpoint. -default['openstack']['endpoints']['network-api']['host'] = "127.0.0.1" -default['openstack']['endpoints']['network-api']['scheme'] = "http" -default['openstack']['endpoints']['network-api']['port'] = "9696" -# quantumclient appends the protocol version to the endpoint URL, so the +# The OpenStack Network (Neutron) API endpoint. +default['openstack']['endpoints']['network-api-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['network-api-bind']['port'] = '9696' +default['openstack']['endpoints']['network-api-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['network-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['network-api']['scheme'] = 'http' +default['openstack']['endpoints']['network-api']['port'] = '9696' +# neutronclient appends the protocol version to the endpoint URL, so the # path needs to be empty -default['openstack']['endpoints']['network-api']['path'] = "" +default['openstack']['endpoints']['network-api']['path'] = '' +default['openstack']['endpoints']['network-api']['bind_interface'] = nil + +# The OpenStack Network Linux Bridge endpoint +default['openstack']['endpoints']['network-linuxbridge']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['network-linuxbridge']['scheme'] = nil +default['openstack']['endpoints']['network-linuxbridge']['port'] = nil +default['openstack']['endpoints']['network-linuxbridge']['path'] = nil +default['openstack']['endpoints']['network-linuxbridge']['bind_interface'] = nil + +# The OpenStack Network Open vSwitch endpoint +default['openstack']['endpoints']['network-openvswitch']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['network-openvswitch']['scheme'] = nil +default['openstack']['endpoints']['network-openvswitch']['port'] = nil +default['openstack']['endpoints']['network-openvswitch']['path'] = nil +default['openstack']['endpoints']['network-openvswitch']['bind_interface'] = nil # ******************** OpenStack Image Endpoints ****************************** # The OpenStack Image (Glance) API endpoint -default['openstack']['endpoints']['image-api']['host'] = "127.0.0.1" -default['openstack']['endpoints']['image-api']['scheme'] = "http" -default['openstack']['endpoints']['image-api']['port'] = "9292" -default['openstack']['endpoints']['image-api']['path'] = "/v2" +default['openstack']['endpoints']['image-api-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['image-api-bind']['port'] = '9292' +default['openstack']['endpoints']['image-api-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['image-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['image-api']['scheme'] = 'http' +default['openstack']['endpoints']['image-api']['port'] = '9292' +# The glance client appends the protocol version to the endpoint URL, +# so the path needs to be empty +default['openstack']['endpoints']['image-api']['path'] = '' +default['openstack']['endpoints']['image-api']['bind_interface'] = nil # The OpenStack Image (Glance) Registry API endpoint -default['openstack']['endpoints']['image-registry']['host'] = "127.0.0.1" -default['openstack']['endpoints']['image-registry']['scheme'] = "http" -default['openstack']['endpoints']['image-registry']['port'] = "9191" -default['openstack']['endpoints']['image-registry']['path'] = "/v2" +default['openstack']['endpoints']['image-registry-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['image-registry-bind']['port'] = '9191' +default['openstack']['endpoints']['image-registry-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['image-registry']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['image-registry']['scheme'] = 'http' +default['openstack']['endpoints']['image-registry']['port'] = '9191' +default['openstack']['endpoints']['image-registry']['path'] = '/v2' +default['openstack']['endpoints']['image-registry']['bind_interface'] = nil # ******************** OpenStack Volume Endpoints ***************************** # The OpenStack Volume (Cinder) API endpoint -default['openstack']['endpoints']['volume-api']['host'] = "127.0.0.1" -default['openstack']['endpoints']['volume-api']['scheme'] = "http" -default['openstack']['endpoints']['volume-api']['port'] = "8776" -default['openstack']['endpoints']['volume-api']['path'] = "/v1/%(tenant_id)s" +default['openstack']['endpoints']['block-storage-api-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['block-storage-api-bind']['port'] = '8776' +default['openstack']['endpoints']['block-storage-api-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['block-storage-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['block-storage-api']['scheme'] = 'http' +default['openstack']['endpoints']['block-storage-api']['port'] = '8776' +default['openstack']['endpoints']['block-storage-api']['path'] = '/v1/%(tenant_id)s' +default['openstack']['endpoints']['block-storage-api']['bind_interface'] = nil + +# ******************** OpenStack Object Storage Endpoint ***************************** + +# The OpenStack Object Storage (Swift) API endpoint +default['openstack']['endpoints']['object-storage-api-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['object-storage-api-bind']['port'] = '8080' +default['openstack']['endpoints']['object-storage-api-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['object-storage-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['object-storage-api']['scheme'] = 'http' +default['openstack']['endpoints']['object-storage-api']['port'] = '8080' +default['openstack']['endpoints']['object-storage-api']['path'] = '/v1/' +default['openstack']['endpoints']['object-storage-api']['bind_interface'] = nil # ******************** OpenStack Metering Endpoints *************************** # The OpenStack Metering (Ceilometer) API endpoint -default['openstack']['endpoints']['metering-api']['host'] = "127.0.0.1" -default['openstack']['endpoints']['metering-api']['scheme'] = "http" -default['openstack']['endpoints']['metering-api']['port'] = "8777" -default['openstack']['endpoints']['metering-api']['path'] = "/v1" +default['openstack']['endpoints']['telemetry-api-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['telemetry-api-bind']['port'] = '8777' +default['openstack']['endpoints']['telemetry-api-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['telemetry-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['telemetry-api']['scheme'] = 'http' +default['openstack']['endpoints']['telemetry-api']['port'] = '8777' +# The ceilometer client appends the protocol version to the endpoint URL, +# so the path needs to be empty +default['openstack']['endpoints']['telemetry-api']['path'] = '' +default['openstack']['endpoints']['telemetry-api']['bind_interface'] = nil + +# ******************** OpenStack Orchestration Endpoints *************************** + +# The OpenStack Orchestration (Heat) API endpoint +default['openstack']['endpoints']['orchestration-api-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['orchestration-api-bind']['port'] = '8004' +default['openstack']['endpoints']['orchestration-api-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['orchestration-api']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['orchestration-api']['scheme'] = 'http' +default['openstack']['endpoints']['orchestration-api']['port'] = '8004' +default['openstack']['endpoints']['orchestration-api']['path'] = '/v1/%(tenant_id)s' +default['openstack']['endpoints']['orchestration-api']['bind_interface'] = nil + +# The OpenStack Orchestration (Heat) CloudFormation API endpoint +default['openstack']['endpoints']['orchestration-api-cfn-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['orchestration-api-cfn-bind']['port'] = '8000' +default['openstack']['endpoints']['orchestration-api-cfn-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['orchestration-api-cfn']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['orchestration-api-cfn']['scheme'] = 'http' +default['openstack']['endpoints']['orchestration-api-cfn']['port'] = '8000' +default['openstack']['endpoints']['orchestration-api-cfn']['path'] = '/v1' +default['openstack']['endpoints']['orchestration-api-cfn']['bind_interface'] = nil + +# The OpenStack Orchestration (Heat) CloudWatch API endpoint +default['openstack']['endpoints']['orchestration-api-cloudwatch-bind']['host'] = node['openstack']['endpoints']['bind-host'] +default['openstack']['endpoints']['orchestration-api-cloudwatch-bind']['port'] = '8003' +default['openstack']['endpoints']['orchestration-api-cloudwatch-bind']['bind_interface'] = nil + +default['openstack']['endpoints']['orchestration-api-cloudwatch']['host'] = node['openstack']['endpoints']['host'] +default['openstack']['endpoints']['orchestration-api-cloudwatch']['scheme'] = 'http' +default['openstack']['endpoints']['orchestration-api-cloudwatch']['port'] = '8003' +default['openstack']['endpoints']['orchestration-api-cloudwatch']['path'] = '/v1' +default['openstack']['endpoints']['orchestration-api-cloudwatch']['bind_interface'] = nil # Alternately, if you used some standardized DNS naming scheme, you could # do something like this, which would override any part-wise specifications above. # -# default['openstack']['endpoints']['identity-api']['uri'] = "https://identity.example.com:35357/v2.0" -# default['openstack']['endpoints']['identity-admin']['uri'] = "https://identity.example.com:5000/v2.0" -# default['openstack']['endpoints']['compute-api']['uri'] = "https://compute.example.com:8774/v2/%(tenant_id)s" -# default['openstack']['endpoints']['compute-ec2-api']['uri'] = "https://ec2.example.com:8773/services/Cloud" -# default['openstack']['endpoints']['compute-ec2-admin']['uri'] = "https://ec2.example.com:8773/services/Admin" -# default['openstack']['endpoints']['compute-xvpvnc']['uri'] = "https://xvpvnc.example.com:6081/console" -# default['openstack']['endpoints']['compute-novnc']['uri'] = "https://novnc.example.com:6080/vnc_auto.html" -# default['openstack']['endpoints']['image-api']['uri'] = "https://image.example.com:9292/v2" -# default['openstack']['endpoints']['image-registry']['uri'] = "https://image.example.com:9191/v2" -# default['openstack']['endpoints']['volume-api']['uri'] = "https://volume.example.com:8776/v1/%(tenant_id)s" -# default['openstack']['endpoints']['metering-api']['uri'] = "https://metering.example.com:9000/v1" +# default['openstack']['endpoints']['identity-api']['uri'] = 'https://identity.example.com:35357/v2.0' +# default['openstack']['endpoints']['identity-admin']['uri'] = 'https://identity.example.com:5000/v2.0' +# default['openstack']['endpoints']['compute-api']['uri'] = 'https://compute.example.com:8774/v2/%(tenant_id)s' +# default['openstack']['endpoints']['compute-ec2-api']['uri'] = 'https://ec2.example.com:8773/services/Cloud' +# default['openstack']['endpoints']['compute-ec2-admin']['uri'] = 'https://ec2.example.com:8773/services/Admin' +# default['openstack']['endpoints']['compute-xvpvnc']['uri'] = 'https://xvpvnc.example.com:6081/console' +# default['openstack']['endpoints']['compute-novnc']['uri'] = 'https://novnc.example.com:6080/vnc_auto.html' +# default['openstack']['endpoints']['image-api']['uri'] = 'https://image.example.com:9292/v2' +# default['openstack']['endpoints']['image-registry']['uri'] = 'https://image.example.com:9191/v2' +# default['openstack']['endpoints']['block-storage-api']['uri'] = 'https://volume.example.com:8776/v1/%(tenant_id)s' +# default['openstack']['endpoints']['telemetry-api']['uri'] = 'https://telemetry.example.com:9000/v1' +# default['openstack']['endpoints']['orchestration-api']['uri'] = 'https://orchestration.example.com:8004//v1/%(tenant_id)s' +# default['openstack']['endpoints']['orchestration-api-cfn']['uri'] = 'https://orchestration.example.com:8000/v1' +# default['openstack']['endpoints']['orchestration-api-cloudwatch']['uri'] = 'https://orchestration.example.com:8003/v1' -# ======================== OpenStack DB Support ================================ -# -# This section of node attributes stores information about the database hosts -# used in an OpenStack deployment. -# -# There is no 'scheme' key. Instead, there is a 'db_type' key that should -# contain one of 'sqlite', 'mysql', or 'postgresql' -# -# The ::Openstack::db() library routine allows a lookup from any recipe -# to this array, returning the host information for the server that contains -# the database for , where is one of 'compute' (Nova), -# 'image' (Glance), 'identity' (Keystone), 'network' (Quantum), or 'volume' (Cinder) -# -# The ::Openstack::db_connection(, , ) library routine -# returns the SQLAlchemy DB URI for , with the supplied user and password -# that a calling service might be using when connecting to the database. -# -# For example, let's assume that the database that is used by the OpenStack Identity -# service (Keystone) is configured as follows: -# -# host: 192.168.0.3 -# port: 3306 -# db_type: mysql -# db_name: keystone -# -# Further suppose that a node running the OpenStack Identity API service needs to -# connect to the above identity database server. It has the following in it's node -# attributes: -# -# node['db']['user'] = 'keystone' -# -# In a "keystone" recipe, you might find the following code: -# -# user = node['db']['user'] -# pass = secret 'passwords', 'keystone' -# -# sql_connection = ::Openstack::db_uri('identity', user, pass) -# -# The sql_connection variable would then be set to "mysql://keystone:password@192.168.0.3:keystone" -# and could then be written to the keystone.conf file in a template. +# Set a default region that other regions are set to - such that changing the region for all services can be done in one place +default['openstack']['region'] = 'RegionOne' -# Default database attributes -default['openstack']['db']['server_role'] = "os-ops-database" -default['openstack']['db']['service_type'] = "mysql" -default['openstack']['db']['port'] = "3306" - -# tenant and user -default['openstack']['identity']['admin_token'] = "openstack_identity_bootstrap_token" - -default['openstack']['identity']['admin_tenant_name'] = "admin" -default['openstack']['identity']['admin_user'] = "admin" -default['openstack']['identity']['admin_password'] = "admin" -default['openstack']['identity']['roles']['admin'] = "admin" -default['openstack']['identity']['roles']['member'] = "Member" - -# define enable services -default['openstack']['services'] = { - "compute" => { - "name" => "nova", - "status" => "enable" - }, - "network" => { - "name" => "quantum", - "status" => "enable" - }, - "volume" => { - "name" => "cinder", - "status" => "enable" - }, - "identity" => { - "name" => "keystone", - "status" => "enable" - }, - "image" => { - "name" => "glance", - "status" => "enable" - }, - "object-store" => { - "name" => "swift", - "status" => "disable" - } - } - -# define service users at keystone -default['openstack']['identity']['compute']['username'] = "nova" -default['openstack']['identity']['compute']['password'] = "admin" -default['openstack']['identity']['compute']['role'] = default['openstack']['identity']['roles']['admin'] - -default['openstack']['identity']['network']['username'] = "quantum" -default['openstack']['identity']['network']['password'] = "admin" -default['openstack']['identity']['network']['role'] = default['openstack']['identity']['roles']['admin'] - -default['openstack']['identity']['volume']['username'] = "cinder" -default['openstack']['identity']['volume']['password'] = "admin" -default['openstack']['identity']['volume']['role'] = default['openstack']['identity']['roles']['admin'] - -default['openstack']['identity']['image']['username'] = "glance" -default['openstack']['identity']['image']['password'] = "admin" -default['openstack']['identity']['image']['role'] = default['openstack']['identity']['roles']['admin'] - -default['openstack']['identity']['objectstorage']['username'] = "swift" -default['openstack']['identity']['objectstorage']['password'] = "admin" -default['openstack']['identity']['objectstorage']['role'] = default['openstack']['identity']['roles']['admin'] - - -# Database used by the OpenStack Compute (Nova) service -default['openstack']['db']['compute']['db_type'] = node['openstack']['db']['service_type'] -default['openstack']['db']['compute']['host'] = "127.0.0.1" -default['openstack']['db']['compute']['port'] = node['openstack']['db']['port'] -default['openstack']['db']['compute']['db_name'] = "nova" -default['openstack']['db']['compute']['username'] = "nova" -default['openstack']['db']['compute']['password'] = "admin" - - -# Database used by the OpenStack Identity (Keystone) service -default['openstack']['db']['identity']['db_type'] = node['openstack']['db']['service_type'] -default['openstack']['db']['identity']['host'] = "127.0.0.1" -default['openstack']['db']['identity']['port'] = node['openstack']['db']['port'] -default['openstack']['db']['identity']['db_name'] = "keystone" -default['openstack']['db']['identity']['username'] = "keystone" -default['openstack']['db']['identity']['password'] = "admin" - - -# Database used by the OpenStack Image (Glance) service -default['openstack']['db']['image']['db_type'] = node['openstack']['db']['service_type'] -default['openstack']['db']['image']['host'] = "127.0.0.1" -default['openstack']['db']['image']['port'] = node['openstack']['db']['port'] -default['openstack']['db']['image']['db_name'] = "glance" -default['openstack']['db']['image']['username'] = "glance" -default['openstack']['db']['image']['password'] = "admin" - -# Database used by the OpenStack Network (Quantum) service -default['openstack']['db']['network']['db_type'] = node['openstack']['db']['service_type'] -default['openstack']['db']['network']['host'] = "127.0.0.1" -default['openstack']['db']['network']['port'] = node['openstack']['db']['port'] -default['openstack']['db']['network']['db_name'] = "quantum" -default['openstack']['db']['network']['username'] = "quantum" -default['openstack']['db']['network']['password'] = "admin" - -# Database used by the OpenStack Volume (Cinder) service -default['openstack']['db']['volume']['db_type'] = node['openstack']['db']['service_type'] -default['openstack']['db']['volume']['host'] = "127.0.0.1" -default['openstack']['db']['volume']['port'] = node['openstack']['db']['port'] -default['openstack']['db']['volume']['db_name'] = "cinder" -default['openstack']['db']['volume']['username'] = "cinder" -default['openstack']['db']['volume']['password'] = "admin" - -# Database used by the OpenStack Dashboard (Horizon) -default['openstack']['db']['dashboard']['db_type'] = node['openstack']['db']['service_type'] -default['openstack']['db']['dashboard']['host'] = "127.0.0.1" -default['openstack']['db']['dashboard']['port'] = node['openstack']['db']['port'] -default['openstack']['db']['dashboard']['db_name'] = "horizon" -default['openstack']['db']['dashboard']['username'] = "horizon" -default['openstack']['db']['dashboard']['password'] = "admin" - -# Database used by OpenStack Metering (Ceilometer) -default['openstack']['db']['metering']['db_type'] = node['openstack']['db']['service_type'] -default['openstack']['db']['metering']['host'] = "127.0.0.1" -default['openstack']['db']['metering']['port'] = node['openstack']['db']['port'] -default['openstack']['db']['metering']['db_name'] = "ceilometer" -default['openstack']['db']['metering']['username'] = "ceilometer" -default['openstack']['db']['metering']['password'] = "admin" - - -# Switch to store the MySQL root password in a databag instead of -# using the generated OpenSSL cookbook secure_password one. -default['openstack']['db']['root_user_use_databag'] = false - -# If above root_user_use_databag is true, the below string -# will be passed to the user_password library routine. -default['openstack']['db']['root_user_key'] = 'mysqlroot' +# Set a default auth api version that other components use to interact with identity service. +# Allowed auth API versions: v2.0 or v3.0. By default, it is set to v2.0. +default['openstack']['api']['auth']['version'] = 'v2.0' # logging.conf list keypairs module_name => log level to write -default['openstack']['logging']['ignore'] = {'nova.api.openstack.wsgi' => 'WARNING', - 'nova.osapi_compute.wsgi.server' => 'WARNING'} +default['openstack']['logging']['ignore'] = { 'nova.api.openstack.wsgi' => 'WARNING', + 'nova.osapi_compute.wsgi.server' => 'WARNING' } default['openstack']['memcached_servers'] = nil -# Default database attributes -default["openstack"]["mq"]["server_role"] = "os-ops-messaging" -default["openstack"]["mq"]["service_type"] = "rabbitmq" -default["openstack"]["mq"]["bind_address"] = "0.0.0.0" -default["openstack"]["mq"]["port"] = "5672" -default["openstack"]["mq"]["user"] = "guest" -default["openstack"]["mq"]["vhost"] = "/" +# Default sysctl settings +default['openstack']['sysctl']['net.ipv4.conf.all.rp_filter'] = 0 +default['openstack']['sysctl']['net.ipv4.conf.default.rp_filter'] = 0 + +# Default Ceph settings +default['openstack']['ceph']['key-url'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' + +# Default OpenStack Network Type: nova (optional: neutron) +default['openstack']['compute']['network']['service_type'] = 'nova' + +case platform_family +when 'debian' + default['openstack']['ceph']['platform']['uri'] = 'http://ceph.com/debian-emperor' +when 'fedora', 'rhel', 'suse' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['ceph']['platform']['uri'] = 'http://ceph.com/rpm-emperor' +end + +# Overriding this makes the ceph_client recipe do nothing +# (set this when using the ceph/ceph-cookbooks cookbook). +default['openstack']['ceph']['setup_client'] = true + +default['openstack']['ceph']['global'] = { + fsid: '00000000-0000-0000-0000-000000000000', + mon_initial_members: [], + mon_host: [], + auth_cluster_required: 'cephx', + auth_service_required: 'cephx', + auth_client_required: 'cephx', + filestore_xattr_use_omap: true +} + +case node['platform_family'] +when 'rhel', 'suse' + default['openstack']['common']['platform'] = { + 'common_client_packages' => ['python-openstackclient'], + 'package_overrides' => '' + } +when 'debian' + default['openstack']['common']['platform'] = { + 'common_client_packages' => ['python-openstackclient'], + 'package_overrides' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } +end + +# The name of the Chef role that installs the Keystone Service API +default['openstack']['identity_service_chef_role'] = 'os-identity' + +# Array of bare options for openrc (e.g. 'option=value') +default['openstack']['misc_openrc'] = nil diff --git a/chef/cookbooks/openstack-common/attributes/messaging.rb b/chef/cookbooks/openstack-common/attributes/messaging.rb new file mode 100644 index 0000000..4176504 --- /dev/null +++ b/chef/cookbooks/openstack-common/attributes/messaging.rb @@ -0,0 +1,139 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-common +# Attributes:: messaging +# +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# Copyright 2013-2014, Rackspace US, Inc. +# +# 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. +# +# The rabbitmq user's password is stored in an encrypted databag and accessed +# with openstack-common cookbook library's user_password routine. You are +# expected to create the user, pass, vhost in a wrapper rabbitmq cookbook. +# + +# ******************** RabbitMQ Endpoint ************************************** +default['openstack']['endpoints']['mq']['host'] = '127.0.0.1' +default['openstack']['endpoints']['mq']['scheme'] = nil +default['openstack']['endpoints']['mq']['port'] = '5672' +default['openstack']['endpoints']['mq']['path'] = nil +default['openstack']['endpoints']['mq']['bind_interface'] = nil + +################################################################### +# Services to assign mq attributes for +################################################################### +services = %w{block-storage compute image telemetry network orchestration} + +################################################################### +# Generic default attributes +################################################################### +default['openstack']['mq']['server_role'] = 'os-ops-messaging' +default['openstack']['mq']['service_type'] = 'rabbitmq' +default['openstack']['mq']['user'] = 'guest' +default['openstack']['mq']['vhost'] = '/' + +# defined in oslo/messaging/_drivers/amqp.py +default['openstack']['mq']['durable_queues'] = false +default['openstack']['mq']['auto_delete'] = false + +################################################################### +# Default qpid and rabbit values (for attribute assignment below) +################################################################### +default['openstack']['mq']['qpid']['protocol'] = 'tcp' +# defined in oslo/messaging/_drivers/impl_qpid.py +default['openstack']['mq']['qpid']['topology_version'] = 1 +qpid_defaults = { + username: node['openstack']['mq']['user'], + sasl_mechanisms: '', + reconnect: true, + reconnect_timeout: 0, + reconnect_limit: 0, + reconnect_interval_min: 0, + reconnect_interval_max: 0, + reconnect_interval: 0, + heartbeat: 60, + protocol: node['openstack']['mq']['qpid']['protocol'], + tcp_nodelay: true, + host: node['openstack']['endpoints']['mq']['host'], + port: node['openstack']['endpoints']['mq']['port'], + qpid_hosts: ["#{node['openstack']['endpoints']['mq']['host']}:#{node['openstack']['endpoints']['mq']['port']}"], + topology_version: node['openstack']['mq']['qpid']['topology_version'] +} + +rabbit_defaults = { + userid: node['openstack']['mq']['user'], + vhost: node['openstack']['mq']['vhost'], + port: node['openstack']['endpoints']['mq']['port'], + host: node['openstack']['endpoints']['mq']['host'], + ha: false, + use_ssl: false +} + +################################################################### +# Assign default mq attributes for every service +################################################################### +services.each do |svc| + default['openstack']['mq'][svc]['service_type'] = node['openstack']['mq']['service_type'] + default['openstack']['mq'][svc]['notification_topic'] = 'notifications' + + default['openstack']['mq'][svc]['durable_queues'] = + node['openstack']['mq']['durable_queues'] + default['openstack']['mq'][svc]['auto_delete'] = + node['openstack']['mq']['auto_delete'] + + case node['openstack']['mq'][svc]['service_type'] + when 'qpid' + qpid_defaults.each do |key, val| + default['openstack']['mq'][svc]['qpid'][key.to_s] = val + end + when 'rabbitmq' + rabbit_defaults.each do |key, val| + default['openstack']['mq'][svc]['rabbit'][key.to_s] = val + end + end +end + +################################################################### +# Overrides and additional attributes for individual services +################################################################### +# block-storage +default['openstack']['mq']['block-storage']['qpid']['notification_topic'] = + node['openstack']['mq']['block-storage']['notification_topic'] +default['openstack']['mq']['block-storage']['rabbit']['notification_topic'] = + node['openstack']['mq']['block-storage']['notification_topic'] +default['openstack']['mq']['block-storage']['control_exchange'] = 'cinder' + +# image +default['openstack']['mq']['image']['notifier_strategy'] = 'noop' +default['openstack']['mq']['image']['notification_topic'] = 'glance_notifications' +default['openstack']['mq']['image']['qpid']['notification_topic'] = + node['openstack']['mq']['image']['notification_topic'] +default['openstack']['mq']['image']['rabbit']['notification_topic'] = + node['openstack']['mq']['image']['notification_topic'] +default['openstack']['mq']['image']['control_exchange'] = 'glance' + +# network +# AMQP topics used for openstack notifications, can be comma-separated values +default['openstack']['mq']['network']['notification_topics'] = 'notifications' +default['openstack']['mq']['network']['control_exchange'] = 'neutron' + +# compute +default['openstack']['mq']['compute']['control_exchange'] = 'nova' + +# orchestration +default['openstack']['mq']['orchestration']['control_exchange'] = 'heat' + +# telemetry +default['openstack']['mq']['telemetry']['control_exchange'] = 'ceilometer' diff --git a/chef/cookbooks/openstack-common/files/default/RPM-GPG-KEY-RDO-Icehouse b/chef/cookbooks/openstack-common/files/default/RPM-GPG-KEY-RDO-Icehouse new file mode 100644 index 0000000..459b84e --- /dev/null +++ b/chef/cookbooks/openstack-common/files/default/RPM-GPG-KEY-RDO-Icehouse @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.11 (GNU/Linux) + +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 +5KmnPi+m +=6cHR +-----END PGP PUBLIC KEY BLOCK----- diff --git a/chef/cookbooks/openstack-common/libraries/cli.rb b/chef/cookbooks/openstack-common/libraries/cli.rb new file mode 100755 index 0000000..9a752c2 --- /dev/null +++ b/chef/cookbooks/openstack-common/libraries/cli.rb @@ -0,0 +1,90 @@ +# encoding: UTF-8 + +# +# Cookbook Name:: openstack-common +# library:: cli +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +require 'chef/mixin/shell_out' +include Chef::Mixin::ShellOut +require 'uri' + +module ::Openstack # rubocop:disable Documentation + # return an environment suitable for calling openstack commands. + # + # @param [String] user name + # @param [String] tenant name + # @return [Hash] environment + def openstack_command_env(name, tenant) + identity_admin_endpoint = endpoint 'identity-admin' + auth_uri = ::URI.decode identity_admin_endpoint.to_s + pass = get_password 'user', name + { + 'OS_USERNAME' => name, + 'OS_PASSWORD' => pass, + 'OS_TENANT_NAME' => tenant, + 'OS_AUTH_URL' => auth_uri + } + end + + # return stdout from calling an openstack command. + # + # @param [String] command to run + # @param [String] command options + # @param [Hash] environment to use + # @param [Hash] optional command argument/values pairs + # @return [String] stdout or fail + # + # TODO: this was taken from the identity register provider, will need to + # update the provider to use this. + # + def openstack_command(cmd, options = '', env = {}, args = {}) + openstackcmd = [cmd] << options + args.each do |key, val| + openstackcmd << "--#{key}" << val.to_s + end + Chef::Log.debug("Running openstack command: #{openstackcmd} with environment: #{env}") + result = shell_out(openstackcmd, env: env) + fail "#{result.stderr} (#{result.exitstatus})" if result.exitstatus != 0 + result.stdout + end + + # return uuid for an identity resource. + # + # @param [String] type of resource (user, service, tenant, endpoint, role) + # @param [String] key of resource to match + # @param [String] value of resource key to match + # @param [Hash] environment to use. + # @param [Hash] optional command argument/values pairs + # @param [String] optional uuid field to match + # @return [String] uuid or nil + # + # TODO: this was taken from the identity register provider, will need to + # update the provider to use this. + # + def identity_uuid(type, key, value, env, args = {}, uuid_field = 'id') # rubocop: disable ParameterLists + begin + output = openstack_command('keystone', "#{type}-list", env, args) + prettytable_to_array(output).each do |obj| + return obj[uuid_field] if obj.key?(uuid_field) && obj[key] == value + end + rescue RuntimeError => e + raise "Could not lookup uuid for #{type}:#{key}=>#{value}. Error was #{e.message}" + end + nil + end +end diff --git a/chef/cookbooks/openstack-common/libraries/database.rb b/chef/cookbooks/openstack-common/libraries/database.rb index 089d0f7..6dacd05 100644 --- a/chef/cookbooks/openstack-common/libraries/database.rb +++ b/chef/cookbooks/openstack-common/libraries/database.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 + # # Cookbook Name:: openstack-common # library:: default @@ -17,57 +19,71 @@ # limitations under the License. # -module ::Openstack +module ::Openstack # rubocop:disable Documentation # Library routine that uses the database cookbook to create the # service's database and grant read/write access to the # given user and password. # - # A privileged "super user" and password is determined from the + # A privileged 'super user' and password is determined from the # underlying database cookbooks. For instance, if a MySQL database - # is used, the node["mysql"]["server_root_password"] is used along - # with the "root" (super)user. - def db_create_with_user service, user, pass + # is used, the node['mysql']['server_root_password'] is used along + # with the 'root' (super)user. + def db_create_with_user(service, user, pass) # rubocop:disable CyclomaticComplexity, MethodLength root_user_use_databag = node['openstack']['db']['root_user_use_databag'] info = db service if info host = info['host'] port = info['port'].to_s - type = info['db_type'] + type = info['service_type'] db_name = info['db_name'] case type - when "postgresql", "pgsql" - include_recipe "database::postgresql" + when 'postgresql', 'pgsql' + include_recipe 'database::postgresql' db_prov = ::Chef::Provider::Database::Postgresql user_prov = ::Chef::Provider::Database::PostgresqlUser - super_user = "postgres" + super_user = 'postgres' if root_user_use_databag user_key = node['openstack']['db']['root_user_key'] - super_password = user_password user_key + super_password = get_password 'user', user_key else super_password = node['postgresql']['password']['postgres'] end - when "mysql" + when 'mysql' # we have to install the 'mysql' gem, otherwise the provider won't work - include_recipe "database::mysql" + include_recipe 'database::mysql' db_prov = ::Chef::Provider::Database::Mysql user_prov = ::Chef::Provider::Database::MysqlUser - super_user = "root" + super_user = 'root' if root_user_use_databag user_key = node['openstack']['db']['root_user_key'] - super_password = user_password user_key + super_password = get_password 'user', user_key else super_password = node['mysql']['server_root_password'] end + when 'db2' + db2_database 'create database' do + db_name db_name + action :create + end + + db2_user 'create database user' do + db_user user + db_pass pass + db_name db_name + action :create + end + + return info else ::Chef::Log.error("Unsupported database type #{type}") end connection_info = { - :host => host, - :port => port.to_i, - :username => super_user, - :password => super_password + host: host, + port: port.to_i, + username: super_user, + password: super_password } # create database diff --git a/chef/cookbooks/openstack-common/libraries/endpoints.rb b/chef/cookbooks/openstack-common/libraries/endpoints.rb index 92812fa..1944c13 100644 --- a/chef/cookbooks/openstack-common/libraries/endpoints.rb +++ b/chef/cookbooks/openstack-common/libraries/endpoints.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 + # # Cookbook Name:: openstack-common # library:: endpoints @@ -17,24 +19,21 @@ # limitations under the License. # -require "uri" +require 'uri' -module ::Openstack - # Shortcut to get the full URI for an endpoint. If the "uri" key isn't - # set in the endpoint hash, we use the ::Openstack.get_uri_from_mash - # library routine from the openstack-common cookbook to grab a URI object - # and construct the URI object from the endpoint parts. - def endpoint name - ep = endpoint_for name - if ep && ep['uri'] - ::URI.parse ::URI.encode(ep['uri']) - elsif ep - uri_from_hash ep - end +module ::Openstack # rubocop:disable Documentation + # Shortcut to get the full URI for an endpoint, and return it as a URI object + # First we get the attribute hash for the endpoint, using endpoint_for(). + # Then we call uri_from_hash(). If the hash has a 'uri' key, + # this gets converted to a URI object and returned. If not, a URI object is + # constructed from the endpoint parts in the endpoint hash, and returned + def endpoint(name) + ep = endpoint_for(name) + uri_from_hash(ep) if ep end # Useful for iterating over the OpenStack endpoints - def endpoints &block + def endpoints(&block) node['openstack']['endpoints'].each do | name, info | block.call(name, info) end @@ -42,43 +41,75 @@ module ::Openstack nil end - # Instead of specifying the verbose node["openstack"]["db"][service], + # Instead of specifying the verbose node['openstack']['db'][service], # this shortcut allows the simpler and shorter db(service), where - # service is one of 'compute', 'image', 'identity', 'network', - # and 'volume' - def db service + # service is one of 'compute', 'image', 'identity', 'network', 'dashboard' + # 'orchestration', 'telemetry' 'block-storage' and 'volume' + def db(service) node['openstack']['db'][service] rescue nil end # Shortcut to get the SQLAlchemy DB URI for a named service - def db_uri service, user, pass + def db_uri(service, user, pass) # rubocop:disable MethodLength, CyclomaticComplexity info = db(service) if info host = info['host'] port = info['port'].to_s - type = info['db_type'] + type = info['service_type'] name = info['db_name'] - if type == "pgsql" - # Normalize to the SQLAlchemy standard db type identifier - type = "postgresql" - end + options = info['options'][type] + + # Normalize to the SQLAlchemy standard db type identifier case type - when "mysql", "postgresql" - result = "#{type}://#{user}:#{pass}@#{host}:#{port}/#{name}" - when "sqlite" + when 'db2' + # NoSQL is used for telemetry in the DB2 case + if service == 'telemetry' && node['openstack']['db']['telemetry']['nosql']['used'] + options = info['options']['nosql'] + port = info['nosql']['port'] + type = 'db2' + else + type = 'ibm_db_sa' + end + when 'pgsql' + type = 'postgresql' + end + + # Build uri + case type + when 'mysql', 'postgresql', 'db2', 'ibm_db_sa' + "#{type}://#{user}:#{pass}@#{host}:#{port}/#{name}#{options}" + when 'sqlite' # SQLite uses filepaths not db name + # README(galstrom): 3 slashes is a relative path, 4 slashes is an absolute path + # example: info['path'] = 'path/to/foo.db' -- will return sqlite:///foo.db + # example: info['path'] = '/path/to/foo.db' -- will return sqlite:////foo.db path = info['path'] - result = "sqlite://#{path}" + "#{type}:///#{path}#{options}" end end end -private - # Instead of specifying the verbose node["openstack"]["endpoints"][name], + # Return the IPv4 address for the hash. + # + # If the bind_interface is set, then return the first IP on the interface. + # otherwise return the IP specified in the host attribute. + def address(hash) + bind_interface = hash['bind_interface'] if hash['bind_interface'] + + if bind_interface + return address_for bind_interface + else + return hash['host'] + end + end + + private + + # Instead of specifying the verbose node['openstack']['endpoints'][name], # this shortcut allows the simpler and shorter endpoint(name) - def endpoint_for name + def endpoint_for(name) node['openstack']['endpoints'][name] rescue nil diff --git a/chef/cookbooks/openstack-common/libraries/network.rb b/chef/cookbooks/openstack-common/libraries/network.rb index fca7e28..67f6119 100644 --- a/chef/cookbooks/openstack-common/libraries/network.rb +++ b/chef/cookbooks/openstack-common/libraries/network.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 + # # Cookbook Name:: openstack-common # library:: address @@ -17,18 +19,16 @@ # limitations under the License. # -module ::Openstack +module ::Openstack # rubocop:disable Documentation # return the IPv4 (default) address of the given interface. # # @param [String] interface The interface to query. # @param [String] family The protocol family to use. - # @return [String] The IPv4 address. - def address_for interface, family="inet" - interface_node = node["network"]["interfaces"][interface]["addresses"] + # @return [String] The address. + def address_for(interface, family = node['openstack']['endpoints']['family']) + interface_node = node['network']['interfaces'][interface]['addresses'] interface_node.select do |address, data| - if data['family'] == family - return address - end + return address if data['family'] == family end end end diff --git a/chef/cookbooks/openstack-common/libraries/parse.rb b/chef/cookbooks/openstack-common/libraries/parse.rb index 50f0ffa..1f3ec08 100644 --- a/chef/cookbooks/openstack-common/libraries/parse.rb +++ b/chef/cookbooks/openstack-common/libraries/parse.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 + # # Cookbook Name:: openstack-common # library:: parse @@ -15,10 +17,8 @@ # 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. -# - -module ::Openstack +module ::Openstack # rubocop:disable Documentation # The current state of (at least some) OpenStack CLI tools do not provide a # mechanism for outputting data in formats other than PrettyTable output. # Therefore this function is intended to parse PrettyTable output into a @@ -26,38 +26,33 @@ module ::Openstack # into a single element array. # table - the raw PrettyTable output of the CLI command # output - array of hashes representing the data. - def prettytable_to_array table + def prettytable_to_array(table) # rubocop:disable MethodLength ret = [] - return ret if table == nil + return ret if table.nil? indicies = [] - (table.split(/$/).collect{|x| x.strip}).each { |line| - unless line.start_with?('+--') or line.empty? - cols = line.split('|').collect{|x| x.strip} + (table.split(/$/).map { |x| x.strip }).each do |line| + unless line.start_with?('+--') || line.empty? + cols = line.split('|').map { |x| x.strip } cols.shift if indicies == [] indicies = cols next end newobj = {} - cols.each { |val| - newobj[indicies[newobj.length]] = val - } + cols.each { |val| newobj[indicies[newobj.length]] = val } ret.push(newobj) end - } + end # this kinda sucks, but some prettytable data comes # as Property Value pairs. If this is the case, then # flatten it as expected. newobj = {} if indicies == ['Property', 'Value'] - ret.each { |x| - newobj[x['Property']] = x['Value'] - } + ret.each { |x| newobj[x['Property']] = x['Value'] } [newobj] else ret end end - end diff --git a/chef/cookbooks/openstack-common/libraries/passwords.rb b/chef/cookbooks/openstack-common/libraries/passwords.rb index 9e58e16..5cc9044 100644 --- a/chef/cookbooks/openstack-common/libraries/passwords.rb +++ b/chef/cookbooks/openstack-common/libraries/passwords.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 + # # Cookbook Name:: openstack-common # library:: passwords @@ -17,13 +19,13 @@ # limitations under the License. # -module ::Openstack +module ::Openstack # rubocop:disable Documentation # Library routine that returns an encrypted data bag value # for a supplied string. The key used in decrypting the # encrypted value should be located at - # node["openstack"]["secret"]["key_path"]. + # node['openstack']['secret']['key_path']. # - # Note that if node["openstack"]["developer_mode"] is true, + # Note that if node['openstack']['developer_mode'] is true, # then the value of the index parameter is just returned as-is. This # means that in developer mode, if a cookbook does this: # @@ -33,39 +35,39 @@ module ::Openstack # end # end # - # nova_password = secret "passwords", "nova" + # nova_password = secret 'passwords', 'nova' # - # That means nova_password will == "nova". - def secret bag_name, index - if node["openstack"]["developer_mode"] - return index - end - key_path = node["openstack"]["secret"]["key_path"] + # That means nova_password will == 'nova'. + # + # You also can provide a default password value in developer mode, + # like following: + # + # node.set['openstack']['secret']['nova'] = 'nova_password' + # nova_password = secret 'passwords', 'nova' + # + # The nova_password will == 'nova_password' + def secret(bag_name, index, password = nil) + return (node['openstack']['secret'][index] || password || index) if node['openstack']['developer_mode'] + key_path = node['openstack']['secret']['key_path'] ::Chef::Log.info "Loading encrypted databag #{bag_name}.#{index} using key at #{key_path}" secret = ::Chef::EncryptedDataBagItem.load_secret key_path ::Chef::EncryptedDataBagItem.load(bag_name, index, secret)[index] end - # Ease-of-use/standardization routine that returns a service password - # for a named OpenStack service. Note that databases are named - # after the OpenStack project nickname, like "nova" or "glance" - def service_password service - bag = node["openstack"]["secret"]["service_passwords_data_bag"] - secret bag, service + # Ease-of-use/standarization routine that returns a secret from the + # attribute-specified openstack secrets databag. + def get_secret(key) + secret node['openstack']['secret']['secrets_data_bag'], key end - # Ease-of-use/standardization routine that returns a database password - # for a named OpenStack database. Note that databases are named - # after the OpenStack project nickname, like "nova" or "glance" - def db_password service - bag = node["openstack"]["secret"]["db_passwords_data_bag"] - secret bag, service - end - - # Ease-of-use/standardization routine that returns a password - # for a user. - def user_password user - bag = node["openstack"]["secret"]["user_passwords_data_bag"] - secret bag, user + # Ease-of-use/standarization routine that returns a service/database/user + # password for a named OpenStack service/database/user. Accepts 'user', + # 'service' or 'db' as the type. + def get_password(type, key, password = nil) + if ['db', 'user', 'service'].include?(type) + secret node['openstack']['secret']["#{type}_passwords_data_bag"], key, password + else + ::Chef::Log.error("Unsupported type for get_password: #{type}") + end end end diff --git a/chef/cookbooks/openstack-common/libraries/search.rb b/chef/cookbooks/openstack-common/libraries/search.rb index 8e97c6e..d1017e0 100644 --- a/chef/cookbooks/openstack-common/libraries/search.rb +++ b/chef/cookbooks/openstack-common/libraries/search.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-common # library:: search @@ -17,63 +18,72 @@ # limitations under the License. # -module ::Openstack +module ::Openstack # rubocop:disable Documentation # Search the nodes environment for the given role or recipe. # # @param [String] The role or recipe to be found. # @return [Array] The matching result or an empty list. - def search_for r, &block + def search_for(r, &block) # rubocop:disable MethodLength role_query = "(chef_environment:#{node.chef_environment} AND roles:#{r})" - recipe_query = "(chef_environment:#{node.chef_environment} AND recipes:#{r})".sub("::","\\:\\:") + recipe_query = "(chef_environment:#{node.chef_environment} AND recipes:#{r})".sub('::', '\:\:') query = "#{role_query} OR #{recipe_query}" - - resp = search(:node, query, &block) + count = 1 + sum = 7 + while count < sum + resp = search(:node, query, &block) + break unless resp.nil? + sleep 2**count + count += 1 + end resp ? resp : [] end - # Returns the value for ["openstack"]["memcached_servers"] when + # Returns the value for ['openstack']['memcached_servers'] when # set, otherwise will perform a search. # # @param [String] role The role to be found (optional). # @return [Array] A list of memcached servers in format # ':'. - def memcached_servers role="infra-caching" - unless node['openstack']['memcached_servers'] + def memcached_servers(role = 'infra-caching') # rubocop:disable MethodLength + if !node['openstack']['memcached_servers'] search_for(role).map do |n| listen = n['memcached']['listen'] - port = n['memcached']['port'] || "11211" + port = n['memcached']['port'] || '11211' "#{listen}:#{port}" end.sort else - node['openstack']['memcached_servers'].length != 0 ? - node['openstack']['memcached_servers'] : [] + if node['openstack']['memcached_servers'].length != 0 + node['openstack']['memcached_servers'] + else + [] + end end end # Returns all rabbit servers. - # Uses the value for ["openstack"]["mq"]["servers"] when set, otherwise + # Uses the value for ['openstack']['mq']['servers'] when set, otherwise # will perform a search. # # @return [String] Rabbit servers joined by a comma in # the format of ':'. - def rabbit_servers - if node["openstack"]["mq"]["servers"] - servers = node["openstack"]["mq"]["servers"] - port = node["openstack"]["mq"]["port"] + def rabbit_servers # rubocop:disable MethodLength + if node['openstack']['mq']['servers'] + servers = node['openstack']['mq']['servers'] + port = node['openstack']['endpoints']['mq']['port'] - servers.map { |s| "#{s}:#{port}" }.join "," + servers.map { |s| "#{s}:#{port}" }.join ',' else - role = node["openstack"]["mq"]["server_role"] + role = node['openstack']['mq']['server_role'] search_for(role).map do |n| # The listen attribute should be saved to the node # in the wrapper cookbook. See the reference cookbook # openstack-ops-messaging. - address = n["openstack"]["mq"]["listen"] - port = n["openstack"]["mq"]["port"] + address = n['openstack']['mq']['listen'] + port = n['openstack']['endpoints']['mq']['port'] "#{address}:#{port}" - end.sort.join "," + end.sort.join ',' end end end diff --git a/chef/cookbooks/openstack-common/libraries/uri.rb b/chef/cookbooks/openstack-common/libraries/uri.rb index 15d255e..10d21fb 100644 --- a/chef/cookbooks/openstack-common/libraries/uri.rb +++ b/chef/cookbooks/openstack-common/libraries/uri.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 + # # Cookbook Name:: openstack-common # library:: uri @@ -17,24 +19,28 @@ # limitations under the License. # -require "uri" +require 'uri' -module ::Openstack - # Returns a uri::URI from a hash. If the hash has a "uri" key, the value +module ::Openstack # rubocop:disable Documentation + # Returns a uri::URI from a hash. If the hash has a 'uri' key, the value # of that is returned. If not, then the routine attempts to construct - # the URI from other parts of the hash, notably looking for keys of - # "host", "port", "scheme", and "path" to construct the URI. + # the URI from other parts of the hash. The values of the 'port' and 'path' + # keys are used directly from the hash. For the host, if the + # 'bind_interface' key is non-nil then it will use the first IP address on + # the specified interface, otherwise it will use the value of the 'host' key + # from the hash. # - # Returns nil if neither "uri" or "host" keys exist in the supplied - # hash. - def uri_from_hash hash + # Returns nil if the 'uri' key does not exist in the supplied hash and if + # the determined host is nil (both the values of the 'bind_interface' and + # 'host' keys are nil). + def uri_from_hash(hash) if hash['uri'] - ::URI.parse hash['uri'] + ::URI.parse ::URI.encode(hash['uri']) else - return nil unless hash['host'] + host = address hash + return nil unless host - scheme = hash['scheme'] ? hash['scheme'] : "http" - host = hash['host'] + scheme = hash['scheme'] ? hash['scheme'] : 'http' port = hash['port'] # Returns nil if missing, which is fine. path = hash['path'] # Returns nil if missing, which is fine. ::URI::Generic.new scheme, nil, host, port, nil, path, nil, nil, nil @@ -48,9 +54,21 @@ module ::Openstack return nil if paths.length == 0 leadingslash = paths[0][0] == '/' ? '/' : '' trailingslash = paths[-1][-1] == '/' ? '/' : '' - paths.map! { |path| - path = path.sub(/^\/+/,'').sub(/\/+$/,'') - } + paths.map! { |path| path.sub(/^\/+/, '').sub(/\/+$/, '') } leadingslash + paths.join('/') + trailingslash end + + def auth_uri_transform(auth_uri, auth_version) + case auth_version + when 'v2.0' + auth_uri + when 'v3.0' + # The auth_uri should contain /v2.0 in most cases, but if the + # auth_version is v3.0, we set it to v3. This is only necessary + # for environments that need to support V3 non-default-domain + # tokens, which is really the only reason to set version to + # something other than v2.0 (the default) + auth_uri.gsub('/v2.0', '/v3') + end + end end diff --git a/chef/cookbooks/openstack-common/metadata.rb b/chef/cookbooks/openstack-common/metadata.rb old mode 100644 new mode 100755 index 584ccea..5c08a50 --- a/chef/cookbooks/openstack-common/metadata.rb +++ b/chef/cookbooks/openstack-common/metadata.rb @@ -1,18 +1,22 @@ -name "openstack-common" -maintainer "AT&T Services, Inc." -maintainer_email "cookbooks@lists.tfoundry.com" -license "Apache 2.0" -description "Common OpenStack attributes, libraries and recipes." +name 'openstack-common' +maintainer 'AT&T Services, Inc.' +maintainer_email 'cookbooks@lists.tfoundry.com' +license 'Apache 2.0' +description 'Common OpenStack attributes, libraries and recipes.' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "0.4.3" +version '9.4.1' -recipe "openstack-common", "Installs/Configures common recipes" -recipe "openstack-common::logging", "Installs/Configures common logging" +recipe 'openstack-common', 'Installs/Configures common recipes' +recipe 'openstack-common::set_endpoints_by_interface', 'Set endpoints by interface' +recipe 'openstack-common::logging', 'Installs/Configures common logging' +recipe 'openstack-common::sysctl', 'Configures sysctl settings' +recipe 'openstack-common::openrc', 'Creates openrc file' -%w{ ubuntu suse }.each do |os| +%w{ ubuntu suse redhat centos }.each do |os| supports os end -depends "apt" -depends "yum" -depends "database" +depends 'apt', '>= 2.3.8' +depends 'database', '>= 2.0.0' +depends 'yum', '>= 3.1.4' +depends 'yum-epel', '>= 0.3.4' diff --git a/chef/cookbooks/openstack-common/recipes/ceph_client.rb b/chef/cookbooks/openstack-common/recipes/ceph_client.rb new file mode 100644 index 0000000..ffb5ca8 --- /dev/null +++ b/chef/cookbooks/openstack-common/recipes/ceph_client.rb @@ -0,0 +1,48 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-common +# Recipe:: ceph_client +# +# Copyright 2014, x-ion GmbH +# +# 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. +# + +return unless node['openstack']['ceph']['setup_client'] + +case node['platform_family'] +when 'debian' + apt_repository 'ceph' do + uri node['openstack']['ceph']['platform']['uri'] + distribution node['lsb']['codename'] + components ['main'] + key node['openstack']['ceph']['key-url'] + end +when 'fedora', 'rhel', 'suse' # :pragma-foodcritic: ~FC024 - won't fix this + # TODO +end + +directory '/etc/ceph' do + user 'root' + group 'root' +end + +template '/etc/ceph/ceph.conf' do + source 'ceph.conf.erb' + user 'root' + group 'root' + mode '644' + variables( + global: node['openstack']['ceph']['global'] + ) +end diff --git a/chef/cookbooks/openstack-common/recipes/client.rb b/chef/cookbooks/openstack-common/recipes/client.rb new file mode 100755 index 0000000..a24d0b6 --- /dev/null +++ b/chef/cookbooks/openstack-common/recipes/client.rb @@ -0,0 +1,32 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-common +# Recipe:: client +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +platform_options = node['openstack']['common']['platform'] +platform_options['common_client_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end diff --git a/chef/cookbooks/openstack-common/recipes/databag.rb b/chef/cookbooks/openstack-common/recipes/databag.rb deleted file mode 100644 index b50f406..0000000 --- a/chef/cookbooks/openstack-common/recipes/databag.rb +++ /dev/null @@ -1,388 +0,0 @@ -# -# Cookbook Name:: openstack-common -# Attributes:: default -# -# Copyright 2013, Futurewei, Inc. -# -# 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. -# -# import initial data from databag with customized user data. -# - -class ::Chef::Recipe - include ::Openstack -end - -defaultbag = "openstack" -if !Chef::DataBag.list.key?(defaultbag) - Chef::Application.fatal!("databag '#{defaultbag}' doesn't exist.") - return -end - -myitem = node.attribute?('cluster')? node['cluster']:"env_default" - -if !search(defaultbag, "id:#{myitem}") - Chef::Application.fatal!("databagitem '#{myitem}' doesn't exist.") - return -end - -mydata = data_bag_item(defaultbag, myitem) -# use unsecreted text username and password at chef server. -node.override['openstack']['developer_mode'] = mydata['credential']['text'] - -# The coordinated release of OpenStack codename -node.override['openstack']['release'] = mydata['release'] - -verbose = mydata['debugging']['verbose'] -if verbose.eql?("True") - node.override["openstack"]["block-storage"]["verbose"] = verbose - node.override["openstack"]["image"]["verbose"] = verbose - node.override["openstack"]["identity"]["verbose"] = verbose - node.override["openstack"]["compute"]["verbose"] = verbose - node.override["openstack"]["network"]["verbose"] = verbose -end - -debug = mydata['debugging']['debug'] -if debug.eql?("True") - node.override["openstack"]["block-storage"]["debug"] = debug - node.override["openstack"]["image"]["debug"] = debug - node.override["openstack"]["identity"]["debug"] = debug - node.override["openstack"]["compute"]["debug"] = debug - node.override["openstack"]["network"]["debug"] = debug -end - -# Openstack repo setup -# ubuntu -node.override['openstack']['apt']['components'] = [ "precise-updates/#{node['openstack']['release']}", "main" ] - -# redhat -if node['openstack']['release'].eql?("grizzly") - node.override['openstack']['yum']['openstack']['url']="http://repos.fedorapeople.org/repos/openstack/EOL/openstack-#{node['openstack']['release']}/epel-#{node['platform_version'].to_i}/" -else - node.override['openstack']['yum']['openstack']['url']="http://repos.fedorapeople.org/repos/openstack/openstack-#{node['openstack']['release']}/epel-#{node['platform_version'].to_i}/" -end -# Tenant and user -node.override['openstack']['identity']['admin_token'] = mydata['credential']['identity']['token']['admin'] -node.override['openstack']['identity']['tenants'] = ["#{ mydata['credential']['identity']['tenants']['admin']}", "#{ mydata['credential']['identity']['tenants']['service']}"] -node.override["openstack"]["identity"]["roles"] = ["#{ mydata['credential']['identity']['roles']['admin']}", "#{ mydata['credential']['identity']['roles']['member']}"] - -node.override['openstack']['identity']['admin_tenant_name'] = mydata['credential']['identity']['tenants']['admin'] -node.override['openstack']['identity']['admin_user'] = mydata['credential']['identity']['users']['admin']['username'] -node.override['openstack']['identity']['admin_password'] = mydata['credential']['identity']['users']['admin']['password'] - -# services with related usernames and passwords -node['openstack']['services'].each_key do |service| - node.override['openstack']['services']["#{service}"]['name'] = mydata['services']["#{service}"]['name'] - node.override['openstack']['services']["#{service}"]['status'] = mydata['services']["#{service}"]['status'] - - if service != "object-store" - node.set['openstack']['db']["#{service}"]['username'] = mydata['credential']['mysql']["#{service}"]['username'] - node.set['openstack']['db']["#{service}"]['password'] = mydata['credential']['mysql']["#{service}"]['password'] - end - - if "#{service}" != "identity" and "#{service}" != "dashboard" - node.override['openstack']['identity']["#{service}"]['username'] = mydata['credential']['identity']['users']["#{service}"]['username'] - node.override['openstack']['identity']["#{service}"]['password'] = mydata['credential']['identity']['users']["#{service}"]['password'] - node.override['openstack']['identity']["#{service}"]['role'] = mydata['credential']['identity']['roles']['admin'] - node.set['openstack']['identity']["#{service}"]['tenant'] = mydata['credential']['identity']['users']["#{service}"]['tenant'] - end -end - -node.set["openstack"]["ha"]["status"] = mydata['ha']['status'] -if node["openstack"]["ha"]["status"].eql?('enable') - node.set["openstack"]["identity"]["bind_interface"] = mydata['networking']['control']['interface'] - node.set["openstack"]["image"]["api"]["bind_interface"] = mydata['networking']['control']['interface'] - node.set["openstack"]["image"]["registry"]["bind_interface"] = mydata['networking']['control']['interface'] - node.set["openstack"]["network"]["api"]["bind_interface"] = mydata['networking']['control']['interface'] -end - -# network plugins -node.override["openstack"]["network"]["plugins"] = ['openvswitch', 'openvswitch-agent'] - - - -# ======================== OpenStack Endpoints ================================ -# Identity (keystone) -node.override['openstack']['endpoints']['identity-api']['host'] = mydata['endpoints']['identity']['service']['host'] -node.override['openstack']['endpoints']['identity-api']['scheme'] = mydata['endpoints']['identity']['service']['scheme'] -node.override['openstack']['endpoints']['identity-api']['port'] = "5000" -node.override['openstack']['endpoints']['identity-api']['path'] = "/v2.0" - -node.override['openstack']['endpoints']['identity-admin']['host'] = mydata['endpoints']['identity']['admin']['host'] -node.override['openstack']['endpoints']['identity-admin']['scheme'] = mydata['endpoints']['identity']['admin']['scheme'] -node.override['openstack']['endpoints']['identity-admin']['port'] = "35357" -node.override['openstack']['endpoints']['identity-admin']['path'] = "/v2.0" - -# Compute (Nova) -node.override['openstack']['endpoints']['compute-api']['host'] = mydata['endpoints']['compute']['service']['host'] -node.override['openstack']['endpoints']['compute-api']['scheme'] = mydata['endpoints']['compute']['service']['scheme'] -#node.override['openstack']['endpoints']['compute-api']['port'] = "8774" -#node.override['openstack']['endpoints']['compute-api']['path'] = "/v2/%(tenant_id)s" - -# The OpenStack Compute (Nova) EC2 API endpoint -node.override['openstack']['endpoints']['compute-ec2-api']['host'] = mydata['endpoints']['ec2']['service']['host'] -node.override['openstack']['endpoints']['compute-ec2-api']['scheme'] = mydata['endpoints']['ec2']['service']['scheme'] -#node.override['openstack']['endpoints']['compute-ec2-api']['port'] = "8773" -#node.override['openstack']['endpoints']['compute-ec2-api']['path'] = "/services/Cloud" - -# The OpenStack Compute (Nova) EC2 Admin API endpoint -node.override['openstack']['endpoints']['compute-ec2-admin']['host'] = mydata['endpoints']['ec2']['admin']['host'] -node.override['openstack']['endpoints']['compute-ec2-admin']['scheme'] = mydata['endpoints']['ec2']['admin']['scheme'] -#node.override['openstack']['endpoints']['compute-ec2-admin']['port'] = "8773" -#node.override['openstack']['endpoints']['compute-ec2-admin']['path'] = "/services/Admin" - -# The OpenStack Compute (Nova) XVPvnc endpoint -node.override['openstack']['endpoints']['compute-xvpvnc']['host'] = mydata['endpoints']['compute']['xvpvnc']['host'] -node.override['openstack']['endpoints']['compute-xvpvnc']['scheme'] = mydata['endpoints']['compute']['xvpvnc']['scheme'] -#node.override['openstack']['endpoints']['compute-xvpvnc']['port'] = "6081" -#node.override['openstack']['endpoints']['compute-xvpvnc']['path'] = "/console" - -# The OpenStack Compute (Nova) novnc endpoint -node.override['openstack']['endpoints']['compute-novnc']['host'] = mydata['endpoints']['compute']['novnc']['host'] -node.override['openstack']['endpoints']['compute-novnc']['scheme'] = mydata['endpoints']['compute']['novnc']['scheme'] -#node.override['openstack']['endpoints']['compute-novnc']['port'] = "6080" -#node.override['openstack']['endpoints']['compute-novnc']['path'] = "/vnc_auto.html" - - -# Network (Quantum) -node.override['openstack']['endpoints']['network-api']['host'] = mydata['endpoints']['network']['service']['host'] -node.override['openstack']['endpoints']['network-api']['scheme'] = mydata['endpoints']['network']['service']['scheme'] -#node.override['openstack']['endpoints']['network-api']['port'] = "9696" -# quantumclient appends the protocol version to the endpoint URL, so the - -# Image (Glance) -node.override['openstack']['endpoints']['image-api']['host'] = mydata['endpoints']['image']['service']['host'] -node.override['openstack']['endpoints']['image-api']['scheme'] = mydata['endpoints']['image']['service']['scheme'] -#node.override['openstack']['endpoints']['image-api']['port'] = "9292" -#node.override['openstack']['endpoints']['image-api']['path'] = "/v2" - -# Image (Glance) Registry -node.override['openstack']['endpoints']['image-registry']['host'] = mydata['endpoints']['image']['registry']['host'] -node.override['openstack']['endpoints']['image-registry']['scheme'] = mydata['endpoints']['image']['registry']['scheme'] -node.override['openstack']['endpoints']['image-registry']['port'] = "9191" -node.override['openstack']['endpoints']['image-registry']['path'] = "/v2" - -# Volume (Cinder) -node.override['openstack']['endpoints']['volume-api']['host'] = mydata['endpoints']['volume']['service']['host'] -node.override['openstack']['endpoints']['volume-api']['scheme'] = mydata['endpoints']['volume']['service']['scheme'] -#node.override['openstack']['endpoints']['volume-api']['port'] = "8776" -#node.override['openstack']['endpoints']['volume-api']['path'] = "/v1/%(tenant_id)s" - -# Metering (Ceilometer) -node.override['openstack']['endpoints']['metering-api']['host'] = mydata['endpoints']['metering']['service']['host'] -node.override['openstack']['endpoints']['metering-api']['scheme'] = mydata['endpoints']['metering']['service']['scheme'] -#node.override['openstack']['endpoints']['metering-api']['port'] = "8777" -#node.override['openstack']['endpoints']['metering-api']['path'] = "/v1" - - -# ======================== OpenStack DB Support ================================ -# set database attributes -#node.override['openstack']['db']['server_role'] = "os-ops-database" -node.override['openstack']['db']['service_type'] = mydata['db']['service_type'] -node.override['openstack']['db']['bind_address'] = mydata['db']["#{node['openstack']['db']['service_type']}"]['bind_address'] -node.override['openstack']['db']['port'] = mydata['db']["#{node['openstack']['db']['service_type']}"]['port'] - -node.override['openstack']['db']['super']['username'] = mydata['credential']["#{node['openstack']['db']['service_type']}"]['super']['username'] -node.override['openstack']['db']['super']['password'] = mydata['credential']["#{node['openstack']['db']['service_type']}"]['super']['password'] - -# Database used by the OpenStack Compute (Nova) service -node.override['openstack']['db']['compute']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['compute']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['compute']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['compute']['db_name'] = "nova" - -# Database used by the OpenStack Identity (Keystone) service -node.override['openstack']['db']['identity']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['identity']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['identity']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['identity']['db_name'] = "keystone" - -# Database used by the OpenStack Image (Glance) service -node.override['openstack']['db']['image']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['image']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['image']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['image']['db_name'] = "glance" - -# Database used by the OpenStack Network (Quantum) service -node.override['openstack']['db']['network']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['network']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['network']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['network']['db_name'] = "quantum" - -# Database used by the OpenStack Volume (Cinder) service -node.override['openstack']['db']['volume']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['volume']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['volume']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['volume']['db_name'] = "cinder" - -# Database used by the OpenStack Dashboard (Horizon) -node.override['openstack']['db']['dashboard']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['dashboard']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['dashboard']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['dashboard']['db_name'] = "horizon" - -# Database used by OpenStack Metering (Ceilometer) -node.override['openstack']['db']['metering']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['metering']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['metering']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['metering']['db_name'] = "ceilometer" - -# node.override database attributes -#node.override['openstack']['mq']['server_role'] = "os-ops-messaging" -node.override['openstack']['mq']['service_type'] = mydata['mq']['service_type'] -node.override['openstack']['mq']['bind_address'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['bind_address'] -node.override['openstack']['mq']['port'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['port'] -node.override['openstack']['mq']['user'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['username'] -node.override['openstack']['mq']['password'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['password'] -node.override['openstack']['mq']['vhost'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['vhost'] - - - - -# ============================= Network service Configuration ===================== -# Gets set in the Network Endpoint when registering with Keystone -node.override["openstack"]["network"]["service_user"] = node['openstack']['identity']['network']['username'] -node.override["openstack"]["network"]["service_role"] = node['openstack']['identity']['network']['role'] -node.override["openstack"]["network"]["service_name"] = node['openstack']['services']['network']['name'] -# node.override["openstack"]["network"]["service_type"] = "network" -# node.override["openstack"]["network"]["description"] = "OpenStack Networking service" - -# The rabbit user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# user_password routine. You are expected to create -# the user, pass, vhost in a wrapper rabbitmq cookbook. -#node.override["openstack"]["network"]["rabbit_server_chef_role"] = "rabbitmq-server" -node.override["openstack"]["network"]["rabbit"]["username"] = node['openstack']['mq']['user'] -#node.override["openstack"]["network"]["rabbit"]["vhost"] = "/" -node.override["openstack"]["network"]["rabbit"]["port"] = node['openstack']['mq']['port'] -node.override["openstack"]["network"]["rabbit"]["host"] = node['openstack']['mq']['bind_address'] -#node.override["openstack"]["network"]["rabbit"]["ha"] = false - -# The database username for the quantum database -node.override["openstack"]["network"]["db"]["username"] = mydata['credential']['mysql']['network']['username'] - -# Used in the Keystone authtoken middleware configuration -node.override["openstack"]["network"]["service_tenant_name"] = node['openstack']['identity']['network']['tenant'] -node.override["openstack"]["network"]["service_user"] = node['openstack']['identity']['network']['username'] -node.override["openstack"]["network"]["service_role"] = node['openstack']['identity']['network']['role'] - -node.override["openstack"]["network"]["api"]["bind_interface"] = mydata['networking']['control']['interface'] - -node.set['openstack']['networking']['control']['interface'] = mydata['networking']['control']['interface'] -node.set['openstack']['networking']['tenant']['interface'] = mydata['networking']['tenant']['interface'] -node.set['openstack']['networking']['public']['interface'] = mydata['networking']['public']['interface'] -node.set['openstack']['networking']['storage']['interface'] = mydata['networking']['storage']['interface'] - - -# ============================= L3 Agent Configuration ===================== - -# Indicates that this L3 agent should also handle routers that do not have -# an external network gateway configured. This option should be True only -# for a single agent in a Quantum deployment, and may be False for all agents -# if all routers must have an external network gateway -#node.override["openstack"]["network"]["l3"]["handle_internal_only_routers"] = "False" - -# Name of bridge used for external network traffic. This should be set to -# empty value for the linux bridge -# node.override["openstack"]["network"]["l3"]["external_network_bridge"] = "br-ex" - -# Interface to use for external bridge. -node.override["openstack"]["network"]["l3"]["external_network_bridge_interface"] = mydata['networking']['public']['interface'] - - -# ============================= Metadata Agent Configuration =============== - -# The location of the Nova Metadata API service to proxy to (nil uses node.override) -node.override["openstack"]["network"]["metadata"]["nova_metadata_ip"] = mydata['endpoints']['compute']['service']['host'] -#node.override["openstack"]["network"]["metadata"]["nova_metadata_port"] = 8775 - -# The name of the secret databag containing the metadata secret -node.override["openstack"]["network"]["metadata"]["secret_name"] = mydata['credential']['metadata']['password'] - - -# ============================= OVS Plugin Configuration =================== - -# Type of network to allocate for tenant networks. The node.override value 'local' is -# useful only for single-box testing and provides no connectivity between hosts. -# You MUST either change this to 'vlan' and configure network_vlan_ranges below -# or change this to 'gre' and configure tunnel_id_ranges below in order for tenant -# networks to provide connectivity between hosts. Set to 'none' to disable creation -# of tenant networks. - -tenant_network_type = mydata['networking']['plugins']['ovs']['tenant_network_type'] -node.override["openstack"]["network"]["openvswitch"]["tenant_network_type"] =tenant_network_type - -# Comma-separated list of [::] tuples enumerating -# ranges of VLAN IDs on named physical networks that are available for allocation. -# All physical networks listed are available for flat and VLAN provider network -# creation. Specified ranges of VLAN IDs are available for tenant network -# allocation if tenant_network_type is 'vlan'. If empty, only gre and local -# networks may be created -# -# Example: network_vlan_ranges = physnet1:1000:2999 -node.override["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] =mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['network_vlan_ranges'] - -# Set to True in the server and the agents to enable support -# for GRE networks. Requires kernel support for OVS patch ports and -# GRE tunneling. -node.override["openstack"]["network"]["openvswitch"]["enable_tunneling"] = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['enable_tunneling'] - -# Comma-separated list of : tuples -# enumerating ranges of GRE tunnel IDs that are available for tenant -# network allocation if tenant_network_type is 'gre'. -# -# Example: tunnel_id_ranges = 1:1000 -node.override["openstack"]["network"]["openvswitch"]["tunnel_id_ranges"] = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['tunnel_id_ranges'] - -# Do not change this parameter unless you have a good reason to. -# This is the name of the OVS integration bridge. There is one per hypervisor. -# The integration bridge acts as a virtual "patch bay". All VM VIFs are -# attached to this bridge and then "patched" according to their network -# connectivity -node.override["openstack"]["network"]["openvswitch"]["integration_bridge"] = mydata['networking']['plugins']['ovs']['integration_bridge'] - -# Only used for the agent if tunnel_id_ranges (above) is not empty for -# the server. In most cases, the node.override value should be fine -node.override["openstack"]["network"]["openvswitch"]["tunnel_bridge"] = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['tunnel_bridge'] - -# Uncomment this line for the agent if tunnel_id_ranges (above) is not -# empty for the server. Set local_ip to be the local IP address of -# this hypervisor or set the local_ip_interface parameter to use the IP -# address of the specified interface. If local_ip_interface is set -# it will take precedence. -#local_ip_interface = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['local_ip_interface'] -local_ip_interface = mydata['networking']['tenant']['interface'] -if local_ip_interface != ("nil") - local_ip= address_for(local_ip_interface) -else - local_ip="nil" -end - -node.override["openstack"]["network"]["openvswitch"]["local_ip"] = local_ip -node.override["openstack"]["network"]["openvswitch"]["local_ip_interface"] = local_ip_interface - -# Comma-separated list of : tuples -# mapping physical network names to the agent's node-specific OVS -# bridge names to be used for flat and VLAN networks. The length of -# bridge names should be no more than 11. Each bridge must -# exist, and should have a physical network interface configured as a -# port. All physical networks listed in network_vlan_ranges on the -# server should have mappings to appropriate bridges on each agent. -# -# Example: bridge_mappings = physnet1:br-eth1 -node.override["openstack"]["network"]["openvswitch"]["bridge_mappings"] = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['bridge_mappings'] - - -# #### nova ##### -node.override["openstack"]["compute"]["network"]["service_type"] = mydata['networking']['nova']['network_type'] - diff --git a/chef/cookbooks/openstack-common/recipes/default.rb b/chef/cookbooks/openstack-common/recipes/default.rb index acadc76..1f88c08 100644 --- a/chef/cookbooks/openstack-common/recipes/default.rb +++ b/chef/cookbooks/openstack-common/recipes/default.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-common # library:: default @@ -17,112 +18,94 @@ # limitations under the License. # -include_recipe "openstack-common::databag" - -case node["platform_family"] -when "debian" - apt_uri = node["openstack"]["apt"]["uri"] - apt_components = node["openstack"]["apt"]["components"] - - # Simple variable substitution for LSB codename and OpenStack release - apt_components.each do | comp | - comp = comp.gsub "%release%", node["openstack"]["release"] - comp = comp.gsub "%codename%", node["lsb"]["codename"] +platform_options = node['openstack']['common']['platform'] +case node['platform_family'] +when 'debian' + package 'ubuntu-cloud-keyring' do + options platform_options['package_overrides'] + action :upgrade end - apt_repository "openstack-ppa" do - uri node["openstack"]["apt"]["uri"] - components apt_components - end - - execute "apt-update" do - user "root" - command "apt-get -y update" - action :run - end - - execute "apt-upgrade" do - user "root" - command "apt-get -y upgrade" - action :run - end - - package "ubuntu-cloud-keyring" do - action :install - end - -when "suse" - if node["lsb"]["description"].nil? - # Workaround for SLE11 - # - # On SLE11 ohai is broken and prefers lsb-release. We need to - # install it to be able to detect if recipe is run on openSUSE or SLES. - # - # https://bugzilla.novell.com/show_bug.cgi?id=809129 - # - # - install_lsb_release = package "lsb-release" do - action :nothing + if node['openstack']['apt']['live_updates_enabled'] + apt_components = node['openstack']['apt']['components'] + # Simple variable substitution for LSB codename and OpenStack release + apt_components.each do | comp | + comp.gsub! '%release%', node['openstack']['release'] + comp.gsub! '%codename%', node['lsb']['codename'] end - reload_ohai = ohai "reload_lsb" do - action :nothing + apt_repository 'openstack-ppa' do + uri node['openstack']['apt']['uri'] + components apt_components end - install_lsb_release.run_action(:install) - reload_ohai.run_action(:reload) end - if node["lsb"]["description"][/^SUSE Linux Enterprise Server/] - release, patchlevel = node["platform_version"].split(".") +when 'rhel' + + cookbook_file '/etc/pki/rpm-gpg/RPM-GPG-KEY-RDO-Icehouse' do + source 'RPM-GPG-KEY-RDO-Icehouse' + mode 00644 + action :create + end + + if node['openstack']['yum']['rdo_enabled'] + repo_action = :add + include_recipe 'yum-epel' + elsif FileTest.exist? "/etc/yum.repos.d/RDO-#{node['openstack']['release']}.repo" + repo_action = :remove + else + repo_action = :nothing + end + + yum_repository "RDO-#{node['openstack']['release']}" do + description "OpenStack RDO repo for #{node['openstack']['release']}" + gpgkey node['openstack']['yum']['repo-key'] + baseurl node['openstack']['yum']['uri'] + enabled true + action repo_action + end + +when 'suse' + if node['lsb']['description'].nil? + # Ohai lsb does not work at all on SLES11SP3 + # See https://tickets.opscode.com/browse/OHAI-454 + # Until then, copy chef's lsb_release parsing code from its lsb module. + package 'lsb-release' + + Mixlib::ShellOut.new('lsb_release -a').run_command.stdout.split("\n").each do |line| + case line + when /^Description:\s+(.+)$/ + node.set_unless['lsb']['description'] = Regexp.last_match[1] + when /^Release:\s+(.+)$/ + node.set_unless['lsb']['release'] = Regexp.last_match[1] + end + end + end + if node['lsb']['description'][/^SUSE Linux Enterprise Server/] + release, patchlevel = node['platform_version'].split('.') zypp_release = "SLE_#{release}_SP#{patchlevel}" - elsif node["lsb"]["description"][/^openSUSE/] - zypp_release = "openSUSE_" + node["lsb"]["release"] + elsif node['lsb']['description'][/^openSUSE/] + zypp_release = 'openSUSE_' + node['lsb']['release'] end - zypp = node["openstack"]["zypp"] - repo_uri = zypp["uri"].gsub( - "%release%", node["openstack"]["release"].capitalize) - repo_uri.gsub! "%suse-release%", zypp_release - repo_alias = "Cloud:OpenStack:" + node["openstack"]["release"].capitalize + zypp = node['openstack']['zypp'] + repo_uri = zypp['uri'].gsub( + '%release%', node['openstack']['release'].capitalize) + repo_uri.gsub! '%suse-release%', zypp_release + repo_alias = 'Cloud:OpenStack:' + node['openstack']['release'].capitalize # TODO(iartarisi) this should be moved to its own cookbook - bash "add repository key" do - cwd "/tmp" + bash 'add repository key' do + cwd '/tmp' code <<-EOH - gpg --keyserver pgp.mit.edu --recv-keys #{zypp["repo-key"]} - gpg --armor --export #{zypp["repo-key"]} > cloud.asc + gpg --keyserver pgp.mit.edu --recv-keys #{zypp['repo-key']} + gpg --armor --export #{zypp['repo-key']} > cloud.asc rpm --import cloud.asc rm -f cloud.asc EOH - not_if { `rpm -qa gpg-pubkey*`.include? zypp["repo-key"].downcase } + not_if { Mixlib::ShellOut.new('rpm -qa gpg-pubkey*').run_command.stdout.include? zypp['repo-key'].downcase } end - execute "add repository" do + execute 'add repository' do command "zypper addrepo --check #{repo_uri} #{repo_alias}" - not_if { `zypper repos --export -`.include? repo_uri } - end - -when "rhel", "fedora" - if node['platform_version'].to_f < 6.4 - Chef::Log.error("The client(IP: #{node['ipaddress']}) OS #{node['platform']} #{node['platform_version']} is lower than 6.4") - return - end - - # add epel repo - include_recipe "yum::epel" - - # add repoforge repo - # include_recipe "yum::repoforge" - - yum_repository "openstack" do - description "redhat packages for openstack" - url node["openstack"]["yum"]["openstack"]["url"] - # mirrorlist true - action :create - end - - execute "yum-update" do - user "root" - command "yum -y update" - action :run + not_if { Mixlib::ShellOut.new('zypper repos --export -').run_command.stdout.include? repo_uri } end end - diff --git a/chef/cookbooks/openstack-common/recipes/environ.rb b/chef/cookbooks/openstack-common/recipes/environ.rb deleted file mode 100644 index 30f191f..0000000 --- a/chef/cookbooks/openstack-common/recipes/environ.rb +++ /dev/null @@ -1,342 +0,0 @@ -# -# Cookbook Name:: openstack-common -# Attributes:: default -# -# Copyright 2014, Futurewei, Inc. -# -# 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. -# -# import initial data from databag with customized user data. -# - -class ::Chef::Recipe - include ::Openstack -end -# Maps variables from environment attributes to attributes that cookbooks understand -node.override['openstack']['developer_mode'] = node['credential']['text'] -node.override['openstack']['release'] = node['release'] - -# Openstack repo setup -# ubuntu -node.override['openstack']['apt']['components'] = [ "precise-updates/#{node['openstack']['release']}", "main" ] - -# redhat -if node['openstack']['release'].eql?("grizzly") - node.override['openstack']['yum']['openstack']['url']="http://repos.fedorapeople.org/repos/openstack/EOL/openstack-#{node['openstack']['release']}/epel-#{node['platform_version'].to_i}/" -else - node.override['openstack']['yum']['openstack']['url']="http://repos.fedorapeople.org/repos/openstack/openstack-#{node['openstack']['release']}/epel-#{node['platform_version'].to_i}/" -end - -# Tenant and user -node.override['openstack']['identity']['admin_token'] = node['credential']['identity']['token']['admin'] -node.override['openstack']['identity']['tenants'] = ["#{ node['credential']['identity']['tenants']['admin']}", "#{ node['credential']['identity']['tenants']['service']}"] -node.override["openstack"]["identity"]["roles"] = ["#{ node['credential']['identity']['roles']['admin']}", "#{ node['credential']['identity']['roles']['member']}"] - -node.override['openstack']['identity']['admin_tenant_name'] = node['credential']['identity']['tenants']['admin'] -node.override['openstack']['identity']['admin_user'] = node['credential']['identity']['users']['admin']['username'] -node.override['openstack']['identity']['admin_password'] = node['credential']['identity']['users']['admin']['password'] - -# services with related usernames and passwords -node['openstack']['services'].each_key do |service| - node.override['openstack']['services']["#{service}"]['name'] = node['services']["#{service}"]['name'] - node.override['openstack']['services']["#{service}"]['status'] = node['services']["#{service}"]['status'] - - if service != "object-store" - node.set['openstack']['db']["#{service}"]['username'] = node['credential']['mysql']["#{service}"]['username'] - node.set['openstack']['db']["#{service}"]['password'] = node['credential']['mysql']["#{service}"]['password'] - end - - if "#{service}" != "identity" and "#{service}" != "dashboard" - node.override['openstack']['identity']["#{service}"]['username'] = node['credential']['identity']['users']["#{service}"]['username'] - node.override['openstack']['identity']["#{service}"]['password'] = node['credential']['identity']['users']["#{service}"]['password'] - node.override['openstack']['identity']["#{service}"]['role'] = node['credential']['identity']['roles']['admin'] - node.set['openstack']['identity']["#{service}"]['tenant'] = node['credential']['identity']['users']["#{service}"]['tenant'] - end -end - -# network plugins -node.override["openstack"]["network"]["plugins"] = ['openvswitch', 'openvswitch-agent'] - - -# ======================== OpenStack Endpoints ================================ -# Identity (keystone) -node.override['openstack']['endpoints']['identity-api']['host'] = node['endpoints']['identity']['service']['host'] -node.override['openstack']['endpoints']['identity-api']['scheme'] = node['endpoints']['identity']['service']['scheme'] -node.override['openstack']['endpoints']['identity-api']['port'] = "5000" -node.override['openstack']['endpoints']['identity-api']['path'] = "/v2.0" - -node.override['openstack']['endpoints']['identity-admin']['host'] = node['endpoints']['identity']['admin']['host'] -node.override['openstack']['endpoints']['identity-admin']['scheme'] = node['endpoints']['identity']['admin']['scheme'] -node.override['openstack']['endpoints']['identity-admin']['port'] = "35357" -node.override['openstack']['endpoints']['identity-admin']['path'] = "/v2.0" - -# Compute (Nova) -node.override['openstack']['endpoints']['compute-api']['host'] = node['endpoints']['compute']['service']['host'] -node.override['openstack']['endpoints']['compute-api']['scheme'] = node['endpoints']['compute']['service']['scheme'] -#node.override['openstack']['endpoints']['compute-api']['port'] = "8774" -#node.override['openstack']['endpoints']['compute-api']['path'] = "/v2/%(tenant_id)s" - -# The OpenStack Compute (Nova) EC2 API endpoint -node.override['openstack']['endpoints']['compute-ec2-api']['host'] = node['endpoints']['ec2']['service']['host'] -node.override['openstack']['endpoints']['compute-ec2-api']['scheme'] = node['endpoints']['ec2']['service']['scheme'] -#node.override['openstack']['endpoints']['compute-ec2-api']['port'] = "8773" -#node.override['openstack']['endpoints']['compute-ec2-api']['path'] = "/services/Cloud" - -# The OpenStack Compute (Nova) EC2 Admin API endpoint -node.override['openstack']['endpoints']['compute-ec2-admin']['host'] = node['endpoints']['ec2']['admin']['host'] -node.override['openstack']['endpoints']['compute-ec2-admin']['scheme'] = node['endpoints']['ec2']['admin']['scheme'] -#node.override['openstack']['endpoints']['compute-ec2-admin']['port'] = "8773" -#node.override['openstack']['endpoints']['compute-ec2-admin']['path'] = "/services/Admin" - -# The OpenStack Compute (Nova) XVPvnc endpoint -node.override['openstack']['endpoints']['compute-xvpvnc']['host'] = node['endpoints']['compute']['xvpvnc']['host'] -node.override['openstack']['endpoints']['compute-xvpvnc']['scheme'] = node['endpoints']['compute']['xvpvnc']['scheme'] -#node.override['openstack']['endpoints']['compute-xvpvnc']['port'] = "6081" -#node.override['openstack']['endpoints']['compute-xvpvnc']['path'] = "/console" - -# The OpenStack Compute (Nova) novnc endpoint -node.override['openstack']['endpoints']['compute-novnc']['host'] = node['endpoints']['compute']['novnc']['host'] -node.override['openstack']['endpoints']['compute-novnc']['scheme'] = node['endpoints']['compute']['novnc']['scheme'] -#node.override['openstack']['endpoints']['compute-novnc']['port'] = "6080" -#node.override['openstack']['endpoints']['compute-novnc']['path'] = "/vnc_auto.html" - -# Network (Quantum) -node.override['openstack']['endpoints']['network-api']['host'] = node['endpoints']['network']['service']['host'] -node.override['openstack']['endpoints']['network-api']['scheme'] = node['endpoints']['network']['service']['scheme'] -#node.override['openstack']['endpoints']['network-api']['port'] = "9696" -# quantumclient appends the protocol version to the endpoint URL, so the - -# Image (Glance) -node.override['openstack']['endpoints']['image-api']['host'] = node['endpoints']['image']['service']['host'] -node.override['openstack']['endpoints']['image-api']['scheme'] = node['endpoints']['image']['service']['scheme'] -#node.override['openstack']['endpoints']['image-api']['port'] = "9292" -#node.override['openstack']['endpoints']['image-api']['path'] = "/v2" - -# Image (Glance) Registry -node.override['openstack']['endpoints']['image-registry']['host'] = node['endpoints']['image']['registry']['host'] -node.override['openstack']['endpoints']['image-registry']['scheme'] = node['endpoints']['image']['registry']['scheme'] -node.override['openstack']['endpoints']['image-registry']['port'] = "9191" -node.override['openstack']['endpoints']['image-registry']['path'] = "/v2" - -# Volume (Cinder) -node.override['openstack']['endpoints']['volume-api']['host'] = node['endpoints']['volume']['service']['host'] -node.override['openstack']['endpoints']['volume-api']['scheme'] = node['endpoints']['volume']['service']['scheme'] -#node.override['openstack']['endpoints']['volume-api']['port'] = "8776" -#node.override['openstack']['endpoints']['volume-api']['path'] = "/v1/%(tenant_id)s" - -# Metering (Ceilometer) -node.override['openstack']['endpoints']['metering-api']['host'] = node['endpoints']['metering']['service']['host'] -node.override['openstack']['endpoints']['metering-api']['scheme'] = node['endpoints']['metering']['service']['scheme'] -#node.override['openstack']['endpoints']['metering-api']['port'] = "8777" -#node.override['openstack']['endpoints']['metering-api']['path'] = "/v1" - -# ======================== OpenStack DB Support ================================ -# set database attributes -#node.override['openstack']['db']['server_role'] = "os-ops-database" -node.override['openstack']['db']['service_type'] = node['db']['service_type'] -node.override['openstack']['db']['bind_address'] = node['db']["#{node['openstack']['db']['service_type']}"]['bind_address'] -node.override['openstack']['db']['port'] = node['db']["#{node['openstack']['db']['service_type']}"]['port'] - -node.override['openstack']['db']['super']['username'] = node['credential']["#{node['openstack']['db']['service_type']}"]['super']['username'] -node.override['openstack']['db']['super']['password'] = node['credential']["#{node['openstack']['db']['service_type']}"]['super']['password'] - -# Database used by the OpenStack Compute (Nova) service -node.override['openstack']['db']['compute']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['compute']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['compute']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['compute']['db_name'] = "nova" - -# Database used by the OpenStack Identity (Keystone) service -node.override['openstack']['db']['identity']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['identity']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['identity']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['identity']['db_name'] = "keystone" - -# Database used by the OpenStack Image (Glance) service -node.override['openstack']['db']['image']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['image']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['image']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['image']['db_name'] = "glance" - -# Database used by the OpenStack Network (Quantum) service -node.override['openstack']['db']['network']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['network']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['network']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['network']['db_name'] = "quantum" - -# Database used by the OpenStack Volume (Cinder) service -node.override['openstack']['db']['volume']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['volume']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['volume']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['volume']['db_name'] = "cinder" - -# Database used by the OpenStack Dashboard (Horizon) -node.override['openstack']['db']['dashboard']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['dashboard']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['dashboard']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['dashboard']['db_name'] = "horizon" - -# Database used by OpenStack Metering (Ceilometer) -node.override['openstack']['db']['metering']['db_type'] = node['openstack']['db']['service_type'] -node.override['openstack']['db']['metering']['host'] = node['openstack']['db']['bind_address'] -node.override['openstack']['db']['metering']['port'] = node['openstack']['db']['port'] -#node.override['openstack']['db']['metering']['db_name'] = "ceilometer" - -# node.override database attributes -#node.override['openstack']['mq']['server_role'] = "os-ops-messaging" -node.override['openstack']['mq']['service_type'] = node['mq']['service_type'] -node.override['openstack']['mq']['bind_address'] = node['mq']["#{node['openstack']['mq']['service_type']}"]['bind_address'] -node.override['openstack']['mq']['port'] = node['mq']["#{node['openstack']['mq']['service_type']}"]['port'] -node.override['openstack']['mq']['user'] = node['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['username'] -node.override['openstack']['mq']['password'] = node['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['password'] -#node.override['openstack']['mq']['vhost'] = "/" - - - -# ============================= Network service Configuration ===================== -# Gets set in the Network Endpoint when registering with Keystone -node.override["openstack"]["network"]["service_user"] = node['openstack']['identity']['network']['username'] -node.override["openstack"]["network"]["service_role"] = node['openstack']['identity']['network']['role'] -node.override["openstack"]["network"]["service_name"] = node['openstack']['services']['network']['name'] -# node.override["openstack"]["network"]["service_type"] = "network" -# node.override["openstack"]["network"]["description"] = "OpenStack Networking service" - -# The rabbit user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# user_password routine. You are expected to create -# the user, pass, vhost in a wrapper rabbitmq cookbook. -#node.override["openstack"]["network"]["rabbit_server_chef_role"] = "rabbitmq-server" -node.override["openstack"]["network"]["rabbit"]["username"] = node['openstack']['mq']['user'] -#node.override["openstack"]["network"]["rabbit"]["vhost"] = "/" -node.override["openstack"]["network"]["rabbit"]["port"] = node['openstack']['mq']['port'] -node.override["openstack"]["network"]["rabbit"]["host"] = node['openstack']['mq']['bind_address'] -#node.override["openstack"]["network"]["rabbit"]["ha"] = false - -# The database username for the quantum database -node.override["openstack"]["network"]["db"]["username"] = node['credential']['mysql']['network']['username'] - -# Used in the Keystone authtoken middleware configuration -node.override["openstack"]["network"]["service_tenant_name"] = node['openstack']['identity']['network']['tenant'] -node.override["openstack"]["network"]["service_user"] = node['openstack']['identity']['network']['username'] -node.override["openstack"]["network"]["service_role"] = node['openstack']['identity']['network']['role'] - -node.override["openstack"]["network"]["api"]["bind_interface"] = node['networking']['control']['interface'] - -node.set['openstack']['networking']['control']['interface'] = node['networking']['control']['interface'] -node.set['openstack']['networking']['tenant']['interface'] = node['networking']['tenant']['interface'] -node.set['openstack']['networking']['public']['interface'] = node['networking']['public']['interface'] -node.set['openstack']['networking']['storage']['interface'] = node['networking']['storage']['interface'] - - - -# ============================= L3 Agent Configuration ===================== - -# Indicates that this L3 agent should also handle routers that do not have -# an external network gateway configured. This option should be True only -# for a single agent in a Quantum deployment, and may be False for all agents -# if all routers must have an external network gateway -#node.override["openstack"]["network"]["l3"]["handle_internal_only_routers"] = "False" - -# Name of bridge used for external network traffic. This should be set to -# empty value for the linux bridge -# node.override["openstack"]["network"]["l3"]["external_network_bridge"] = "br-ex" - -# Interface to use for external bridge. -node.override["openstack"]["network"]["l3"]["external_network_bridge_interface"] = node['networking']['public']['interface'] - - -# ============================= Metadata Agent Configuration =============== - -# The location of the Nova Metadata API service to proxy to (nil uses node.override) -node.override["openstack"]["network"]["metadata"]["nova_metadata_ip"] = node['endpoints']['compute']['service']['host'] -#node.override["openstack"]["network"]["metadata"]["nova_metadata_port"] = 8775 - -# The name of the secret databag containing the metadata secret -node.override["openstack"]["network"]["metadata"]["secret_name"] = node['credential']['metadata']['password'] - - -# ============================= OVS Plugin Configuration =================== - -# Type of network to allocate for tenant networks. The node.override value 'local' is -# useful only for single-box testing and provides no connectivity between hosts. -# You MUST either change this to 'vlan' and configure network_vlan_ranges below -# or change this to 'gre' and configure tunnel_id_ranges below in order for tenant -# networks to provide connectivity between hosts. Set to 'none' to disable creation -# of tenant networks. - -tenant_network_type = node['networking']['plugins']['ovs']['tenant_network_type'] -node.override["openstack"]["network"]["openvswitch"]["tenant_network_type"] =tenant_network_type - -# Comma-separated list of [::] tuples enumerating -# ranges of VLAN IDs on named physical networks that are available for allocation. -# All physical networks listed are available for flat and VLAN provider network -# creation. Specified ranges of VLAN IDs are available for tenant network -# allocation if tenant_network_type is 'vlan'. If empty, only gre and local -# networks may be created -# -# Example: network_vlan_ranges = physnet1:1000:2999 -node.override["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] =node['networking']['plugins']['ovs']["#{tenant_network_type}"]['network_vlan_ranges'] - -# Set to True in the server and the agents to enable support -# for GRE networks. Requires kernel support for OVS patch ports and -# GRE tunneling. -node.override["openstack"]["network"]["openvswitch"]["enable_tunneling"] = node['networking']['plugins']['ovs']["#{tenant_network_type}"]['enable_tunneling'] - -# Comma-separated list of : tuples -# enumerating ranges of GRE tunnel IDs that are available for tenant -# network allocation if tenant_network_type is 'gre'. -# -# Example: tunnel_id_ranges = 1:1000 -node.override["openstack"]["network"]["openvswitch"]["tunnel_id_ranges"] = node['networking']['plugins']['ovs']["#{tenant_network_type}"]['tunnel_id_ranges'] - -# Do not change this parameter unless you have a good reason to. -# This is the name of the OVS integration bridge. There is one per hypervisor. -# The integration bridge acts as a virtual "patch bay". All VM VIFs are -# attached to this bridge and then "patched" according to their network -# connectivity -node.override["openstack"]["network"]["openvswitch"]["integration_bridge"] = node['networking']['plugins']['ovs']['integration_bridge'] - -# Only used for the agent if tunnel_id_ranges (above) is not empty for -# the server. In most cases, the node.override value should be fine -node.override["openstack"]["network"]["openvswitch"]["tunnel_bridge"] = node['networking']['plugins']['ovs']["#{tenant_network_type}"]['tunnel_bridge'] - -# Uncomment this line for the agent if tunnel_id_ranges (above) is not -# empty for the server. Set local_ip to be the local IP address of -# this hypervisor or set the local_ip_interface parameter to use the IP -# address of the specified interface. If local_ip_interface is set -# it will take precedence. -#local_ip_interface = node['networking']['plugins']['ovs']["#{tenant_network_type}"]['local_ip_interface'] -local_ip_interface = node['networking']['tenant']['interface'] -if local_ip_interface != ("nil") - local_ip= address_for(local_ip_interface) -else - local_ip="nil" -end - -node.override["openstack"]["network"]["openvswitch"]["local_ip"] = local_ip -node.override["openstack"]["network"]["openvswitch"]["local_ip_interface"] = local_ip_interface - -# Comma-separated list of : tuples -# mapping physical network names to the agent's node-specific OVS -# bridge names to be used for flat and VLAN networks. The length of -# bridge names should be no more than 11. Each bridge must -# exist, and should have a physical network interface configured as a -# port. All physical networks listed in network_vlan_ranges on the -# server should have mappings to appropriate bridges on each agent. -# -# Example: bridge_mappings = physnet1:br-eth1 -node.override["openstack"]["network"]["openvswitch"]["bridge_mappings"] = node['networking']['plugins']['ovs']["#{tenant_network_type}"]['bridge_mappings'] - - -# #### nova ##### -node.override["openstack"]["compute"]["network"]["service_type"] = node['networking']['nova']['network_type'] diff --git a/chef/cookbooks/openstack-common/recipes/logging.rb b/chef/cookbooks/openstack-common/recipes/logging.rb index 877ff5f..544abf2 100644 --- a/chef/cookbooks/openstack-common/recipes/logging.rb +++ b/chef/cookbooks/openstack-common/recipes/logging.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-common # library:: logging @@ -17,16 +18,16 @@ # limitations under the License. # -directory "/etc/openstack" do - owner "root" - group "root" +directory '/etc/openstack' do + owner 'root' + group 'root' mode 00755 action :create end -template "/etc/openstack/logging.conf" do - source "logging.conf.erb" - owner "root" - group "root" +template '/etc/openstack/logging.conf' do + source 'logging.conf.erb' + owner 'root' + group 'root' mode 00644 end diff --git a/chef/cookbooks/openstack-common/recipes/openrc.rb b/chef/cookbooks/openstack-common/recipes/openrc.rb new file mode 100644 index 0000000..e2a26ea --- /dev/null +++ b/chef/cookbooks/openstack-common/recipes/openrc.rb @@ -0,0 +1,58 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-common +# recipe:: openrc +# +# Copyright 2014 IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +# check attributes before searching +if node['openstack']['identity'] && node['openstack']['identity']['admin_tenant_name'] && node['openstack']['identity']['admin_user'] + ksadmin_tenant_name = node['openstack']['identity']['admin_tenant_name'] + ksadmin_user = node['openstack']['identity']['admin_user'] +else + identity_service_role = node['openstack']['identity_service_chef_role'] + keystone = search_for(identity_service_role).first + + if keystone.nil? + Chef::Log.warn("openrc not created, identity role node not found: #{identity_service_role}") + return + end + + ksadmin_tenant_name = keystone['openstack']['identity']['admin_tenant_name'] + ksadmin_user = keystone['openstack']['identity']['admin_user'] +end + +ksadmin_pass = get_password 'user', ksadmin_user +identity_endpoint = endpoint 'identity-api' + +template '/root/openrc' do + source 'openrc.erb' + # TODO: (MRV) Consider making the name, location, contents and owner/group + # of this more flexible with attributes. + owner 'root' + group 'root' + mode 00600 + variables( + user: ksadmin_user, + tenant: ksadmin_tenant_name, + password: ksadmin_pass, + identity_endpoint: identity_endpoint.to_s + ) +end diff --git a/chef/cookbooks/openstack-common/recipes/set_endpoints_by_interface.rb b/chef/cookbooks/openstack-common/recipes/set_endpoints_by_interface.rb new file mode 100644 index 0000000..63194bf --- /dev/null +++ b/chef/cookbooks/openstack-common/recipes/set_endpoints_by_interface.rb @@ -0,0 +1,31 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-common +# recipe:: set_endpoints_by_interface +# +# Copyright 2013, Opscode, Inc. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +# iterate over the endpoints, look for bind_interface to set the host +node['openstack']['endpoints'].keys.each do |component| + unless node['openstack']['endpoints'][component]['bind_interface'].nil? + ip_address = address node['openstack']['endpoints'][component] + node.default['openstack']['endpoints'][component]['host'] = ip_address + end +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng-client.rb b/chef/cookbooks/openstack-common/recipes/sysctl.rb similarity index 57% rename from chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng-client.rb rename to chef/cookbooks/openstack-common/recipes/sysctl.rb index dab7cea..5035f2e 100644 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng-client.rb +++ b/chef/cookbooks/openstack-common/recipes/sysctl.rb @@ -1,6 +1,7 @@ +# encoding: UTF-8 # -# Cookbook Name:: apt_test -# Recipe:: cacher-ng-client +# Cookbook Name:: openstack-common +# recipe:: sysctl # # Copyright 2013, Opscode, Inc. # @@ -17,8 +18,20 @@ # limitations under the License. # -include_recipe "apt::cacher-ng" -include_recipe "apt::cacher-client" +directory '/etc/sysctl.d' do + owner 'root' + group 'root' + mode 00755 +end -#install a small, innocuous application to verify this works -package "colordiff" +template '/etc/sysctl.d/60-openstack.conf' do + source '60-openstack.conf.erb' + owner 'root' + group 'root' + mode 00644 +end + +execute 'sysctl -p /etc/sysctl.d/60-openstack.conf' do + action :nothing + subscribes :run, 'template[/etc/sysctl.d/60-openstack.conf]', :immediately +end diff --git a/chef/cookbooks/openstack-common/spec/ceph_spec.rb b/chef/cookbooks/openstack-common/spec/ceph_spec.rb new file mode 100644 index 0000000..22a56fb --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/ceph_spec.rb @@ -0,0 +1,70 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-common::ceph_client' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['ceph']['global']['fsid'] = '9e5038a9-4329-4cad-8c24-0813a49d1125' + node.set['openstack']['ceph']['global']['mon_initial_members'] = %w{ 10.0.1.10 10.0.1.20 } + node.set['openstack']['ceph']['global']['mon_hosts'] = %w{ mon01 mon02 } + node.set['lsb']['codename'] = 'precise' + + runner.converge(described_recipe) + end + let(:file) { chef_run.template('/etc/ceph/ceph.conf') } + + it 'configures ceph repository' do + # Using cookbook(apt) LWRP custom matcher + # https://github.com/sethvargo/chefspec#packaging-custom-matchers + expect(chef_run).to add_apt_repository('ceph').with( + uri: 'http://ceph.com/debian-emperor', + components: ['main'], + distribution: 'precise') + end + + it 'creates the /etc/ceph directory' do + expect(chef_run).to create_directory('/etc/ceph').with( + owner: 'root', + group: 'root' + ) + end + + context 'configuration file' do + it 'creates the file' do + expect(chef_run).to create_template(file.name).with( + owner: 'root', + group: 'root', + mode: '644' + ) + end + + it 'sets file contents from the global ceph configuration attributes' do + node.set['openstack']['ceph']['global'] = { + 'key_1' => %w(value_1_1 value_1_2), + 'key_2' => 'value_2' + } + [/^key_1 = value_1_1, value_1_2$/, + /^key_2 = value_2$/].each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + end + end + describe 'when setup_client is not set' do + let(:chef_run) do + node.set['openstack']['ceph']['setup_client'] = false + node.set['lsb']['codename'] = 'precise' + + runner.converge(described_recipe) + end + + it "doesn't add the repository or create ceph.conf" do + expect(chef_run).not_to create_directory('/etc/ceph') + expect(chef_run).not_to create_template('/etc/ceph/ceph.conf') + expect(chef_run).not_to add_apt_repository('ceph') + end + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/cli_spec.rb b/chef/cookbooks/openstack-common/spec/cli_spec.rb new file mode 100755 index 0000000..932fb3a --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/cli_spec.rb @@ -0,0 +1,104 @@ +# encoding: UTF-8 +require_relative 'spec_helper' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'cli' + +describe 'openstack-common::default' do + describe 'Openstack CLI' do + let(:runner) { ChefSpec::Runner.new(CHEFSPEC_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + let(:subject) { Object.new.extend(Openstack) } + + include_context 'library-stubs' + + describe 'openstack_command_env' do + it 'returns cli enviroment' do + subject.stub(:get_password) + .with('user', 'name') + .and_return('pass') + + expect( + subject.openstack_command_env('name', 'tenant') + ).to eq( + 'OS_USERNAME' => 'name', + 'OS_PASSWORD' => 'pass', + 'OS_TENANT_NAME' => 'tenant', + 'OS_AUTH_URL' => 'http://127.0.0.1:35357/v2.0' + ) + end + end + + describe 'openstack_command' do + it 'runs openstack command' do + env = + { + 'OS_USERNAME' => 'name', + 'OS_PASSWORD' => 'pass', + 'OS_TENANT_NAME' => 'tenant', + 'OS_AUTH_URL' => 'http://127.0.0.1:35357/v2.0' + } + subject.stub(:shell_out).with( + ['keystone', 'user-list'], + env: env + ).and_return double('shell_out', exitstatus: 0, stdout: 'good', stderr: '') + + result = subject.openstack_command('keystone', 'user-list', env) + expect(result).to eq('good') + end + + it 'runs openstack command with args' do + env = + { + 'OS_USERNAME' => 'name', + 'OS_PASSWORD' => 'pass', + 'OS_TENANT_NAME' => 'tenant', + 'OS_AUTH_URL' => 'http://127.0.0.1:35357/v2.0' + } + subject.stub(:shell_out).with( + %w(keystone user-list --key1 value1 --key2 value2), + env: env + ).and_return double('shell_out', exitstatus: 0, stdout: 'good', stderr: '') + + result = subject.openstack_command('keystone', 'user-list', env, 'key1' => 'value1', 'key2' => 'value2') + expect(result).to eq('good') + end + + it 'runs openstack command with failure' do + env = + { + 'OS_USERNAME' => 'name', + 'OS_PASSWORD' => 'pass', + 'OS_TENANT_NAME' => 'tenant', + 'OS_AUTH_URL' => 'http://127.0.0.1:35357/v2.0' + } + subject.stub(:shell_out).with( + ['keystone', 'user-list'], + env: env + ).and_return double('shell_out', exitstatus: 123, stdout: 'fail', stderr: '') + + # TODO: need to figure out why this won't work. + # expect(subject.openstack_command('keystone', 'user-list', env)).to fail + end + end + + describe 'identity_uuid' do + it 'runs identity command to query uuid' do + env = + { + 'OS_USERNAME' => 'name', + 'OS_PASSWORD' => 'pass', + 'OS_TENANT_NAME' => 'tenant', + 'OS_AUTH_URL' => 'http://127.0.0.1:35357/v2.0' + } + subject.stub(:openstack_command).with('keystone', 'user-list', env, {}) + subject.stub(:prettytable_to_array) + .and_return([{ 'name' => 'user1', 'id' => '1234567890ABCDEFGH' }]) + + result = subject.identity_uuid('user', 'name', 'user1', env) + expect(result).to eq('1234567890ABCDEFGH') + end + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/client-redhat_spec.rb b/chef/cookbooks/openstack-common/spec/client-redhat_spec.rb new file mode 100755 index 0000000..e90a996 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/client-redhat_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-common::client' do + + describe 'redhat' do + + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades common client packages' do + expect(chef_run).to upgrade_package('python-openstackclient') + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/client_spec.rb b/chef/cookbooks/openstack-common/spec/client_spec.rb new file mode 100755 index 0000000..f7142a2 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/client_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-common::client' do + + describe 'ubuntu' do + + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades common client packages' do + expect(chef_run).to upgrade_package('python-openstackclient') + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/database_spec.rb b/chef/cookbooks/openstack-common/spec/database_spec.rb index 5b3f0fa..877a7c8 100644 --- a/chef/cookbooks/openstack-common/spec/database_spec.rb +++ b/chef/cookbooks/openstack-common/spec/database_spec.rb @@ -1,39 +1,48 @@ -require_relative "spec_helper" -require ::File.join ::File.dirname(__FILE__), "..", "libraries", "database" +# encoding: UTF-8 +require_relative 'spec_helper' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'database' -describe ::Openstack do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::CHEFSPEC_OPTS - @chef_run.converge "openstack-common::default" - @subject = ::Object.new.extend ::Openstack - @subject.stub :include_recipe - end +describe 'openstack-common::default' do + describe 'Openstack Database' do + let(:runner) { ChefSpec::Runner.new(CHEFSPEC_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + let(:subject) { Object.new.extend(Openstack) } - describe "#db_create_with_user" do - it "returns nil when no such service was found" do - @subject.stub(:node).and_return @chef_run.node - @subject.db_create_with_user("nonexisting", "user", "pass").should be_nil - end + include_context 'library-stubs' - it "returns db info and creates database with user when service found" do - @subject.stub(:database).and_return {} - @subject.stub(:database_user).and_return {} - @subject.stub(:node).and_return @chef_run.node - result = @subject.db_create_with_user "compute", "user", "pass" - result['host'].should == "127.0.0.1" - result['port'].should == "3306" - end + describe '#db_create_with_user' do + before do + subject.stub(:include_recipe) + .with('database::mysql') + .and_return('') + end - it "creates database" do - pending "TODO: test this LWRP" - end + it 'returns nil when no such service was found' do + expect( + subject.db_create_with_user('nonexisting', 'user', 'pass') + ).to be_nil + end - it "creates database user" do - pending "TODO: test this LWRP" - end + it 'returns db info and creates database with user when service found' do + subject.stub(:database).and_return({}) + subject.stub(:database_user).and_return({}) + result = subject.db_create_with_user('compute', 'user', 'pass') + expect(result['host']).to eq('127.0.0.1') + expect(result['port']).to eq('3306') + end - it "grants privs to database user" do - pending "TODO: test this LWRP" + it 'creates database' do + pending 'TODO: test this LWRP' + end + + it 'creates database user' do + pending 'TODO: test this LWRP' + end + + it 'grants privs to database user' do + pending 'TODO: test this LWRP' + end end end end diff --git a/chef/cookbooks/openstack-common/spec/default-redhat_spec.rb b/chef/cookbooks/openstack-common/spec/default-redhat_spec.rb new file mode 100644 index 0000000..508e169 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/default-redhat_spec.rb @@ -0,0 +1,53 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-common::default' do + describe 'rhel-rdo' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['release'] = 'testrelease' + + runner.converge(described_recipe) + end + + context 'enabling RDO' do + before do + node.set['openstack']['yum']['rdo_enabled'] = true + end + + it 'adds RDO yum repository' do + # Using cookbook(yum) LWRP custom matcher + # https://github.com/sethvargo/chefspec#packaging-custom-matchers + expect(chef_run).to add_yum_repository('RDO-testrelease') + end + + it 'includes yum-epel recipe' do + expect(chef_run).to include_recipe('yum-epel') + end + end + + context 'disabling RDO' do + before do + node.set['openstack']['yum']['rdo_enabled'] = false + end + + it 'removes RDO yum repository' do + ::FileTest.stub(:exist?).with('/etc/yum.repos.d/RDO-testrelease.repo').and_return(true) + + # Using cookbook(yum) LWRP custom matcher + # https://github.com/sethvargo/chefspec#packaging-custom-matchers + expect(chef_run).to remove_yum_repository('RDO-testrelease') + end + + it 'does nothing when RDO yum repository does not exist' do + repo = chef_run.find_resource('yum_repository', 'RDO-testrelease') + expect(repo.performed_actions).to be_empty + end + + it 'does not include yum-epel recipe' do + expect(chef_run).to_not include_recipe('yum-epel') + end + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/default-suse_spec.rb b/chef/cookbooks/openstack-common/spec/default-suse_spec.rb index d0d0118..d53b2c3 100644 --- a/chef/cookbooks/openstack-common/spec/default-suse_spec.rb +++ b/chef/cookbooks/openstack-common/spec/default-suse_spec.rb @@ -1,9 +1,46 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-common::default" do - describe "suse" do - it "configures openstack repository" do - pending "TODO: implement" +describe 'openstack-common::default' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set_unless['lsb']['description'] = 'SUSE Linux Enterprise Server 11 (x86_64)' + node.set_unless['lsb']['release'] = '11' + runner.converge(described_recipe) + end + + it 'adds the openstack repository key, but not the repository' do + Mixlib::ShellOut.stub_chain( + new: 'rpm -qa gpg-pubkey', run_command: nil, stdout: nil, + new: 'zypper repos --export -').and_return( + 'http://download.opensuse.org/repositories/Cloud:/OpenStack:/Icehouse/SLE_11_SP3/') + + expect(chef_run).to run_bash('add repository key') + expect(chef_run).not_to run_execute('add repository').with(command: /zypper addrepo/) + end + + it 'adds the repository and the key' do + Mixlib::ShellOut.stub_chain( + new: 'rpm -qa gpg-pubkey', run_command: nil, stdout: nil, + new: 'zypper repos --export -').and_return('') + + expect(chef_run).to run_bash('add repository key') + expect(chef_run).to run_execute('add repository').with( + command: 'zypper addrepo --check '\ + "http://download.opensuse.org/repositories/Cloud:/OpenStack:/#{node['openstack']['release'].capitalize}/SLE_11_SP3/ "\ + 'Cloud:OpenStack:Icehouse') + end + + it 'does not add the repository nor the key' do + Mixlib::ShellOut.stub_chain( + new: 'rpm -qa gpg-pubkey', run_command: nil, stdout: nil, + new: 'zypper repos --export -').and_return( + 'd85f9316', + 'http://download.opensuse.org/repositories/Cloud:/OpenStack:/Icehouse/SLE_11_SP3/') + expect(chef_run).not_to run_bash('add repository key') + expect(chef_run).not_to run_execute('add repository').with(command: /zypper addrepo/) end end end diff --git a/chef/cookbooks/openstack-common/spec/default_spec.rb b/chef/cookbooks/openstack-common/spec/default_spec.rb index c250945..3205ea7 100644 --- a/chef/cookbooks/openstack-common/spec/default_spec.rb +++ b/chef/cookbooks/openstack-common/spec/default_spec.rb @@ -1,24 +1,34 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-common::default" do - describe "ubuntu" do - before do - opts = ::UBUNTU_OPTS.merge :step_into => ["apt_repository"] - @chef_run = ::ChefSpec::ChefRunner.new(opts) do |n| - n.set["lsb"]["codename"] = "precise" - end - @chef_run.converge "openstack-common::default" +describe 'openstack-common::default' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['lsb']['codename'] = 'precise' + + runner.converge(described_recipe) end - it "installs ubuntu-cloud-keyring package" do - expect(@chef_run).to install_package "ubuntu-cloud-keyring" + it 'upgrades ubuntu-cloud-keyring package' do + expect(chef_run).to upgrade_package 'ubuntu-cloud-keyring' end - it "configures openstack repository" do - file = "/etc/apt/sources.list.d/openstack-ppa.list" - expected = "deb http://ubuntu-cloud.archive.canonical.com/ubuntu precise-updates/grizzly main" + it 'configures openstack repository' do + # Using cookbook(apt) LWRP custom matcher + # https://github.com/sethvargo/chefspec#packaging-custom-matchers + node.set['openstack']['apt']['live_updates_enabled'] = true + expect(chef_run).to add_apt_repository('openstack-ppa').with( + uri: 'http://ubuntu-cloud.archive.canonical.com/ubuntu', + components: ['precise-updates/icehouse', 'main']) + end - expect(@chef_run).to create_file_with_content file, expected + it 'disables openstack live updates' do + node.set['openstack']['apt']['live_updates_enabled'] = false + expect(chef_run).to_not add_apt_repository('openstack-ppa').with( + uri: 'http://ubuntu-cloud.archive.canonical.com/ubuntu', + components: ['precise-updates/icehouse', 'main']) end end end diff --git a/chef/cookbooks/openstack-common/spec/endpoints_spec.rb b/chef/cookbooks/openstack-common/spec/endpoints_spec.rb index db49285..d0a0a94 100644 --- a/chef/cookbooks/openstack-common/spec/endpoints_spec.rb +++ b/chef/cookbooks/openstack-common/spec/endpoints_spec.rb @@ -1,133 +1,253 @@ -require_relative "spec_helper" -require ::File.join ::File.dirname(__FILE__), "..", "libraries", "endpoints" +# encoding: UTF-8 +require_relative 'spec_helper' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'uri' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'endpoints' -describe ::Openstack do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::CHEFSPEC_OPTS - @chef_run.converge "openstack-common::default" - @subject = ::Object.new.extend ::Openstack - end +describe 'openstack-common::set_endpoints_by_interface' do + describe 'Openstack endpoints' do + let(:runner) { ChefSpec::Runner.new(CHEFSPEC_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + let(:subject) { Object.new.extend(Openstack) } - describe "#endpoint" do - it "returns nil when no openstack.endpoints not in node attrs" do - @subject.stub(:node).and_return {} - @subject.endpoint("nonexisting").should be_nil - end - it "returns nil when no such endpoint was found" do - @subject.stub(:node).and_return @chef_run.node - @subject.endpoint("nonexisting").should be_nil - end - it "handles a URI needing escaped" do - uri_hash = { - "openstack" => { - "endpoints" => { - "compute-api" => { - "uri" => "http://localhost:8080/v2/%(tenant_id)s" - } - } - } - } - @subject.stub(:node).and_return uri_hash - result = @subject.endpoint "compute-api" - result.path.should == "/v2/%25(tenant_id)s" - end - it "returns endpoint URI object when uri key in endpoint hash" do - uri_hash = { - "openstack" => { - "endpoints" => { - "compute-api" => { - "uri" => "http://localhost:8080/path" - } - } - } - } - @subject.stub(:node).and_return uri_hash - result = @subject.endpoint "compute-api" - result.port.should == 8080 - end - it "returns endpoint URI string when uri key in endpoint hash and host also in hash" do - uri_hash = { - "openstack" => { - "endpoints" => { - "compute-api" => { - "uri" => "http://localhost", - "host" => "ignored" - } - } - } - } - @subject.stub(:node).and_return uri_hash - @subject.endpoint("compute-api").to_s.should == "http://localhost" - end - it "returns endpoint URI object when uri key not in endpoint hash but host is in hash" do - @subject.should_receive(:uri_from_hash).with({"host"=>"localhost", "port"=>"8080"}) - uri_hash = { - "openstack" => { - "endpoints" => { - "compute-api" => { - "host" => "localhost", - "port" => "8080" - } - } - } - } - @subject.stub(:node).and_return uri_hash - @subject.endpoint "compute-api" - end - end - - describe "#endpoints" do - it "does nothing when no endpoints" do - @subject.stub(:node).and_return {} - @subject.endpoints.should be_nil - end - it "does nothing when empty endpoints" do - @subject.stub(:node).and_return({"openstack" => { "endpoints" => {}}}) - @count = 0 - @subject.endpoints do | ep | - @count += 1 + describe '#endpoint' do + it 'returns nil when no openstack.endpoints not in node attrs' do + subject.stub(:node).and_return({}) + expect( + subject.endpoint('nonexisting') + ).to be_nil end - @count.should == 0 - end - it "executes block count when have endpoints" do - @subject.stub(:node).and_return @chef_run.node - @count = 0 - @subject.endpoints do |ep| - @count += 1 + + it 'returns nil when no such endpoint was found' do + subject.stub(:node).and_return(node) + expect( + subject.endpoint('nonexisting') + ).to be_nil end - @count.should >= 1 - end - end - describe "#db" do - it "returns nil when no openstack.db not in node attrs" do - @subject.stub(:node).and_return {} - @subject.db("nonexisting").should be_nil - end - it "returns nil when no such service was found" do - @subject.stub(:node).and_return @chef_run.node - @subject.db("nonexisting").should be_nil - end - it "returns db info hash when service found" do - @subject.stub(:node).and_return @chef_run.node - @subject.db("compute")['host'].should == "127.0.0.1" - @subject.db("compute").has_key?("uri").should be_false - end - end + it 'handles a URI needing escaped' do + uri_hash = { + 'openstack' => { + 'endpoints' => { + 'compute-api' => { + 'uri' => 'http://localhost:8080/v2/%(tenant_id)s' + } + } + } + } + subject.stub(:node).and_return(uri_hash) + expect( + subject.endpoint('compute-api').path + ).to eq('/v2/%25(tenant_id)s') + end - describe "#db_uri" do - it "returns nil when no openstack.db not in node attrs" do - @subject.stub(:node).and_return {} - @subject.db_uri("nonexisting", "user", "pass").should be_nil + it 'returns endpoint URI object when uri key in endpoint hash' do + uri_hash = { + 'openstack' => { + 'endpoints' => { + 'compute-api' => { + 'uri' => 'http://localhost:8080/path' + } + } + } + } + subject.stub(:node).and_return(uri_hash) + expect( + subject.endpoint('compute-api').port + ).to eq(8080) + end + + it 'returns endpoint URI string when uri key in endpoint hash and host also in hash' do + uri_hash = { + 'openstack' => { + 'endpoints' => { + 'compute-api' => { + 'uri' => 'http://localhost', + 'host' => 'ignored' + } + } + } + } + subject.stub(:node).and_return(uri_hash) + expect(subject.endpoint('compute-api').to_s).to eq('http://localhost') + end + + it 'returns endpoint URI object when uri key not in endpoint hash but host is in hash' do + pending 'TODO: implement' + subject.should_receive(:uri_from_hash).with('host' => 'localhost', 'port' => '8080') + uri_hash = { + 'openstack' => { + 'endpoints' => { + 'compute-api' => { + 'host' => 'localhost', + 'port' => '8080' + } + } + } + } + subject.stub(:node).and_return(uri_hash) + subject.endpoint 'compute-api' + end + + it 'endpoints recipe bind_interface sets host' do + node.set['openstack']['endpoints']['identity-api']['bind_interface'] = 'eth0' + node.set['network'] = { + 'interfaces' => { + 'lo' => { + 'addresses' => { + '127.0.0.1' => { + 'family' => 'inet', + 'netmask' => '255.0.0.0', + 'scope' => 'Node' + } + } + }, + 'eth0' => { + 'addresses' => { + '10.0.0.100' => { + 'family' => 'inet', + 'netmask' => '255.255.255.0', + 'scope' => 'Global' + } + } + } + } + } + subject.stub('address_for').and_return('10.0.0.100') + expect( + chef_run.node['openstack']['endpoints']['identity-api']['host'] + ).to eq('10.0.0.100') + end end - it "returns nil when no such service was found" do - @subject.stub(:node).and_return @chef_run.node - @subject.db_uri("nonexisting", "user", "pass").should be_nil + + describe '#endpoints' do + it 'does nothing when no endpoints' do + subject.stub(:node).and_return({}) + expect(subject.endpoints).to be_nil + end + + it 'does nothing when empty endpoints' do + subject.stub(:node).and_return('openstack' => { 'endpoints' => {} }) + count = 0 + subject.endpoints do | ep | + count += 1 + end + expect(count).to eq(0) + end + + it 'executes block count when have endpoints' do + subject.stub(:node).and_return(chef_run.node) + count = 0 + subject.endpoints do |ep| + count += 1 + end + expect(count).to be >= 1 + end end - it "returns db info hash when service found" do - @subject.stub(:node).and_return @chef_run.node - expect = "mysql://user:pass@127.0.0.1:3306/nova" - @subject.db_uri("compute", "user", "pass").should == expect + + describe '#db' do + it 'returns nil when no openstack.db not in node attrs' do + subject.stub(:node).and_return({}) + expect(subject.db('nonexisting')).to be_nil + end + + it 'returns nil when no such service was found' do + subject.stub(:node).and_return(chef_run.node) + expect(subject.db('nonexisting')).to be_nil + end + + it 'returns db info hash when service found' do + subject.stub(:node).and_return(chef_run.node) + expect(subject.db('compute')['host']).to eq('127.0.0.1') + expect(subject.db('compute').key?('uri')).to be_false + end + end + + describe '#db_uri' do + it 'returns nil when no openstack.db not in node attrs' do + subject.stub(:node).and_return({}) + expect(subject.db_uri('nonexisting', 'user', 'pass')).to be_nil + end + + it 'returns nil when no such service was found' do + subject.stub(:node).and_return(chef_run.node) + expect( + subject.db_uri('nonexisting', 'user', 'pass') + ).to be_nil + end + + it 'returns compute db info hash when service found for default mysql' do + subject.stub(:node).and_return(chef_run.node) + expected = 'mysql://user:pass@127.0.0.1:3306/nova?charset=utf8' + expect( + subject.db_uri('compute', 'user', 'pass') + ).to eq(expected) + end + + it 'returns network db info hash when service found for sqlite with options' do + node.set['openstack']['db']['service_type'] = 'sqlite' + node.set['openstack']['db']['options'] = { 'sqlite' => '?options' } + node.set['openstack']['db']['network']['path'] = 'path' + subject.stub(:node).and_return(chef_run.node) + expected = 'sqlite:///path?options' + expect( + subject.db_uri('network', 'user', 'pass') + ).to eq(expected) + end + + it 'returns block-storage db info hash when service found for db2 with options' do + node.set['openstack']['db']['service_type'] = 'db2' + node.set['openstack']['db']['options'] = { 'db2' => '?options' } + subject.stub(:node).and_return(chef_run.node) + expected = 'ibm_db_sa://user:pass@127.0.0.1:3306/cinder?options' + expect( + subject.db_uri('block-storage', 'user', 'pass') + ).to eq(expected) + end + + it 'returns telemetry db info hash when service found for db2' do + node.set['openstack']['db']['service_type'] = 'db2' + node.set['openstack']['db']['telemetry']['nosql']['used'] = true + subject.stub(:node).and_return(chef_run.node) + expected = 'db2://user:pass@127.0.0.1:27017/ceilometer' + expect( + subject.db_uri('telemetry', 'user', 'pass') + ).to eq(expected) + end + + it 'returns telemetry db info hash when service found for db2 with options' do + node.set['openstack']['db']['service_type'] = 'db2' + node.set['openstack']['db']['options'] = { 'nosql' => '?options' } + node.set['openstack']['db']['telemetry']['nosql']['used'] = true + subject.stub(:node).and_return(chef_run.node) + expected = 'db2://user:pass@127.0.0.1:27017/ceilometer?options' + expect( + subject.db_uri('telemetry', 'user', 'pass') + ).to eq(expected) + end + end + + describe '#address' do + it 'returns interface IP if bind_interface specified' do + ep_hash = { + 'bind_interface' => 'eth0', + 'host' => '5.6.7.8' + } + subject.stub('address_for').and_return('1.2.3.4') + expect( + subject.address(ep_hash) + ).to eq('1.2.3.4') + end + it 'returns host IP if bind_interface not specified' do + ep_hash = { + 'bind_interface' => nil, + 'host' => '5.6.7.8' + } + subject.stub('address_for').and_return('1.2.3.4') + expect( + subject.address(ep_hash) + ).to eq('5.6.7.8') + end end end end diff --git a/chef/cookbooks/openstack-common/spec/logging_spec.rb b/chef/cookbooks/openstack-common/spec/logging_spec.rb index 437973e..b39a2ca 100644 --- a/chef/cookbooks/openstack-common/spec/logging_spec.rb +++ b/chef/cookbooks/openstack-common/spec/logging_spec.rb @@ -1,55 +1,65 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-common::logging" do - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-common::logging" - end +describe 'openstack-common::logging' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } - describe "/etc/openstack" do - before do - @dir = @chef_run.directory "/etc/openstack" + describe '/etc/openstack' do + let(:dir) { chef_run.directory('/etc/openstack') } + + it 'has proper owner' do + expect(dir.owner).to eq('root') + expect(dir.group).to eq('root') end - it "has proper owner" do - expect(@dir).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "755" + it 'has proper modes' do + expect(sprintf('%o', dir.mode)).to eq '755' end end - describe "logging.conf" do - before do - @file = "/etc/openstack/logging.conf" + describe 'logging.conf' do + let(:file) { chef_run.template('/etc/openstack/logging.conf') } + + it 'has proper owner' do + expect(file.owner).to eq('root') + expect(file.group).to eq('root') end - it "has proper owner" do - expect(@chef_run.template(@file)).to be_owned_by "root", "root" + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq '644' end - it "has proper modes" do - m = @chef_run.template(@file).mode - expect(sprintf("%o", m)).to eq "644" - end + context 'logging ignore' do + it 'adds loggers keys ignore' do + node.set['openstack']['logging']['ignore'] = { + 'ignore.key.1' => 'ignore.value.1', + 'ignore.key.2' => 'ignore.value.2' + } + [ + /^\[loggers\]$/, + /^keys=.*ignore_key_1,ignore_key_2$/ + ].each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + end - it "templates openstack.logging.ignore block" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-common::logging" - node = chef_run.node - node.set["openstack"]["logging"]["ignore"] = { - "test.nova.api.openstack.wsgi" => "WARNING" - } + it 'adds specific logger ignore block' do + node.set['openstack']['logging']['ignore'] = { + 'test.nova.api.openstack.wsgi' => 'WARNING' + } - tmp = [ - "[logger_test_nova_api_openstack_wsgi]", - "level = WARNING", - "handlers = prod,debug", - "qualname = test.nova.api.openstack.wsgi" - ] - expect(chef_run).to create_file_with_content @file, tmp.join("\n") + [ + /^\[logger_test_nova_api_openstack_wsgi\]$/, + /^level = WARNING$/, + /^handlers = prod,debug$/, + /^qualname = test.nova.api.openstack.wsgi$/ + ].each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + end end end end diff --git a/chef/cookbooks/openstack-common/spec/network_spec.rb b/chef/cookbooks/openstack-common/spec/network_spec.rb index 65977b5..530043f 100644 --- a/chef/cookbooks/openstack-common/spec/network_spec.rb +++ b/chef/cookbooks/openstack-common/spec/network_spec.rb @@ -1,46 +1,64 @@ -require_relative "spec_helper" -require ::File.join ::File.dirname(__FILE__), "..", "libraries", "network" +# encoding: UTF-8 +require_relative 'spec_helper' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'network' -describe ::Openstack do - before do - @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| - n.set["network"] = { - "interfaces" => { - "lo" => { - "addresses" => { - "127.0.0.1"=> { - "family" => "inet", - "prefixlen" => "8", - "netmask" => "255.0.0.0", - "scope" => "Node" +describe 'openstack-common::default' do + describe 'Openstack address_for' do + let(:runner) { ChefSpec::Runner.new(CHEFSPEC_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['network'] = { + 'interfaces' => { + 'lo' => { + 'addresses' => { + '127.0.0.1' => { + 'family' => 'inet', + 'prefixlen' => '8', + 'netmask' => '255.0.0.0', + 'scope' => 'Node' }, - "::1" => { - "family" => "inet6", - "prefixlen" => "128", - "scope" => "Node" + '::1' => { + 'family' => 'inet6', + 'prefixlen' => '128', + 'scope' => 'Node' } } } } } + + runner.converge(described_recipe) end - @chef_run.converge "openstack-common::default" - @subject = ::Object.new.extend ::Openstack - end + let(:subject) { Object.new.extend(Openstack) } - describe "#address_for" do - it "returns ipv4 address" do - @subject.stub(:node).and_return @chef_run.node - resp = @subject.address_for "lo" + include_context 'library-stubs' - expect(resp).to eq "127.0.0.1" + describe '#address_for ipv4' do + it 'returns ipv4 address' do + expect( + subject.address_for('lo') + ).to eq('127.0.0.1') + end + + it 'returns ipv6 address' do + expect( + subject.address_for('lo', 'inet6') + ).to eq('::1') + end end + describe '#address_for ipv6' do + it 'returns ipv6 address' do + node.set['openstack']['endpoints']['family'] = 'inet6' + expect( + subject.address_for('lo') + ).to eq('::1') + end - it "returns ipv4 address" do - @subject.stub(:node).and_return @chef_run.node - resp = @subject.address_for "lo", "inet6" - - expect(resp).to eq "::1" + it 'returns ipv6 address' do + expect( + subject.address_for('lo', 'inet6') + ).to eq('::1') + end end end end diff --git a/chef/cookbooks/openstack-common/spec/openrc_spec.rb b/chef/cookbooks/openstack-common/spec/openrc_spec.rb new file mode 100644 index 0000000..c533a20 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/openrc_spec.rb @@ -0,0 +1,58 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-common::openrc' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + include_context 'common-stubs' + + describe '/root/openrc' do + let(:file) { chef_run.template('/root/openrc') } + + it 'creates the /root/openrc file' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 0600 + ) + end + + it 'contains auth environment variables' do + [ + /^export OS_USERNAME=admin$/, + /^export OS_TENANT_NAME=admin$/, + /^export OS_PASSWORD=admin$/, + %r{^export OS_AUTH_URL=http://127.0.0.1:5000/v2.0$}, + /^export OS_REGION_NAME=RegionOne$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'templates misc_openrc array correctly' do + node.set['openstack']['misc_openrc'] = ['export MISC1=OPTION1', 'export MISC2=OPTION2'] + expect(chef_run).to render_file(file.name).with_content( + /^export MISC1=OPTION1$/) + expect(chef_run).to render_file(file.name).with_content( + /^export MISC2=OPTION2$/) + end + + it 'contains overridden auth environment variables' do + node.set['openstack']['identity']['admin_tenant_name'] = 'admin-tenant-name-override' + node.set['openstack']['identity']['admin_user'] = 'admin-user-override' + [ + /^export OS_USERNAME=admin-user-override$/, + /^export OS_TENANT_NAME=admin-tenant-name-override$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/parse_spec.rb b/chef/cookbooks/openstack-common/spec/parse_spec.rb index 2f8aee4..ce2d4cb 100644 --- a/chef/cookbooks/openstack-common/spec/parse_spec.rb +++ b/chef/cookbooks/openstack-common/spec/parse_spec.rb @@ -1,65 +1,74 @@ -require_relative "spec_helper" -require "uri" -require ::File.join ::File.dirname(__FILE__), "..", "libraries", "parse" +# encoding: UTF-8 +require_relative 'spec_helper' +require 'uri' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'parse' -describe ::Openstack do - before do - @subject = ::Object.new.extend(::Openstack) - end +describe 'Openstack parse' do + let(:subject) { Object.new.extend(Openstack) } - describe "#prettytable_to_array" do - it "returns [] when no table provided" do - @subject.prettytable_to_array(nil).should == [] + describe '#prettytable_to_array' do + it 'returns [] when no table provided' do + expect( + subject.prettytable_to_array(nil) + ).to eq([]) end - it "returns [] when table provided is empty" do - @subject.prettytable_to_array("").should == [] + it 'returns [] when table provided is empty' do + expect( + subject.prettytable_to_array('') + ).to eq([]) end - it "returns proper array of hashes when proper table provided" do + it 'returns proper array of hashes when proper table provided' do table = -"+---------+----------------------------------+----------------------------------+ +'+---------+----------------------------------+----------------------------------+ | tenant | access | secret | +---------+----------------------------------+----------------------------------+ | service | 91af731b3be244beb8f30fc59b7bc96d | ce811442cfb549c39390a203778a4bf5 | -+---------+----------------------------------+----------------------------------+" - @subject.prettytable_to_array(table).should == - [{"tenant" => "service", - "access" => "91af731b3be244beb8f30fc59b7bc96d", - "secret" => "ce811442cfb549c39390a203778a4bf5"}] ++---------+----------------------------------+----------------------------------+' + expect( + subject.prettytable_to_array(table) + ).to eq( + [{ 'tenant' => 'service', + 'access' => '91af731b3be244beb8f30fc59b7bc96d', + 'secret' => 'ce811442cfb549c39390a203778a4bf5' }]) end - it "returns proper array of hashes when proper table provided including whitespace" do + it 'returns proper array of hashes when proper table provided including whitespace' do table = -"+---------+----------------------------------+----------------------------------+ +'+---------+----------------------------------+----------------------------------+ | tenant | access | secret | +---------+----------------------------------+----------------------------------+ | service | 91af731b3be244beb8f30fc59b7bc96d | ce811442cfb549c39390a203778a4bf5 | +---------+----------------------------------+----------------------------------+ -" - @subject.prettytable_to_array(table).should == - [{"tenant" => "service", - "access" => "91af731b3be244beb8f30fc59b7bc96d", - "secret" => "ce811442cfb549c39390a203778a4bf5"}] +' + expect( + subject.prettytable_to_array(table) + ).to eq( + [{ 'tenant' => 'service', + 'access' => '91af731b3be244beb8f30fc59b7bc96d', + 'secret' => 'ce811442cfb549c39390a203778a4bf5' }]) end - it "returns a flatten hash when provided a Property/Value table" do + it 'returns a flatten hash when provided a Property/Value table' do table = -"+-----------+----------------------------------+ +'+-----------+----------------------------------+ | Property | Value | +-----------+----------------------------------+ | access | 91af731b3be244beb8f30fc59b7bc96d | | secret | ce811442cfb549c39390a203778a4bf5 | | tenant_id | 429271dd1cf54b7ca921a0017524d8ea | | user_id | 1c4fc229560f40689c490c5d0838fd84 | -+-----------+----------------------------------+" - @subject.prettytable_to_array(table).should == - [{"tenant_id" => "429271dd1cf54b7ca921a0017524d8ea", - "access" => "91af731b3be244beb8f30fc59b7bc96d", - "secret" => "ce811442cfb549c39390a203778a4bf5", - "user_id" => "1c4fc229560f40689c490c5d0838fd84"}] ++-----------+----------------------------------+' + expect( + subject.prettytable_to_array(table) + ).to eq( + [{ 'tenant_id' => '429271dd1cf54b7ca921a0017524d8ea', + 'access' => '91af731b3be244beb8f30fc59b7bc96d', + 'secret' => 'ce811442cfb549c39390a203778a4bf5', + 'user_id' => '1c4fc229560f40689c490c5d0838fd84' }]) end - it "returns a flatten hash when provided a Property/Value table including whitespace" do + it 'returns a flatten hash when provided a Property/Value table including whitespace' do table = -" +' +-----------+----------------------------------+ | Property | Value | @@ -68,12 +77,14 @@ describe ::Openstack do | secret | ce811442cfb549c39390a203778a4bf5 | | tenant_id | 429271dd1cf54b7ca921a0017524d8ea | | user_id | 1c4fc229560f40689c490c5d0838fd84 | -+-----------+----------------------------------+" - @subject.prettytable_to_array(table).should == - [{"tenant_id" => "429271dd1cf54b7ca921a0017524d8ea", - "access" => "91af731b3be244beb8f30fc59b7bc96d", - "secret" => "ce811442cfb549c39390a203778a4bf5", - "user_id" => "1c4fc229560f40689c490c5d0838fd84"}] ++-----------+----------------------------------+' + expect( + subject.prettytable_to_array(table) + ).to eq( + [{ 'tenant_id' => '429271dd1cf54b7ca921a0017524d8ea', + 'access' => '91af731b3be244beb8f30fc59b7bc96d', + 'secret' => 'ce811442cfb549c39390a203778a4bf5', + 'user_id' => '1c4fc229560f40689c490c5d0838fd84' }]) end end end diff --git a/chef/cookbooks/openstack-common/spec/password_spec.rb b/chef/cookbooks/openstack-common/spec/password_spec.rb index 0e8f318..ffeee17 100644 --- a/chef/cookbooks/openstack-common/spec/password_spec.rb +++ b/chef/cookbooks/openstack-common/spec/password_spec.rb @@ -1,90 +1,114 @@ -require_relative "spec_helper" -require ::File.join ::File.dirname(__FILE__), "..", "libraries", "passwords" +# encoding: UTF-8 +require_relative 'spec_helper' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'passwords' -describe ::Openstack do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::CHEFSPEC_OPTS - @chef_run.converge "openstack-common::default" - @subject = ::Object.new.extend(::Openstack) - end +describe 'openstack-common::default' do + describe 'Passwords' do + let(:runner) { ChefSpec::Runner.new(CHEFSPEC_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + let(:subject) { Object.new.extend(Openstack) } - describe "#secret" do - it "returns index param when developer_mode is true" do - @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| - n.set["openstack"]["developer_mode"] = true + include_context 'library-stubs' + + describe '#secret' do + it 'returns index param when developer_mode is true' do + node.set['openstack']['developer_mode'] = true + expect(subject.secret('passwords', 'nova')).to eq('nova') end - @chef_run.converge "openstack-common::default" - @subject.stub(:node).and_return @chef_run.node - result = @subject.secret("passwords", "nova") - result.should == "nova" - end - it "returns databag when developer_mode is false" do - value = {"nova" => "this"} - ::Chef::EncryptedDataBagItem.stub(:load_secret).with("/etc/chef/openstack_data_bag_secret").and_return "secret" - ::Chef::EncryptedDataBagItem.stub(:load).with("passwords", "nova", "secret").and_return value - @subject.stub(:node).and_return @chef_run.node - result = @subject.secret("passwords", "nova") - result.should == "this" - end - end - describe "#service_password" do - it "returns index param when developer_mode is true" do - @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| - n.set["openstack"]["developer_mode"] = true + it 'returns the specified password when developer_mode is true' do + node.set['openstack']['developer_mode'] = true + node.override['openstack']['secret']['nova'] = '12345' + expect(subject.secret('passwords', 'nova')).to eq('12345') end - @chef_run.converge "openstack-common::default" - @subject.stub(:node).and_return @chef_run.node - result = @subject.service_password("nova") - result.should == "nova" - end - it "returns databag when developer_mode is false" do - value = {"nova" => "this"} - ::Chef::EncryptedDataBagItem.stub(:load_secret).with("/etc/chef/openstack_data_bag_secret").and_return "secret" - ::Chef::EncryptedDataBagItem.stub(:load).with("service_passwords", "nova", "secret").and_return value - @subject.stub(:node).and_return @chef_run.node - result = @subject.service_password("nova") - result.should == "this" - end - end - describe "#db_password" do - it "returns index param when developer_mode is true" do - @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| - n.set["openstack"]["developer_mode"] = true + it 'returns databag when developer_mode is false' do + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with('passwords', 'nova', 'secret').and_return(value) + expect(subject.secret('passwords', 'nova')).to eq('this') end - @chef_run.converge "openstack-common::default" - @subject.stub(:node).and_return @chef_run.node - result = @subject.db_password("nova") - result.should == "nova" end - it "returns databag when developer_mode is false" do - value = {"nova" => "this"} - ::Chef::EncryptedDataBagItem.stub(:load_secret).with("/etc/chef/openstack_data_bag_secret").and_return "secret" - ::Chef::EncryptedDataBagItem.stub(:load).with("db_passwords", "nova", "secret").and_return value - @subject.stub(:node).and_return @chef_run.node - result = @subject.db_password("nova") - result.should == "this" - end - end - describe "#user_password" do - it "returns index param when developer_mode is true" do - @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| - n.set["openstack"]["developer_mode"] = true + describe '#get_secret' do + it 'returns index param when developer_mode is true' do + node.set['openstack']['developer_mode'] = true + expect(subject.get_secret('nova')).to eq('nova') + end + + it 'returns the specified password when developer_mode is true' do + node.set['openstack']['developer_mode'] = true + node.override['openstack']['secret']['nova'] = '67890' + expect(subject.get_secret('nova')).to eq('67890') + end + + it 'returns databag when developer_mode is false' do + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with('secrets', 'nova', 'secret').and_return(value) + expect(subject.get_secret('nova')).to eq('this') + end + + it 'returns secret from an alternate databag when secrets_data_bag set' do + node.set['openstack']['secret']['secrets_data_bag'] = 'myothersecrets' + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with('myothersecrets', 'nova', 'secret').and_return(value) + expect(subject.get_secret('nova')).to eq('this') end - @chef_run.converge "openstack-common::default" - @subject.stub(:node).and_return @chef_run.node - result = @subject.user_password("nova") - result.should == "nova" end - it "returns databag when developer_mode is false" do - value = {"nova" => "this"} - ::Chef::EncryptedDataBagItem.stub(:load_secret).with("/etc/chef/openstack_data_bag_secret").and_return "secret" - ::Chef::EncryptedDataBagItem.stub(:load).with("user_passwords", "nova", "secret").and_return value - @subject.stub(:node).and_return @chef_run.node - result = @subject.user_password("nova") - result.should == "this" + + describe '#get_password_service_password' do + it 'returns index param when developer_mode is true' do + node.set['openstack']['developer_mode'] = true + expect(subject.get_password('service', 'nova')).to eq('nova') + end + + it 'returns databag when developer_mode is false' do + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with('service_passwords', 'nova', 'secret').and_return(value) + expect( + subject.get_password('service', 'nova') + ).to eq('this') + end + end + + describe '#get_password_db_password' do + it 'returns index param when developer_mode is true' do + node.set['openstack']['developer_mode'] = true + expect( + subject.get_password('db', 'nova') + ).to eq('nova') + end + + it 'returns databag when developer_mode is false' do + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with('db_passwords', 'nova', 'secret').and_return(value) + expect( + subject.get_password('db', 'nova') + ).to eq('this') + end + end + + describe '#get_password_user_password' do + it 'returns index param when developer_mode is true' do + node.set['openstack']['developer_mode'] = true + expect( + subject.get_password('user', 'nova') + ).to eq('nova') + end + + it 'returns databag when developer_mode is false' do + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with('user_passwords', 'nova', 'secret').and_return(value) + expect( + subject.get_password('user', 'nova') + ).to eq('this') + end end end end diff --git a/chef/cookbooks/openstack-common/spec/search_spec.rb b/chef/cookbooks/openstack-common/spec/search_spec.rb index c52da1e..1af9984 100644 --- a/chef/cookbooks/openstack-common/spec/search_spec.rb +++ b/chef/cookbooks/openstack-common/spec/search_spec.rb @@ -1,140 +1,139 @@ -require_relative "spec_helper" -require ::File.join ::File.dirname(__FILE__), "..", "libraries", "search" +# encoding: UTF-8 +require_relative 'spec_helper' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'search' -describe ::Openstack do - before do - @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| - n.set["openstack"]["mq"] = { - "server_role" => "openstack-ops-mq", - "port" => 5672 - } +describe 'openstack-common::default' do + describe 'Openstack Search' do + let(:runner) { ChefSpec::Runner.new(CHEFSPEC_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['mq']['server_role'] = 'openstack-ops-mq' + node.set['openstack']['endpoints']['mq']['port'] = 5672 + + runner.converge(described_recipe) end - @chef_run.converge "openstack-common::default" - @subject = ::Object.new.extend ::Openstack - end + let(:subject) { Object.new.extend(Openstack) } - describe "#search_for" do - it "returns results" do - @subject.stub(:node).and_return @chef_run.node - @subject.stub(:search). - with(:node, "(chef_environment:_default AND roles:role) OR (chef_environment:_default AND recipes:role)"). - and_return [@chef_run.node] - resp = @subject.search_for("role") + describe '#search_for' do + it 'returns results' do + subject.stub(:node).and_return(chef_run.node) + subject.stub(:search) + .with(:node, '(chef_environment:_default AND roles:role) OR (chef_environment:_default AND recipes:role)') + .and_return([chef_run.node]) + resp = subject.search_for('role') + expect(resp[0]['fqdn']).to eq('fauxhai.local') + end - expect(resp[0]['fqdn']).to eq "chefspec.local" + it 'returns empty results' do + subject.stub(:node).and_return(chef_run.node) + subject.stub(:search) + .with(:node, '(chef_environment:_default AND roles:empty-role) OR (chef_environment:_default AND recipes:empty-role)') + .and_return([]) + expect( + subject.search_for('empty-role') + ).to eq([]) + end + + it 'always returns empty results' do + subject.stub(:node).and_return(chef_run.node) + subject.stub(:search) + .with(:node, '(chef_environment:_default AND roles:empty-role) OR (chef_environment:_default AND recipes:empty-role)') + .and_return(nil) + expect( + subject.search_for('empty-role') + ).to eq([]) + end end - it "returns empty results" do - @subject.stub(:node).and_return @chef_run.node - @subject.stub(:search). - with(:node, "(chef_environment:_default AND roles:empty-role) OR (chef_environment:_default AND recipes:empty-role)"). - and_return [] - resp = @subject.search_for("empty-role") + describe '#memcached_servers' do + it 'returns memcached list' do + nodes = [ + { 'memcached' => { 'listen' => '1.1.1.1', 'port' => '11211' } }, + { 'memcached' => { 'listen' => '2.2.2.2', 'port' => '11211' } } + ] + subject.stub(:node).and_return(chef_run.node) + subject.stub(:search_for) + .with('role') + .and_return(nodes) + expect( + subject.memcached_servers('role') + ).to eq(['1.1.1.1:11211', '2.2.2.2:11211']) + end - expect(resp).to eq [] - end + it 'returns sorted memcached list' do + nodes = [ + { 'memcached' => { 'listen' => '3.3.3.3', 'port' => '11211' } }, + { 'memcached' => { 'listen' => '1.1.1.1', 'port' => '11211' } }, + { 'memcached' => { 'listen' => '2.2.2.2', 'port' => '11211' } } + ] + subject.stub(:node).and_return(chef_run.node) + subject.stub(:search_for) + .with('role') + .and_return(nodes) + expect( + subject.memcached_servers('role') + ).to eq(['1.1.1.1:11211', '2.2.2.2:11211', '3.3.3.3:11211']) + end - it "always returns empty results" do - @subject.stub(:node).and_return @chef_run.node - @subject.stub(:search). - with(:node, "(chef_environment:_default AND roles:empty-role) OR (chef_environment:_default AND recipes:empty-role)"). - and_return nil - resp = @subject.search_for("empty-role") - - expect(resp).to eq [] - end - end - - describe "#memcached_servers" do - it "returns memcached list" do - nodes = [ - { "memcached" => { "listen" => "1.1.1.1", "port" => "11211" }}, - { "memcached" => { "listen" => "2.2.2.2", "port" => "11211" }} - ] - @subject.stub(:node).and_return @chef_run.node - @subject.stub(:search_for). - with("role"). - and_return nodes - resp = @subject.memcached_servers("role") - - expect(resp).to eq ["1.1.1.1:11211", "2.2.2.2:11211"] - end - - it "returns sorted memcached list" do - nodes = [ - { "memcached" => { "listen" => "3.3.3.3", "port" => "11211" }}, - { "memcached" => { "listen" => "1.1.1.1", "port" => "11211" }}, - { "memcached" => { "listen" => "2.2.2.2", "port" => "11211" }} - ] - @subject.stub(:node).and_return @chef_run.node - @subject.stub(:search_for). - with("role"). - and_return nodes - resp = @subject.memcached_servers("role") - - expect(resp).to eq ["1.1.1.1:11211", "2.2.2.2:11211", "3.3.3.3:11211"] - end - - it "returns memcached servers as defined by attributes" do - nodes = { - "openstack" => { - "memcached_servers" => ["1.1.1.1:11211", "2.2.2.2:11211"] + it 'returns memcached servers as defined by attributes' do + nodes = { + 'openstack' => { + 'memcached_servers' => ['1.1.1.1:11211', '2.2.2.2:11211'] + } } - } - @subject.stub(:node).and_return @chef_run.node.merge nodes - resp = @subject.memcached_servers("role") + subject.stub(:node).and_return(chef_run.node.merge(nodes)) + expect( + subject.memcached_servers('role') + ).to eq(['1.1.1.1:11211', '2.2.2.2:11211']) + end - expect(resp).to eq ["1.1.1.1:11211", "2.2.2.2:11211"] - end - - it "returns empty memcached servers as defined by attributes" do - nodes = { - "openstack" => { - "memcached_servers" => [] + it 'returns empty memcached servers as defined by attributes' do + nodes = { + 'openstack' => { + 'memcached_servers' => [] + } } - } - @subject.stub(:node).and_return @chef_run.node.merge nodes - resp = @subject.memcached_servers("empty-role") - - expect(resp).to eq [] - end - end - - describe "#rabbit_servers" do - it "returns rabbit servers" do - nodes = [ - { "openstack" => { "mq" => { "listen" => "1.1.1.1", "port" => "5672" }}}, - { "openstack" => { "mq" => { "listen" => "2.2.2.2", "port" => "5672" }}}, - ] - @subject.stub(:node).and_return @chef_run.node - @subject.stub(:search_for). - and_return nodes - resp = @subject.rabbit_servers - - expect(resp).to eq "1.1.1.1:5672,2.2.2.2:5672" + subject.stub(:node).and_return(chef_run.node.merge(nodes)) + expect( + subject.memcached_servers('empty-role') + ).to eq([]) + end end - it "returns sorted rabbit servers" do - nodes = [ - { "openstack" => { "mq" => { "listen" => "3.3.3.3", "port" => "5672" }}}, - { "openstack" => { "mq" => { "listen" => "1.1.1.1", "port" => "5672" }}}, - { "openstack" => { "mq" => { "listen" => "2.2.2.2", "port" => "5672" }}} - ] - @subject.stub(:node).and_return @chef_run.node - @subject.stub(:search_for). - and_return nodes - resp = @subject.rabbit_servers + describe '#rabbit_servers' do + it 'returns rabbit servers' do + nodes = [ + { 'openstack' => { 'mq' => { 'listen' => '1.1.1.1' }, 'endpoints' => { 'mq' => { 'port' => '5672' } } } }, + { 'openstack' => { 'mq' => { 'listen' => '2.2.2.2' }, 'endpoints' => { 'mq' => { 'port' => '5672' } } } } + ] + subject.stub(:node).and_return(chef_run.node) + subject.stub(:search_for) + .and_return(nodes) + expect( + subject.rabbit_servers).to eq('1.1.1.1:5672,2.2.2.2:5672') + end - expect(resp).to eq "1.1.1.1:5672,2.2.2.2:5672,3.3.3.3:5672" - end + it 'returns sorted rabbit servers' do + nodes = [ + { 'openstack' => { 'mq' => { 'listen' => '3.3.3.3' }, 'endpoints' => { 'mq' => { 'port' => '5672' } } } }, + { 'openstack' => { 'mq' => { 'listen' => '1.1.1.1' }, 'endpoints' => { 'mq' => { 'port' => '5672' } } } }, + { 'openstack' => { 'mq' => { 'listen' => '2.2.2.2' }, 'endpoints' => { 'mq' => { 'port' => '5672' } } } } + ] + subject.stub(:node).and_return(chef_run.node) + subject.stub(:search_for) + .and_return(nodes) + expect( + subject.rabbit_servers + ).to eq('1.1.1.1:5672,2.2.2.2:5672,3.3.3.3:5672') + end - it "returns rabbit servers when not searching" do - node = @chef_run.node - node.set["openstack"]["mq"]["servers"] = ["1.1.1.1", "2.2.2.2"] - @subject.stub(:node).and_return @chef_run.node - resp = @subject.rabbit_servers - - expect(resp).to eq "1.1.1.1:5672,2.2.2.2:5672" + it 'returns rabbit servers when not searching' do + chef_run.node.set['openstack']['mq']['servers'] = ['1.1.1.1', '2.2.2.2'] + subject.stub(:node).and_return(chef_run.node) + expect( + subject.rabbit_servers + ).to eq('1.1.1.1:5672,2.2.2.2:5672') + end end end end diff --git a/chef/cookbooks/openstack-common/spec/spec_helper.rb b/chef/cookbooks/openstack-common/spec/spec_helper.rb index 67f67be..690135d 100644 --- a/chef/cookbooks/openstack-common/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-common/spec/spec_helper.rb @@ -1,11 +1,52 @@ -require "chefspec" +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' -::LOG_LEVEL = :fatal -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +ChefSpec::Coverage.start! { add_filter 'openstack-common' } + +LOG_LEVEL = :fatal +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } -::CHEFSPEC_OPTS = { - :log_level => ::LOG_LEVEL +REDHAT_OPTS = { + platform: 'redhat', + version: '6.5', + log_level: LOG_LEVEL } +SUSE_OPTS = { + platform: 'suse', + version: '11.03', + log_lovel: LOG_LEVEL +} +# We set a default platform for non-platform specific test cases +CHEFSPEC_OPTS = UBUNTU_OPTS + +shared_context 'library-stubs' do + before do + subject.stub(:node).and_return(chef_run.node) + end +end + +shared_context 'common-stubs' do + before do + Chef::Recipe.any_instance.stub(:search_for) + .with('os-identity').and_return( + [{ + 'openstack' => { + 'identity' => { + 'admin_tenant_name' => 'admin', + 'admin_user' => 'admin' + } + } + }] + ) + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'admin') + .and_return('admin') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'admin-user-override') + .and_return('admin-user-override') + end +end diff --git a/chef/cookbooks/openstack-common/spec/sysctl_spec.rb b/chef/cookbooks/openstack-common/spec/sysctl_spec.rb new file mode 100644 index 0000000..5eded65 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/sysctl_spec.rb @@ -0,0 +1,32 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-common::sysctl' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + describe '60-openstack.conf' do + let(:file) { chef_run.template('/etc/sysctl.d/60-openstack.conf') } + + it 'has proper owner' do + expect(file.owner).to eq('root') + expect(file.group).to eq('root') + end + + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq '644' + end + + it 'sets the sysctl attributes' do + sysctl_kv = { 'systcl_key1' => 'sysctl_value1', + 'sysctl_key2' => 'sysctl_value2' } + node.set['openstack']['sysctl'] = sysctl_kv + sysctl_kv.each do |k, v| + expect(chef_run).to render_file(file.name).with_content(/^#{k} = #{v}$/) + end + end + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/uri_spec.rb b/chef/cookbooks/openstack-common/spec/uri_spec.rb index a760450..150671e 100644 --- a/chef/cookbooks/openstack-common/spec/uri_spec.rb +++ b/chef/cookbooks/openstack-common/spec/uri_spec.rb @@ -1,85 +1,120 @@ -require_relative "spec_helper" -require ::File.join ::File.dirname(__FILE__), "..", "libraries", "uri" -require "uri" +# encoding: UTF-8 +require_relative 'spec_helper' +require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'uri' +require 'uri' -describe ::Openstack do - before do - @subject = ::Object.new.extend(::Openstack) - end +describe 'Openstack uri' do + let(:subject) { Object.new.extend(Openstack) } - describe "#uri_from_hash" do - it "returns nil when no host or uri key found" do + describe '#uri_from_hash' do + it 'returns nil when no host or uri key found' do hash = { - "port" => 8888, - "path" => "/path" + 'port' => 8888, + 'path' => '/path' } - @subject.uri_from_hash(hash).should be_nil + expect( + subject.uri_from_hash(hash) + ).to be_nil end - it "returns uri when uri key found, ignoring other parts" do - uri = "http://localhost/" + + it 'returns uri when uri key found, ignoring other parts' do + uri = 'http://localhost/' hash = { - "port" => 8888, - "path" => "/path", - "uri" => uri + 'port' => 8888, + 'path' => '/path', + 'uri' => uri } - result = @subject.uri_from_hash(hash) - result.should be_a URI - result.to_s.should == uri + result = subject.uri_from_hash(hash) + expect(result).to be_a URI + expect(result.to_s).to eq(uri) end - it "constructs from host" do - uri = "https://localhost:8888/path" + + it 'constructs from host' do + uri = 'https://localhost:8888/path' hash = { - "scheme" => 'https', - "port" => 8888, - "path" => "/path", - "host" => "localhost" + 'scheme' => 'https', + 'port' => 8888, + 'path' => '/path', + 'host' => 'localhost' } - result = @subject.uri_from_hash(hash) - result.to_s.should == uri + expect( + subject.uri_from_hash(hash).to_s + ).to eq(uri) end - it "constructs with defaults" do - uri = "https://localhost" + + it 'constructs with defaults' do + uri = 'https://localhost' hash = { - "scheme" => 'https', - "host" => "localhost" + 'scheme' => 'https', + 'host' => 'localhost' } - result = @subject.uri_from_hash(hash) - result.to_s.should == uri + expect( + subject.uri_from_hash(hash).to_s + ).to eq(uri) end - it "constructs with extraneous keys" do - uri = "http://localhost" + + it 'constructs with extraneous keys' do + uri = 'http://localhost' hash = { - "host" => "localhost", - "network" => "public" # To emulate the osops-utils::ip_location way... + 'host' => 'localhost', + 'network' => 'public' # To emulate the osops-utils::ip_location way... } - result = @subject.uri_from_hash(hash) - result.to_s.should == uri + expect( + subject.uri_from_hash(hash).to_s + ).to eq(uri) end end - describe "#uri_join_paths" do - it "returns nil when no paths are passed in" do - @subject.uri_join_paths().should be_nil + describe '#uri_join_paths' do + it 'returns nil when no paths are passed in' do + expect(subject.uri_join_paths).to be_nil end - it "preserves absolute path when only absolute path passed in" do - path = "/abspath" - result = @subject.uri_join_paths(path) - result.should == path + + it 'preserves absolute path when only absolute path passed in' do + path = '/abspath' + expect( + subject.uri_join_paths(path) + ).to eq(path) end - it "preserves relative path when only relative path passed in" do - path = "abspath/" - result = @subject.uri_join_paths(path) - result.should == path + + it 'preserves relative path when only relative path passed in' do + path = 'abspath/' + expect( + subject.uri_join_paths(path) + ).to eq(path) end - it "preserves leadng and trailing slashes" do - expected = "/path/to/resource/" - result = @subject.uri_join_paths("/path", "to", "resource/") - result.should == expected + + it 'preserves leadng and trailing slashes' do + expected = '/path/to/resource/' + expect( + subject.uri_join_paths('/path', 'to', 'resource/') + ).to eq(expected) end - it "removes extraneous intermediate slashes" do - expected = "/path/to/resource" - result = @subject.uri_join_paths("/path", "//to/", "/resource") - result.should == expected + + it 'removes extraneous intermediate slashes' do + expected = '/path/to/resource' + expect( + subject.uri_join_paths('/path', '//to/', '/resource') + ).to eq(expected) + end + end + + describe '#auth_uri_transform' do + it 'preserves the original auth uri when the auth version passed is v2.0' do + auth_version = 'v2.0' + auth_uri = 'http://localhost:5000/v2.0' + expect( + subject.auth_uri_transform(auth_uri, auth_version) + ).to eq(auth_uri) + end + + it 'substitute /v2.0 with /v3 in the passed auth uri when auth version passed is v3.0' do + auth_version = 'v3.0' + auth_uri = 'http://localhost:5000/v2.0' + expected_auth_uri = 'http://localhost:5000/v3' + expect( + subject.auth_uri_transform(auth_uri, auth_version) + ).to eq(expected_auth_uri) end end end diff --git a/chef/cookbooks/openstack-common/templates/default/60-openstack.conf.erb b/chef/cookbooks/openstack-common/templates/default/60-openstack.conf.erb new file mode 100644 index 0000000..2327676 --- /dev/null +++ b/chef/cookbooks/openstack-common/templates/default/60-openstack.conf.erb @@ -0,0 +1,5 @@ +# Managed by Chef + +<% node['openstack']['sysctl'].sort.each do |k,v| -%> +<%= k %> = <%= v %> +<% end -%> diff --git a/chef/cookbooks/openstack-common/templates/default/ceph.client.keyring.erb b/chef/cookbooks/openstack-common/templates/default/ceph.client.keyring.erb new file mode 100644 index 0000000..a1edaa4 --- /dev/null +++ b/chef/cookbooks/openstack-common/templates/default/ceph.client.keyring.erb @@ -0,0 +1,2 @@ +[client.<%= @name -%>] + key = <%= @key %> diff --git a/chef/cookbooks/openstack-common/templates/default/ceph.conf.erb b/chef/cookbooks/openstack-common/templates/default/ceph.conf.erb new file mode 100644 index 0000000..24bafa2 --- /dev/null +++ b/chef/cookbooks/openstack-common/templates/default/ceph.conf.erb @@ -0,0 +1,8 @@ +[global] +<% @global.each do |k,v| -%> +<% if v.is_a? Array -%> +<%= k %> = <%= v.join ", " %> +<% else -%> +<%= k %> = <%= v %> +<% end -%> +<% end -%> diff --git a/chef/cookbooks/openstack-common/templates/default/logging.conf.erb b/chef/cookbooks/openstack-common/templates/default/logging.conf.erb index f45a888..421d156 100644 --- a/chef/cookbooks/openstack-common/templates/default/logging.conf.erb +++ b/chef/cookbooks/openstack-common/templates/default/logging.conf.erb @@ -1,5 +1,5 @@ [loggers] -keys=root,ceilometer,cinder,glance,horizon,keystone,nova,quantum,swift,amqplib,sqlalchemy,boto,suds,eventletwsgi,<%= node["openstack"]["logging"]["ignore"].map{|k,v| k.gsub(/\W/, '_')}.join(',') %> +keys=root,ceilometer,cinder,glance,horizon,keystone,nova,neutron,swift,amqplib,sqlalchemy,boto,suds,eventletwsgi,<%= node["openstack"]["logging"]["ignore"].map{|k,v| k.gsub(/\W/, '_')}.join(',') %> [formatters] keys=normal,normal_with_name,debug,syslog_with_name,syslog_debug @@ -95,10 +95,10 @@ level=DEBUG handlers=prod,debug qualname=nova -[logger_quantum] +[logger_neutron] level=DEBUG handlers=prod,debug -qualname=quantum +qualname=neutron [logger_swift] level=DEBUG diff --git a/chef/cookbooks/openstack-common/templates/default/openrc.erb b/chef/cookbooks/openstack-common/templates/default/openrc.erb new file mode 100644 index 0000000..50e7d27 --- /dev/null +++ b/chef/cookbooks/openstack-common/templates/default/openrc.erb @@ -0,0 +1,15 @@ +<%= node['openstack']['common']['custom_template_banner'] %> + +# COMMON OPENSTACK ENVS +export OS_USERNAME=<%= @user %> +export OS_PASSWORD=<%= @password %> +export OS_TENANT_NAME=<%= @tenant %> +export OS_AUTH_URL=<%= @identity_endpoint %> +export OS_REGION_NAME=<%= node['openstack']['region'] %> + +<% if node['openstack']['misc_openrc'] %> +# Misc options +<% node['openstack']['misc_openrc'].each do |m| %> +<%= m %> +<% end %> +<% end %> diff --git a/chef/cookbooks/openstack-compute/.rubocop.yml b/chef/cookbooks/openstack-compute/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-compute/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-compute/.tailor b/chef/cookbooks/openstack-compute/.tailor deleted file mode 100644 index 0b41998..0000000 --- a/chef/cookbooks/openstack-compute/.tailor +++ /dev/null @@ -1,25 +0,0 @@ -Tailor.config do |config| - config.formatters "text" - config.file_set '**/*.rb' do |style| - style.max_line_length 80, level: :off - style.allow_camel_case_methods false, level: :error - style.allow_hard_tabs false, level: :error - style.allow_screaming_snake_case_classes false, level: :error - style.allow_trailing_line_spaces false, level: :error - style.allow_invalid_ruby false, level: :warn - style.indentation_spaces 2, level: :error - style.max_code_lines_in_class 300, level: :error - style.max_code_lines_in_method 50, level: :error - style.spaces_after_comma 1, level: :error - style.spaces_after_lbrace 1, level: :error - style.spaces_after_lbracket 0, level: :error - style.spaces_after_lparen 0, level: :error - style.spaces_before_comma 0, level: :error - style.spaces_before_lbrace 1, level: :error - style.spaces_before_rbrace 1, level: :error - style.spaces_before_rbracket 0, level: :error - style.spaces_before_rparen 0, level: :error - style.spaces_in_empty_braces 0, level: :error - style.trailing_newlines 1, level: :error - end -end diff --git a/chef/cookbooks/openstack-compute/Berksfile.lock b/chef/cookbooks/openstack-compute/Berksfile.lock deleted file mode 100644 index 1b4bc08..0000000 --- a/chef/cookbooks/openstack-compute/Berksfile.lock +++ /dev/null @@ -1,65 +0,0 @@ -{ - "sources": { - "openstack-compute": { - "path": "." - }, - "openstack-image": { - "locked_version": "7.0.0", - "git": "git://github.com/stackforge/cookbook-openstack-image.git", - "ref": "c06c440a371e4b8602a2de54b21e4c6a82a0fbf3" - }, - "openstack-identity": { - "locked_version": "7.0.0", - "git": "git://github.com/stackforge/cookbook-openstack-identity.git", - "ref": "b881af26095cfa869a6970067c49597a0ee63586" - }, - "openstack-common": { - "locked_version": "0.4.3", - "git": "git://github.com/stackforge/cookbook-openstack-common.git", - "ref": "eb5eed7126b6a6efbaf803e8a594d610cf661e97" - }, - "openstack-network": { - "locked_version": "7.0.0", - "git": "git://github.com/stackforge/cookbook-openstack-network.git", - "ref": "2b6ecb00e81e98765343ecb4d8655c5d74fd46c9" - }, - "sysctl": { - "locked_version": "0.2.0", - "git": "git://github.com/Fewbytes/sysctl-cookbook.git", - "ref": "65a96b45d489c904515d916aae6bc474da35f1ca" - }, - "selinux": { - "locked_version": "0.5.6" - }, - "yum": { - "locked_version": "2.3.0" - }, - "python": { - "locked_version": "1.3.6" - }, - "build-essential": { - "locked_version": "1.4.0" - }, - "apt": { - "locked_version": "2.0.0" - }, - "database": { - "locked_version": "1.4.0" - }, - "mysql": { - "locked_version": "3.0.2" - }, - "openssl": { - "locked_version": "1.0.2" - }, - "postgresql": { - "locked_version": "3.0.2" - }, - "aws": { - "locked_version": "0.101.2" - }, - "xfs": { - "locked_version": "1.1.0" - } - } -} diff --git a/chef/cookbooks/openstack-compute/CHANGELOG.md b/chef/cookbooks/openstack-compute/CHANGELOG.md new file mode 100644 index 0000000..8499b11 --- /dev/null +++ b/chef/cookbooks/openstack-compute/CHANGELOG.md @@ -0,0 +1,103 @@ +# CHANGELOG for cookbook-openstack-compute + +This file is used to list changes made in each version of cookbook-openstack-compute. +## 9.2.2 +* Fix to allow workers to have attribute overrides + +## 9.2.1 +* Fix to allow compute driver attributes + +## 9.2.0 +* Get VMware vCenter password from databag + +## 9.1.1 +* Fix package action to allow updates + +## 9.1.0 +* Remove openrc, it's been moved to Common + +## 9.0.1 +### Bug +* Add network_allocate_retries option to nova.conf template + +## 9.0.0 +* Upgrade to Icehouse + +## 8.4.2 +* Fixing allow nova compute and ec2 ip and port to be configured + +## 8.4.1 +### Bug +* Fix the DB2 ODBC driver issue + +## 8.4.0 +### Blue print +* Use the library method auth_uri_transform + +## 8.3.1 +* Fixes including api-metadata recipe only when asked for (LP: 1286300) + +## 8.3.0 +* VMware compute driver support + +## 8.2.1: +* Add the libvirt_inject_key attribute, defaults to true +* Add metering attributes + +## 8.2.0 +* Add client recipe + +## 8.1.0 +* Update to reflect changes in v3 of yum cookbook. Yum v3 now required. + +## 8.0.0 +* Branch for Havana. Add neutron support by search/replace quantum with neutron + +## 7.3.0 +* Add new attributes: auto_assign_floating_ip, disk_allocation_ratio, allow_resize_to_same_host + and force_dhcp_release + +## 7.2.4 +### Bug +* Fixing console auth service and process names for RHEL (LP: 1256456) + +## 7.2.3 +* relax the dependencies to the 7.x series + +## 7.2.2 +### Bug +* Setting the libvirt_cpu_mode is none when libvirt_type is qemu (LP: 1255840) + +## 7.2.1 +* Add new attributes for common rpc configuration + +## 7.2.0: +* adds qpid support. defaults to rabbit + +## 7.1.0: +* adds the enabled_apis attribute, defaults to 'ec2,osapi_compute,metadata' + +## 7.0.3: +* adds the libvirt_inject_password attribute, defaults to false + +## 7.0.2: +* add the new attribute dbus_service settings for different OS platform. + +## 7.0.2: +* Check the run_context.loaded_recipes rather than the run_list, since the + 'openstack-network::server' recipe is most likely contained in a role and + not explicitly in the run_list. +* The sysctl cookbook is unused and was removed as a dependency. + +## 7.0.1: +* Adding attributes for libvirtd.conf settings (max_clients, max_workers, + max_requests, max_client_requests). + +## 7.0.0: +* Initial release of cookbook-openstack-compute. + +- - - +Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. + +The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. + diff --git a/chef/cookbooks/openstack-compute/Gemfile b/chef/cookbooks/openstack-compute/Gemfile index 04ef97e..58934c6 100644 --- a/chef/cookbooks/openstack-compute/Gemfile +++ b/chef/cookbooks/openstack-compute/Gemfile @@ -1,9 +1,10 @@ -source "https://rubygems.org" +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 2.0.3" -gem "chefspec", "~> 1.3.0" -gem "foodcritic" -gem "strainer" -gem "tailor" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' +gem 'fauxhai', '>= 2.1.0' diff --git a/chef/cookbooks/openstack-compute/Gemfile.lock b/chef/cookbooks/openstack-compute/Gemfile.lock index f1a0a65..bbe41ab 100644 --- a/chef/cookbooks/openstack-compute/Gemfile.lock +++ b/chef/cookbooks/openstack-compute/Gemfile.lock @@ -1,30 +1,36 @@ GEM remote: https://rubygems.org/ specs: - activesupport (3.2.14) + activesupport (3.2.17) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.3.5) - akami (1.2.0) + addressable (2.3.6) + akami (1.2.1) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - berkshelf (2.0.7) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) activesupport (~> 3.2.0) addressable (~> 2.3.4) buff-shell_out (~> 0.1) - celluloid (>= 0.14.0) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) minitar (~> 0.5.4) rbzip2 (~> 0.2.0) retryable (~> 1.3.3) - ridley (~> 1.2.1) + ridley (~> 1.5.0) solve (>= 0.5.0) thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) buff-ruby_engine (0.1.0) - buff-shell_out (0.1.0) + buff-shell_out (0.1.1) buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) @@ -32,111 +38,127 @@ GEM celluloid-io (0.14.1) celluloid (>= 0.14.1) nio4r (>= 0.4.5) - chef (11.4.4) - erubis - highline (>= 1.6.9) - json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) net-ssh (~> 2.6) - net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (1.3.1) - chef (>= 10.0) - erubis - fauxhai (>= 0.1.1, < 2.0) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.9.0) - builder (>= 2.1.2) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.8) + faraday (0.8.9) multipart-post (~> 1.2.0) - fauxhai (1.1.1) - httparty + fauxhai (2.1.0) net-ssh ohai - ffi (1.9.0) - foodcritic (2.2.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.1.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.4) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - mime-types (1.23) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.5) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.2.0) - multi_json (1.7.7) - multi_xml (0.5.4) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) multipart-post (1.2.0) - net-http-persistent (2.9) - net-ssh (2.6.8) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - net-ssh-multi (1.1) - net-ssh (>= 2.1.4) - net-ssh-gateway (>= 0.99.0) - nio4r (0.4.6) - nokogiri (1.5.10) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.18.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) + rainbow (2.0.0) + rake (10.2.2) rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (1.2.5) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) buff-extensions (~> 0.3) + buff-ignore (~> 1.1) buff-shell_out (~> 0.1) celluloid (~> 0.14.0) celluloid-io (~> 0.14.0) @@ -147,6 +169,7 @@ GEM mixlib-authentication (>= 1.3.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) varia_model (~> 0.1) @@ -155,10 +178,15 @@ GEM rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - rspec-core (2.14.4) - rspec-expectations (2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.2) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) savon (0.9.5) akami (~> 1.0) @@ -168,34 +196,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.8.0) - strainer (3.1.1) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - text-table (1.2.3) thor (0.18.1) - timers (1.1.0) - tins (0.8.3) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) uuidtools (2.1.4) - varia_model (0.1.1) + varia_model (0.3.2) buff-extensions (~> 0.2) hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -205,10 +228,11 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 2.0.3) - chef (~> 11.4.4) - chefspec (~> 1.3.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + fauxhai (>= 2.1.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - tailor diff --git a/chef/cookbooks/openstack-compute/README.md b/chef/cookbooks/openstack-compute/README.md index f3d999b..7e503d0 100644 --- a/chef/cookbooks/openstack-compute/README.md +++ b/chef/cookbooks/openstack-compute/README.md @@ -41,6 +41,10 @@ api-os-compute - Includes recipe `nova-common` - Installs OS API and configures the service and endpoints in keystone +client +---- +- Install the nova client packages + compute ---- - Includes recipes `nova-common`, `api-metadata`, `network` @@ -50,6 +54,11 @@ libvirt ---- - Installs libvirt, used by nova compute for management of the virtual machine environment +libvirt_rbd +---- +- Prepares the compute node for interaction with a Ceph cluster for block storage (RBD) +- Depends on `openstack-common::ceph_client` for packages and cluster connectivity (i.e. a proper `/etc/ceph/ceph.conf`) + network ---- - Includes recipe `nova-common` @@ -63,7 +72,6 @@ nova-common ---- - May include recipe `selinux` (Fedora) - Builds the basic nova.conf config file with details of the rabbitmq, mysql, glance and keystone servers -- Builds a openrc file for root with appropriate environment variables to interact with the nova client CLI nova-setup ---- @@ -89,16 +97,20 @@ Openstack Compute attributes are in the attribute namespace ["openstack"]["compu * `openstack["compute"]["user"]` - User nova services run as * `openstack["compute"]["group"]` - Group nova services run as * `openstack["compute"]["db"]["username"]` - Username for nova database access -* `openstack["compute"]["rabbit"]["username"]` - Username for nova rabbit access -* `openstack["compute"]["rabbit"]["vhost"]` - The rabbit vhost to use -* `openstack["compute"]["rabbit"]["port"]` - The rabbit port to use -* `openstack["compute"]["rabbit"]["host"]` - The rabbit host to use (must set when `openstack["compute"]["rabbit"]["ha"]` false). -* `openstack["compute"]["rabbit"]["ha"]` - Whether or not to use rabbit ha * `openstack["compute"]["service_tenant_name"]` - Tenant name used by nova when interacting with keystone * `openstack["compute"]["service_user"]` - User name used by nova when interacting with keystone * `openstack["compute"]["service_role"]` - User role used by nova when interacting with keystone * `openstack["compute"]["floating_cmd"]` - Path to the `nova-manage floating create` wrapper script. +* `openstack["compute"]["ec2_workers"]` - Number of ec2 workers +* `openstack["compute"]["osapi_compute_workers"]` - Number of api workers +* `openstack["compute"]["metadata_workers"]` - Number of metadata workders * `openstack["compute"]["config"]["volume_api_class"]` - API Class used for Volume support +* `openstack['compute']['driver'] = Driver to use for controlling virtualization +* `openstack['compute']['default_ephemeral_format'] = The default format an ephemeral_volume will be formatted with on creation +* `openstack['compute']['preallocate_images'] = VM image preallocation mode +* `openstack['compute']['use_cow_images'] = Whether to use cow images +* `openstack['compute']['vif_plugging_is_fatal'] = Fail instance boot if vif plugging fails +* `openstack['compute']['vif_plugging_timeout'] = Number of seconds to wait for neutron vif plugging events to arrive before continuing or failing * `openstack["compute"]["compute"]["api"]["protocol"]` - Protocol used for the OS API * `openstack["compute"]["compute"]["api"]["port"]` - Port on which OS API runs * `openstack["compute"]["compute"]["api"]["version"]` - Version of the OS API used @@ -115,8 +127,44 @@ Openstack Compute attributes are in the attribute namespace ["openstack"]["compu * `openstack["compute"]["config"]["snapshot_image_format"]` - Snapshot image format (valid options are : raw, qcow2, vmdk, vdi [we default to qcow2]). * `openstack["compute"]["config"]["start_guests_on_host_boot"]` - Whether to restart guests when the host reboots * `openstack["compute"]["config"]["resume_guests_state_on_host_boot"]` - Whether to start guests that were running before the host rebooted +* `openstack["compute"]["config"]["disk_allocation_ratio"]` - Virtual disk to physical disk allocation ratio (default 1.0) +* `openstack["compute"]["config"]["allow_resize_to_same_host"]` - Allow destination machine to match source for resize. Useful when testing in single-host environments (default is false) +* `openstack["compute"]["config"]["disk_cachemodes"]` - Cachemodes to use for different disk types e.g: "file=directsync,block=none". Valid cache values are "default", "none", "writethrough", "writeback", "directsync" and "unsafe". * `openstack["compute"]["api"]["signing_dir"]` - Keystone PKI needs a location to hold the signed tokens * `openstack["compute"]["api"]["signing_dir"]` - Keystone PKI needs a location to hold the signed tokens +* `openstack["compute"]["rpc_thread_pool_size"]` - Size of RPC thread pool (default 64) +* `openstack["compute"]["rpc_conn_pool_size"]` - Size of RPC connection pool (default 30) +* `openstack["compute"]["rpc_response_timeout"]` - Seconds to wait for a response from call or multicall (default 60) +TODO: Add DB2 support on other platforms +* `openstack["compute"]["platform"]["db2_python_packages"]` - Array of DB2 python packages, only available on redhat platform +* `openstack['compute']['api']['auth']['version']` - Select v2.0 or v3.0. Default v2.0. The auth API version used to interact with identity service. +* `openstack['compute']['conductor']['workers']` = Number of conductor workers + + +MQ attributes +------------- +* `openstack["compute"]["mq"]["service_type"]` - Select qpid or rabbitmq. default rabbitmq +TODO: move rabbit parameters under openstack["compute"]["mq"] +* `openstack["compute"]["rabbit"]["username"]` - Username for nova rabbit access +* `openstack["compute"]["rabbit"]["vhost"]` - The rabbit vhost to use +* `openstack["compute"]["rabbit"]["port"]` - The rabbit port to use +* `openstack["compute"]["rabbit"]["host"]` - The rabbit host to use (must set when `openstack["compute"]["rabbit"]["ha"]` false). +* `openstack["compute"]["rabbit"]["ha"]` - Whether or not to use rabbit ha + +* `openstack["compute"]["mq"]["qpid"]["host"]` - The qpid host to use +* `openstack["compute"]["mq"]["qpid"]["port"]` - The qpid port to use +* `openstack["compute"]["mq"]["qpid"]["qpid_hosts"]` - Qpid hosts. TODO. use only when ha is specified. +* `openstack["compute"]["mq"]["qpid"]["username"]` - Username for qpid connection +* `openstack["compute"]["mq"]["qpid"]["password"]` - Password for qpid connection +* `openstack["compute"]["mq"]["qpid"]["sasl_mechanisms"]` - Space separated list of SASL mechanisms to use for auth +* `openstack["compute"]["mq"]["qpid"]["reconnect_timeout"]` - The number of seconds to wait before deciding that a reconnect attempt has failed. +* `openstack["compute"]["mq"]["qpid"]["reconnect_limit"]` - The limit for the number of times to reconnect before considering the connection to be failed. +* `openstack["compute"]["mq"]["qpid"]["reconnect_interval_min"]` - Minimum number of seconds between connection attempts. +* `openstack["compute"]["mq"]["qpid"]["reconnect_interval_max"]` - Maximum number of seconds between connection attempts. +* `openstack["compute"]["mq"]["qpid"]["reconnect_interval"]` - Equivalent to setting qpid_reconnect_interval_min and qpid_reconnect_interval_max to the same value. +* `openstack["compute"]["mq"]["qpid"]["heartbeat"]` - Seconds between heartbeat messages sent to ensure that the connection is still alive. +* `openstack["compute"]["mq"]["qpid"]["protocol"]` - Protocol to use. Default tcp. +* `openstack["compute"]["mq"]["qpid"]["tcp_nodelay"]` - Disable the Nagle algorithm. default disabled. Networking Attributes --------------------- @@ -124,10 +172,11 @@ Networking Attributes Basic networking configuration is controlled with the following attributes: * `openstack["compute"]["network"]["network_manager"]` - Defaults to "nova.network.manager.FlatDHCPManager". Set to "nova.network.manager.VlanManager" to configure VLAN Networking. -* `openstack["compute"]["network"]["fixed_range"]` - The CIDR for the network that VMs will be assigned to. In the case of VLAN Networking, this should be the network in which all VLAN networks that tenants are assigned will fit. * `openstack["compute"]["network"]["dmz_cidr"]` - A CIDR for the range of IP addresses that will NOT be SNAT'ed by the nova network controller -* `openstack["compute"]["network"]["public_interface"]` - Defaults to eth0. Refers to the network interface used for VM addresses in the `fixed_range`. +* `openstack["compute"]["network"]["public_interface"]` - Defaults to eth0. Refers to the network interface used for VM addresses`. * `openstack["compute"]["network"]["vlan_interface"]` - Defaults to eth0. Refers to the network interface used for VM addresses when VMs are assigned in a VLAN subnet. +* `openstack["compute"]["network"]["auto_assign_floating_ip"]` - Defaults to false. Autoassigning floating ip to VM, this should be only for nova network. +* `openstack["compute"]["network"]["force_dhcp_release"]` - If True, send a dhcp release on instance termination. (Default is false on "fedora", "redhat", "centos") You can have the cookbook automatically create networks in Nova for you by adding a Hash to the `openstack["compute"]["networks"]` Array. **Note**: The `openstack-compute::nova-setup` recipe contains the code that creates these pre-defined networks. @@ -166,24 +215,27 @@ By default, the `openstack["compute"]["networks"]` array has two networks: * `openstack["compute"]["networks"]["private"]["bridge"]` - Bridge to be created for accessing the VM network (e.g., br200) * `openstack["compute"]["networks"]["private"]["bridge_dev"]` - Physical device on which the bridge device should be attached (e.g., eth3) -VNC Configuration Attributes ----------------------------- - -Requires [network_addr](https://gist.github.com/jtimberman/1040543) Ohai plugin. - -* `openstack["compute"]["xvpvnc_proxy"]["service_port"]` - Port on which XvpVNC runs -* `openstack["compute"]["xvpvnc_proxy"]["bind_interface"]` - Determine the interface's IP address to bind to -* `openstack["compute"]["novnc_proxy"]["service_port"]` - Port on which NoVNC runs -* `openstack["compute"]["novnc_proxy"]["bind_interface"]` - Determine the interface's IP address to bind to - Libvirt Configuration Attributes --------------------------------- * `openstack["compute"]["libvirt"]["virt_type"]` - What hypervisor software layer to use with libvirt (e.g., kvm, qemu) -* `openstack["compute"]["libvirt"]["bind_interface"]` - Determine the interface's IP address (used for VNC). IP address on the hypervisor that libvirt listens for VNC requests on, and IP address on the hypervisor that libvirt exposes for VNC requests on. +* `openstack["compute"]["libvirt"]["volume_backend"]` - What block storage backend to use with libvirt (e.g. rbd) * `openstack["compute"]["libvirt"]["auth_tcp"]` - Type of authentication your libvirt layer requires * `openstack["compute"]["libvirt"]["ssh"]["private_key"]` - Private key to use if using SSH authentication to your libvirt layer * `openstack["compute"]["libvirt"]["ssh"]["public_key"]` - Public key to use if using SSH authentication to your libvirt layer +* `openstack["compute"]["libvirt"]["max_clients"]` - Maximum number of concurrent client connections to allow over all sockets combined. (default: 20) +* `openstack["compute"]["libvirt"]["max_workers"]` - Maximum number of workers spawned, typically equal to max_clients. (default: 20) +* `openstack["compute"]["libvirt"]["max_requests"]` - Total global limit on concurrent RPC calls. Should be at least as large as max_workers. (default: 20) +* `openstack["compute"]["libvirt"]["max_client_requests"]` - Limit on concurrent requests from a single client connection. (default: 5) +* `openstack["compute"]["libvirt"]["libvirt_inject_key"]` - Inject the ssh public key at boot time, without an agent. (default: true) +* `openstack["compute"]["libvirt"]["libvirt_inject_password"]` - Inject the admin password at boot time, without an agent. (default: false) +* `openstack["compute"]["libvirt"]["images_type"]` - How to store local images (ephemeral disks): raw, qcow2, lvm, rbd, or default +* `openstack["compute"]["libvirt"]["volume_group"]` - When images_type is lvm: volume group to use +* `openstack["compute"]["libvirt"]["sparse_logical_volumes"]` - When images_type is lvm: use sparse logical volumes +* `openstack["compute"]["libvirt"]["images_rbd_pool"]` - When images_type is rbd: use this RBD pool +* `openstack["compute"]["libvirt"]["images_rbd_ceph_conf"]` - When images_type is rbd: use this ceph.conf +* `openstack["compute"]["libvirt"]["rbd"]["rbd_user"]` - The cephx user used for accessing the RBD pool used for block storage. (Which pool to use is passed by cinder when nova-compute is instructed to mount a volume.) +* `openstack["compute"]["libvirt"]["rbd"]["rbd_secret_name"]` - The name of the databag item containing the UUID shared between Cinder and nova-compute. `libvirt_rbd` will define a libvirt secret with this UUID, containing the `rbd_user`'s password. The password itself will be retrieved using `get_password` on the service `rbd_block_storage`. Creating the cephx user in a Ceph cluster has to be done outside of the scope of this cookbook. Scheduler Configuration Attributes ---------------------------------- @@ -204,18 +256,100 @@ OSAPI Compute Extentions * `openstack["compute"]["plugins"]` - Array of osapi compute exntesions to add to nova +Miscellaneous Options +--------------------- + +Arrays whose elements will be copied exactly into the respective config files (contents e.g. ['option1=value1', 'option2=value2']). + +* `openstack["compute"]["misc_nova"]` - Array of bare options for `nova.conf`. +* `openstack["compute"]["misc_paste"]` - Array of bare options for `api-paste.ini` + +EC2 Configuration Attributes +---------------------------- + +* `openstack["compute"]["enabled_apis"]` - Which apis have been enabled in nova compute + +Notification Attributes +----------------------- + +* `openstack["compute"]["metering"]`- Boolean that indicates whether to enable the metering attributes with sensible defaults. Default is false. +* `openstack["compute"]["config"]["notification_drivers"]`- An array of drivers to handle sending notifications. +* `openstack["compute"]["config"]["instance_usage_audit"]`- Boolean that indicates whether to generate intance usage audits. +* `openstack["compute"]["config"]["instance_usage_audit_period"]`- Time period to generate instance usages for. Time period must be "hour", "day", "month" or "year". +* `openstack["compute"]["config"]["notify_on_state_change"]`- If set, send compute.instance.update notifications on instance state changes. Valid values are None, "vm_state" or "vm_and_task_state". + +When enabling nova metering with ceilometer, the notification configuration +properties need to be set to values that are different from the default values +used when metering is off. In order to facilitate setting all those +notification properties, the cookbook includes the `openstack["compute"]["metering"]` +attribute which when set to `true` will automatically set all notification +properties to the suggested defaults. + +One of the notification_drivers that is set when metering is on comes from +ceilometer. In order for the notification driver to be available, make sure +the `os-telemetry-agent-compute` role (or the openstack-telemetry::agent-compute recipe) +are set on this node. + +VMware Configuration Attributes +------------------------------- + +* `openstack['compute']['vmware']['secret_name']` - VMware databag secret name +* `openstack['compute']['vmware']['host_ip']` - URL for connection to VMware ESX/VC host. (string value) +* `openstack['compute']['vmware']['host_username']` - Username for connection to VMware ESX/VC host. (string value) +* `openstack['compute']['vmware']['cluster_name']` - Name of a VMware Cluster ComputeResource. Used only if compute_driver is vmwareapi.VMwareVCDriver. (multi valued) +* `openstack['compute']['vmware']['datastore_regex']` - Regex to match the name of a datastore. (string value) +* `openstack['compute']['vmware']['task_poll_interval']` - The interval used for polling of remote tasks. (floating point value, default 0.5) +* `openstack['compute']['vmware']['api_retry_count']` - The number of times we retry on failures, e.g., socket error, etc. (integer value, default 10) +* `openstack['compute']['vmware']['vnc_port']` - VNC starting port (integer value, default 5900) +* `openstack['compute']['vmware']['vnc_port_total']` - Total number of VNC ports (integer value, default 10000) +* `openstack['compute']['vmware']['use_linked_clone']` - Whether to use linked clone (boolean value, default true) +* `openstack['compute']['vmware']['vlan_interface']` - Physical ethernet adapter name for vlan networking (string value, default vmnic0) +* `openstack['compute']['vmware']['wsdl_location']` - Optional VIM Service WSDL Location, you must specify this location of the WSDL files when you try to connect vSphere vCenter versions 5.0 and earlier. +* `openstack['compute']['vmware']['maximum_objects']` - The maximum number of ObjectContent data objects that should be returned in a single result. (integer value, default 100) +* `openstack['compute']['vmware']['integration_bridge']` - Name of Integration Bridge (string value, default br-int) + + +The following attributes are defined in attributes/default.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack['endpoints']['compute-compute api-bind']['host']` - The IP address to bind the compute api service to +* `openstack['endpoints']['compute-compute api-bind']['port']` - The port to bind the compute api service to +* `openstack['endpoints']['compute-compute api-bind']['bind_interface']` - The interface name to bind the compute api service to + +* `openstack['endpoints']['compute-ec2-api-bind']['host']` - The IP address to bind the ec2 api service to +* `openstack['endpoints']['compute-ec2-api-bind']['port']` - The port to bind the ec2 api service to +* `openstack['endpoints']['compute-ec2-api-bind']['bind_interface']` - The interface name to bind the ec2 api service to + +* `openstack['endpoints']['compute-ec2-admin-bind']['host']` - The IP address to bind the ec2 admin api service to +* `openstack['endpoints']['compute-ec2-admin-bind']['port']` - The port to bind the ec2 admin api service to +* `openstack['endpoints']['compute-ec2-admin-bind']['bind_interface']` - The interface name to bind the ec2 admin api service to + +* `openstack['endpoints']['compute-xvpvnc-bind']['host']` - The IP address to bind the xvpvnc service to +* `openstack['endpoints']['compute-xvpvnc-bind']['port']` - The port to bind the xvpvnc service to +* `openstack['endpoints']['compute-xvpvnc-bind']['bind_interface']` - The interface name to bind the xvpvnc service to + +* `openstack['endpoints']['compute-novnc-bind']['host']` - The IP address to bind the novnc service to +* `openstack['endpoints']['compute-novnc-bind']['port']` - The port to bind the novnc service to +* `openstack['endpoints']['compute-novnc-bind']['bind_interface']` - The interface name to bind the novnc service to + +* `openstack['endpoints']['compute-vnc-bind']['host']` - The IP address to bind the vnc service to +* `openstack['endpoints']['compute-vnc-bind']['bind_interface']` - The interface name to bind the vnc service to + +If the value of the 'bind_interface' attribute is non-nil, then the service will be bound to the first IP address on that interface. If the value of the 'bind_interface' attribute is nil, then the service will be bound to the IP address specified in the host attribute. + Testing ===== -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. -Tests are defined in Strainerfile. +Berkshelf +===== -To run tests: - - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -232,16 +366,24 @@ License and Author | **Author** | Matt Ray () | | **Author** | Jay Pipes () | | **Author** | John Dewey () | -| **Author** | Kevin Bringard () | +| **Author** | Kevin Bringard () | | **Author** | Craig Tracey () | | **Author** | Sean Gallagher () | | **Author** | Ionut Artarisi () | +| **Author** | JieHua Jin () | +| **Author** | David Geng () | +| **Author** | Salman Baset () | +| **Author** | Chen Zhiwei () | +| **Author** | Mark Vanderwiel () | +| **Author** | Eric Zhou () | +| **Author** | Mathew Odden () | | | | | **Copyright** | Copyright (c) 2012-2013, Rackspace US, Inc. | | **Copyright** | Copyright (c) 2012-2013, Opscode, Inc. | | **Copyright** | Copyright (c) 2012-2013, AT&T Services, Inc. | | **Copyright** | Copyright (c) 2013, Craig Tracey | | **Copyright** | Copyright (c) 2013, SUSE Linux GmbH | +| **Copyright** | Copyright (c) 2013-2014, IBM, Corp. | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/openstack-compute/Strainerfile b/chef/cookbooks/openstack-compute/Strainerfile index 7e292b4..44e3e14 100644 --- a/chef/cookbooks/openstack-compute/Strainerfile +++ b/chef/cookbooks/openstack-compute/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-compute/TESTING.md b/chef/cookbooks/openstack-compute/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-compute/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-compute/attributes/default.rb b/chef/cookbooks/openstack-compute/attributes/default.rb index b154d10..5316791 100644 --- a/chef/cookbooks/openstack-compute/attributes/default.rb +++ b/chef/cookbooks/openstack-compute/attributes/default.rb @@ -1,338 +1,471 @@ +# encoding: UTF-8 +# ######################################################################## # Toggles - These can be overridden at the environment level -default["enable_monit"] = false # OS provides packages +default['enable_monit'] = false # OS provides packages ######################################################################## # Set to some text value if you want templated config files # to contain a custom banner at the top of the written file -default["openstack"]["compute"]["custom_template_banner"] = " +default['openstack']['compute']['custom_template_banner'] = ' # This file autogenerated by Chef # Do not edit, changes will be overwritten -" +' # The name of the Chef role that knows about the message queue server # that Nova uses -default["openstack"]["compute"]["rabbit_server_chef_role"] = "os-ops-messaging" +default['openstack']['compute']['rabbit_server_chef_role'] = 'os-ops-messaging' -default["openstack"]["compute"]["verbose"] = "False" -default["openstack"]["compute"]["debug"] = "False" +default['openstack']['compute']['verbose'] = 'False' +default['openstack']['compute']['debug'] = 'False' + +default['openstack']['compute']['state_path'] = '/var/lib/nova' +default['openstack']['compute']['instances_path'] = + "#{node['openstack']['compute']['state_path']}/instances" +default['openstack']['compute']['network_allocate_retries'] = 0 +default['openstack']['compute']['instance_name_template'] = 'instance-%08x' + +# The lock_path normally uses /var/lock/nova, but it's not allowed in openSUSE, +# so setting lock_path to $state_path/lock like in Neutron. +default['openstack']['compute']['lock_path'] = + "#{node['openstack']['compute']['state_path']}/lock" + +# Workers +# nova will default to number of cpus +default['openstack']['compute']['ec2_workers'] = nil +default['openstack']['compute']['osapi_compute_workers'] = [6, node['cpu']['total'].to_i].min +default['openstack']['compute']['metadata_workers'] = nil # The name of the Chef role that sets up the Keystone Service API -default["openstack"]["compute"]["identity_service_chef_role"] = "os-identity" +default['openstack']['compute']['identity_service_chef_role'] = 'os-identity' -# This user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# db_password routine. -default["openstack"]["compute"]["db"]["username"] = "nova" +# Common rpc definitions +default['openstack']['compute']['rpc_cast_timeout'] = 300 +default['openstack']['compute']['rpc_response_timeout'] = 300 +default['openstack']['compute']['rpc_thread_pool_size'] = 240 +default['openstack']['compute']['rpc_conn_pool_size'] = 100 -# This user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# user_password routine. You are expected to create -# the user, pass, vhost in a wrapper rabbitmq cookbook. -default["openstack"]["compute"]["rabbit"]["username"] = "guest" -default["openstack"]["compute"]["rabbit"]["vhost"] = "/" -default["openstack"]["compute"]["rabbit"]["port"] = 5672 -default["openstack"]["compute"]["rabbit"]["host"] = "127.0.0.1" -default["openstack"]["compute"]["rabbit"]["ha"] = false - -default["openstack"]["compute"]["service_tenant_name"] = "service" -default["openstack"]["compute"]["service_user"] = "nova" -default["openstack"]["compute"]["service_role"] = "admin" - -case platform -when "fedora", "redhat", "centos", "ubuntu" - default["openstack"]["compute"]["user"] = "nova" - default["openstack"]["compute"]["group"] = "nova" -when "suse" - default["openstack"]["compute"]["user"] = "openstack-nova" - default["openstack"]["compute"]["group"] = "openstack-nova" +case node['openstack']['mq']['service_type'] +when 'rabbitmq' + default['openstack']['compute']['rpc_backend'] = 'nova.openstack.common.rpc.impl_kombu' +when 'qpid' + default['openstack']['compute']['rpc_backend'] = 'nova.openstack.common.rpc.impl_qpid' end +default['openstack']['compute']['service_tenant_name'] = 'service' +default['openstack']['compute']['service_user'] = 'nova' +default['openstack']['compute']['service_role'] = 'admin' + +case platform_family +when 'fedora', 'rhel', 'debian' + default['openstack']['compute']['user'] = 'nova' + default['openstack']['compute']['group'] = 'nova' +when 'suse' + default['openstack']['compute']['user'] = 'openstack-nova' + default['openstack']['compute']['group'] = 'openstack-nova' +end + +# Options defined in nova.image.glance +# Number of retries when downloading an image from glance +# the actually retries num = glance_num_retries + 1 +default['openstack']['compute']['glance_num_retries'] = 2 + # Logging stuff -default["openstack"]["compute"]["syslog"]["use"] = false -default["openstack"]["compute"]["syslog"]["facility"] = "LOG_LOCAL1" -default["openstack"]["compute"]["syslog"]["config_facility"] = "local1" +default['openstack']['compute']['log_dir'] = '/var/log/nova' -default["openstack"]["compute"]["region"] = "RegionOne" +default['openstack']['compute']['syslog']['use'] = false +default['openstack']['compute']['syslog']['facility'] = 'LOG_LOCAL1' +default['openstack']['compute']['syslog']['config_facility'] = 'local1' -default["openstack"]["compute"]["floating_cmd"] = "/usr/local/bin/add_floaters.py" +default['openstack']['compute']['region'] = node['openstack']['region'] -# Support multiple network types. Default network type is "nova" -# with the other option supported being "quantum" -default["openstack"]["compute"]["network"]["service_type"] = "nova" +default['openstack']['compute']['floating_cmd'] = '/usr/local/bin/add_floaters.py' + +# Support multiple network types. Default network type is 'nova' +# with the other option supported being 'neutron' +default['openstack']['compute']['network']['service_type'] = 'nova' # if the network type is not nova, we will load the following # plugins from openstack-network -default["openstack"]["compute"]["network"]["plugins"] = ["openvswitch"] +default['openstack']['compute']['network']['plugins'] = ['openvswitch'] -# Quantum options -default["openstack"]["compute"]["network"]["quantum"]["network_api_class"] = "nova.network.quantumv2.api.API" -default["openstack"]["compute"]["network"]["quantum"]["auth_strategy"] = "keystone" -default["openstack"]["compute"]["network"]["quantum"]["admin_tenant_name"] = "service" -default["openstack"]["compute"]["network"]["quantum"]["admin_username"] = "quantum" -default["openstack"]["compute"]["network"]["quantum"]["libvirt_vif_driver"] = "nova.virt.libvirt.vif.LibvirtHybridOVSBridgeDriver" -default["openstack"]["compute"]["network"]["quantum"]["linuxnet_interface_driver"] = "nova.network.linux_net.LinuxOVSInterfaceDriver" -default["openstack"]["compute"]["network"]["quantum"]["security_group_api"] = "quantum" -default["openstack"]["compute"]["network"]["quantum"]["service_quantum_metadata_proxy"] = true -default["openstack"]["compute"]["network"]["quantum"]["metadata_secret_name"] = "quantum_metadata_shared_secret" -default["openstack"]["compute"]["network"]["quantum"]["public_network_name"] = "public" -default["openstack"]["compute"]["network"]["quantum"]["dns_server"] = "8.8.8.8" +# Neutron options +default["openstack"]["compute"]["network"]["neutron"]["url_timeout"] = 90 +default['openstack']['compute']['network']['neutron']['network_api_class'] = 'nova.network.neutronv2.api.API' +default['openstack']['compute']['network']['neutron']['auth_strategy'] = 'keystone' +default['openstack']['compute']['network']['neutron']['admin_tenant_name'] = 'service' +default['openstack']['compute']['network']['neutron']['admin_username'] = 'neutron' +default['openstack']['compute']['network']['neutron']['libvirt_vif_driver'] = 'nova.virt.libvirt.vif.LibvirtGenericVIFDriver' +default['openstack']['compute']['network']['neutron']['linuxnet_interface_driver'] = 'nova.network.linux_net.LinuxOVSInterfaceDriver' +default['openstack']['compute']['network']['neutron']['security_group_api'] = 'neutron' +default['openstack']['compute']['network']['neutron']['service_neutron_metadata_proxy'] = true +default['openstack']['compute']['network']['neutron']['metadata_secret_name'] = 'neutron_metadata_shared_secret' +default['openstack']['compute']['network']['neutron']['public_network_name'] = 'public' +default['openstack']['compute']['network']['neutron']['dns_server'] = '8.8.8.8' -# TODO(shep): This should probably be ["openstack"]["compute"]["network"]["fixed"] -default["openstack"]["compute"]["networks"] = [ +# TODO(shep): This should probably be ['openstack']['compute']['network']['fixed'] +default['openstack']['compute']['networks'] = [ { - "label" => "public", - "ipv4_cidr" => "192.168.100.0/24", - "num_networks" => "1", - "network_size" => "255", - "bridge" => "br100", - "bridge_dev" => "eth2", - "dns1" => "8.8.8.8", - "dns2" => "8.8.4.4", - "multi_host" => 'T' + 'label' => 'public', + 'ipv4_cidr' => '192.168.100.0/24', + 'num_networks' => '1', + 'network_size' => '255', + 'bridge' => 'br100', + 'bridge_dev' => 'eth2', + 'dns1' => '8.8.8.8', + 'dns2' => '8.8.4.4', + 'multi_host' => 'T' }, { - "label" => "private", - "ipv4_cidr" => "192.168.200.0/24", - "num_networks" => "1", - "network_size" => "255", - "bridge" => "br200", - "bridge_dev" => "eth3", - "dns1" => "8.8.8.8", - "dns2" => "8.8.4.4", - "multi_host" => 'T' + 'label' => 'private', + 'ipv4_cidr' => '192.168.200.0/24', + 'num_networks' => '1', + 'network_size' => '255', + 'bridge' => 'br200', + 'bridge_dev' => 'eth3', + 'dns1' => '8.8.8.8', + 'dns2' => '8.8.4.4', + 'multi_host' => 'T' } ] # For VLAN Networking, do the following: # -# default["openstack"]["compute"]["network"]["network_manager"] = "nova.network.manager.VlanManager" -# default["openstack"]["compute"]["network"]["vlan_interface"] = "eth1" # Or "eth2", "bond1", etc... -# # The fixed_range setting is the **entire** subnet/network that all your VLAN -# # networks will fit inside. -# default["openstack"]["compute"]["network"]["fixed_range"] = "10.0.0.0/8" # Or smaller for smaller deploys... +# default['openstack']['compute']['network']['network_manager'] = 'nova.network.manager.VlanManager' +# default['openstack']['compute']['network']['vlan_interface'] = 'eth1' # Or 'eth2', 'bond1', etc... # # In addition to the above, you typically either want to do one of the following: # -# 1) Set default["openstack"]["compute"]["networks"] to an empty Array ([]) and create your +# 1) Set default['openstack']['compute']['networks'] to an empty Array ([]) and create your # VLAN networks (using nova-manage network create) **when you create a tenant**. # -# 2) Set default["openstack"]["compute"]["networks"] to an Array of VLAN networks that get created +# 2) Set default['openstack']['compute']['networks'] to an Array of VLAN networks that get created # **without a tenant assignment** for tenants to use when they are created later. # Such an array might look like this: # -# default["openstack"]["compute"]["networks"] = [ +# default['openstack']['compute']['networks'] = [ # { -# "label": "vlan100", -# "vlan": "100", -# "ipv4_cidr": "10.0.100.0/24" +# 'label': 'vlan100', +# 'vlan': '100', +# 'ipv4_cidr': '10.0.100.0/24' # }, # { -# "label": "vlan101", -# "vlan": "101", -# "ipv4_cidr": "10.0.101.0/24" +# 'label': 'vlan101', +# 'vlan': '101', +# 'ipv4_cidr': '10.0.101.0/24' # }, # { -# "label": "vlan102", -# "vlan": "102", -# "ipv4_cidr": "10.0.102.0/24" +# 'label': 'vlan102', +# 'vlan': '102', +# 'ipv4_cidr': '10.0.102.0/24' # }, # ] -default["openstack"]["compute"]["network"]["multi_host"] = false -default["openstack"]["compute"]["network"]["fixed_range"] = default["openstack"]["compute"]["networks"][0]["ipv4_cidr"] +default['openstack']['compute']['network']['multi_host'] = false # DMZ CIDR is a range of IP addresses that should not # have their addresses SNAT'ed by the nova network controller -default["openstack"]["compute"]["network"]["dmz_cidr"] = "10.128.0.0/24" -default["openstack"]["compute"]["network"]["network_manager"] = "nova.network.manager.FlatDHCPManager" -default["openstack"]["compute"]["network"]["public_interface"] = "eth0" -default["openstack"]["compute"]["network"]["vlan_interface"] = "eth0" +default['openstack']['compute']['network']['dmz_cidr'] = '10.128.0.0/24' +default['openstack']['compute']['network']['network_manager'] = 'nova.network.manager.FlatDHCPManager' +default['openstack']['compute']['network']['public_interface'] = 'eth0' +default['openstack']['compute']['network']['vlan_interface'] = 'eth0' +default['openstack']['compute']['network']['auto_assign_floating_ip'] = false # https://bugs.launchpad.net/nova/+bug/1075859 -default["openstack"]["compute"]["network"]["use_single_default_gateway"] = false +default['openstack']['compute']['network']['use_single_default_gateway'] = false -default["openstack"]["compute"]["scheduler"]["scheduler_driver"] = "nova.scheduler.filter_scheduler.FilterScheduler" -default["openstack"]["compute"]["scheduler"]["default_filters"] = [ - "AvailabilityZoneFilter", - "RamFilter", - "ComputeFilter", - "CoreFilter", - "SameHostFilter", - "DifferentHostFilter" -] +default['openstack']['compute']['scheduler']['scheduler_driver'] = 'nova.scheduler.filter_scheduler.FilterScheduler' +default['openstack']['compute']['scheduler']['default_filters'] = %W( + AvailabilityZoneFilter + RamFilter + ComputeFilter + CoreFilter + SameHostFilter + DifferentHostFilter) -default["openstack"]["compute"]["xvpvnc_proxy"]["service_port"] = "6081" -default["openstack"]["compute"]["xvpvnc_proxy"]["bind_interface"] = "lo" -default["openstack"]["compute"]["novnc_proxy"]["service_port"] = "6080" -default["openstack"]["compute"]["novnc_proxy"]["bind_interface"] = "lo" +default['openstack']['compute']['scheduler']['host_subset_size']= 100 -default["openstack"]["compute"]["driver"] = "libvirt.LibvirtDriver" -default["openstack"]["compute"]["libvirt"]["virt_type"] = "kvm" -default["openstack"]["compute"]["libvirt"]["bind_interface"] = "lo" -default["openstack"]["compute"]["libvirt"]["auth_tcp"] = "none" -default["openstack"]["compute"]["libvirt"]["remove_unused_base_images"] = true -default["openstack"]["compute"]["libvirt"]["remove_unused_resized_minimum_age_seconds"] = 3600 -default["openstack"]["compute"]["libvirt"]["remove_unused_original_minimum_age_seconds"] = 3600 -default["openstack"]["compute"]["libvirt"]["checksum_base_images"] = false -if node["platform"] == "suse" - default["openstack"]["compute"]["libvirt"]["group"] = "libvirt" +default['openstack']['compute']['driver'] = 'libvirt.LibvirtDriver' +default['openstack']['compute']['default_ephemeral_format'] = nil +default['openstack']['compute']['preallocate_images'] = 'none' +default['openstack']['compute']['use_cow_images'] = true +default['openstack']['compute']['vif_plugging_is_fatal'] = 'True' +default['openstack']['compute']['vif_plugging_timeout'] = 360 + +default['openstack']['compute']['libvirt']['virt_type'] = 'kvm' +default['openstack']['compute']['libvirt']['virt_auto'] = false +default['openstack']['compute']['libvirt']['auth_tcp'] = 'none' +default['openstack']['compute']['libvirt']['remove_unused_base_images'] = true +default['openstack']['compute']['libvirt']['remove_unused_resized_minimum_age_seconds'] = 3600 +default['openstack']['compute']['libvirt']['remove_unused_original_minimum_age_seconds'] = 3600 +default['openstack']['compute']['libvirt']['checksum_base_images'] = false +# libvirt.max_clients (default: 20) +default['openstack']['compute']['libvirt']['max_clients'] = 20 +# libvirt.max_workers (default: 20) +default['openstack']['compute']['libvirt']['max_workers'] = 20 +# libvirt.max_requests (default: 20) +default['openstack']['compute']['libvirt']['max_requests'] = 20 +# libvirt.max_client_requests (default: 5) +default['openstack']['compute']['libvirt']['max_client_requests'] = 5 +if node['platform'] == 'suse' + default['openstack']['compute']['libvirt']['group'] = 'libvirt' else - default["openstack"]["compute"]["libvirt"]["group"] = "libvirtd" + default['openstack']['compute']['libvirt']['group'] = 'libvirtd' end -default["openstack"]["compute"]["config"]["availability_zone"] = "nova" -default["openstack"]["compute"]["config"]["storage_availability_zone"] = "nova" -default["openstack"]["compute"]["config"]["default_schedule_zone"] = "nova" -default["openstack"]["compute"]["config"]["force_raw_images"] = false -default["openstack"]["compute"]["config"]["allow_same_net_traffic"] = true -default["openstack"]["compute"]["config"]["osapi_max_limit"] = 1000 -default["openstack"]["compute"]["config"]["cpu_allocation_ratio"] = 16.0 -default["openstack"]["compute"]["config"]["ram_allocation_ratio"] = 1.5 -default["openstack"]["compute"]["config"]["snapshot_image_format"] = "qcow2" +default['openstack']['compute']['libvirt']['libvirt_inject_key'] = true +default['openstack']['compute']['libvirt']['libvirt_inject_password'] = false +# VM Images format. Acceptable values are: raw, qcow2, lvm, +# rbd, default. If default is specified, then use_cow_images +# flag is used instead of this one. (string value) +# (The cookbook doesn't set that value, so if you want cow, set qcow2 here.) +default['openstack']['compute']['libvirt']['images_type'] = 'default' +# lvm +default['openstack']['compute']['libvirt']['volume_group'] = nil +default['openstack']['compute']['libvirt']['sparse_logical_volumes'] = false +# rbd +default['openstack']['compute']['libvirt']['images_rbd_pool'] = 'rbd' +default['openstack']['compute']['libvirt']['images_rbd_ceph_conf'] = '/etc/ceph/ceph.conf' +# use a different backend for volumes, allowed options: rbd +default['openstack']['compute']['libvirt']['volume_backend'] = nil +default['openstack']['compute']['libvirt']['rbd']['rbd_secret_name'] = 'rbd_secret_uuid' +default['openstack']['compute']['libvirt']['rbd']['rbd_user'] = 'cinder' +default['openstack']['compute']['libvirt']['host_uuid'] = nil + +default['openstack']['compute']['config']['availability_zone'] = 'nova' +default['openstack']['compute']['config']['storage_availability_zone'] = 'nova' +default['openstack']['compute']['config']['default_schedule_zone'] = 'nova' +default['openstack']['compute']['config']['force_raw_images'] = false +default['openstack']['compute']['config']['allow_same_net_traffic'] = true +default['openstack']['compute']['config']['osapi_max_limit'] = 5000 +default['openstack']['compute']['config']['cpu_allocation_ratio'] = 2.0 +default['openstack']['compute']['config']['ram_allocation_ratio'] = 1.0 +default['openstack']['compute']['config']['disk_allocation_ratio'] = 1.0 +default['openstack']['compute']['config']['snapshot_image_format'] = 'qcow2' +default['openstack']['compute']['config']['allow_resize_to_same_host'] = false # `start` will cause nova-compute to error out if a VM is already running, where # `resume` checks to see if it is running first. -default["openstack"]["compute"]["config"]["start_guests_on_host_boot"] = false +default['openstack']['compute']['config']['start_guests_on_host_boot'] = false # requires https://review.openstack.org/#/c/8423/ -default["openstack"]["compute"]["config"]["resume_guests_state_on_host_boot"] = true +default['openstack']['compute']['config']['resume_guests_state_on_host_boot'] = true -# If true, create a config drive regardless of if the user specified --config-drive true in their nova boot call -default["openstack"]["compute"]["config"]["force_config_drive"] = "false" +# force_config_drive can be nil or 'always', +# if it's 'always', nova will create a config drive regardless of if the user specified --config-drive true in their nova boot call +default['openstack']['compute']['config']['force_config_drive'] = nil +default['openstack']['compute']['config']['mkisofs_cmd'] = 'genisoimage' +default['openstack']['compute']['config']['injected_network_template'] = '$pybasedir/nova/virt/interfaces.template' # Volume API class (driver) -default["openstack"]["compute"]["config"]["volume_api_class"] = "nova.volume.cinder.API" +default['openstack']['compute']['config']['volume_api_class'] = 'nova.volume.cinder.API' # quota settings -default["openstack"]["compute"]["config"]["quota_security_groups"] = 50 -default["openstack"]["compute"]["config"]["quota_security_group_rules"] = 20 +default['openstack']['compute']['config']['quota_security_groups'] = 200 +default['openstack']['compute']['config']['quota_security_group_rules'] = 200 # (StrOpt) default driver to use for quota checks (default: nova.quota.DbQuotaDriver) -default["openstack"]["compute"]["config"]["quota_driver"] = "nova.quota.DbQuotaDriver" +default['openstack']['compute']['config']['quota_driver'] = 'nova.quota.DbQuotaDriver' # number of instance cores allowed per project (default: 20) -default["openstack"]["compute"]["config"]["quota_cores"] = 20 +default['openstack']['compute']['config']['quota_cores'] = 200 # number of fixed ips allowed per project (this should be at least the number of instances allowed) (default: -1) -default["openstack"]["compute"]["config"]["quota_fixed_ips"] = -1 +default['openstack']['compute']['config']['quota_fixed_ips'] = -1 # number of floating ips allowed per project (default: 10) -default["openstack"]["compute"]["config"]["quota_floating_ips"] = 10 +default['openstack']['compute']['config']['quota_floating_ips'] = 100 # number of bytes allowed per injected file (default: 10240) -default["openstack"]["compute"]["config"]["quota_injected_file_content_bytes"] = 10240 +default['openstack']['compute']['config']['quota_injected_file_content_bytes'] = 10240 # number of bytes allowed per injected file path (default: 255) -default["openstack"]["compute"]["config"]["quota_injected_file_path_bytes"] = 255 +default['openstack']['compute']['config']['quota_injected_file_path_bytes'] = 255 # number of injected files allowed (default: 5) -default["openstack"]["compute"]["config"]["quota_injected_files"] = 5 +default['openstack']['compute']['config']['quota_injected_files'] = 5 # number of instances allowed per project (defailt: 10) -default["openstack"]["compute"]["config"]["quota_instances"] = 10 +default['openstack']['compute']['config']['quota_instances'] = 100 # number of key pairs per user (default: 100) -default["openstack"]["compute"]["config"]["quota_key_pairs"] = 100 +default['openstack']['compute']['config']['quota_key_pairs'] = 100 # number of metadata items allowed per instance (default: 128) -default["openstack"]["compute"]["config"]["quota_metadata_items"] = 128 +default['openstack']['compute']['config']['quota_metadata_items'] = 128 # megabytes of instance ram allowed per project (default: 51200) -default["openstack"]["compute"]["config"]["quota_ram"] = 51200 +default['openstack']['compute']['config']['quota_ram'] = 2048000 +# disk cache modes +default['openstack']['compute']['config']['disk_cache_modes'] = nil -default["openstack"]["compute"]["ratelimit"]["settings"] = { - "generic-post-limit" => { "verb" => "POST", "uri" => "*", "regex" => ".*", "limit" => "10", "interval" => "MINUTE" }, - "create-servers-limit" => { "verb" => "POST", "uri" => "*/servers", "regex" => "^/servers", "limit" => "50", "interval" => "DAY" }, - "generic-put-limit" => { "verb" => "PUT", "uri" => "*", "regex" => ".*", "limit" => "10", "interval" => "MINUTE" }, - "changes-since-limit" => { "verb" => "GET", "uri" => "*changes-since*", "regex" => ".*changes-since.*", "limit" => "3", "interval" => "MINUTE" }, - "generic-delete-limit" => { "verb" => "DELETE", "uri" => "*", "regex" => ".*", "limit" => "100", "interval" => "MINUTE" } +default['openstack']['compute']['ratelimit']['settings'] = { + 'generic-post-limit' => { 'verb' => 'POST', 'uri' => '*', 'regex' => '.*', 'limit' => '10', 'interval' => 'MINUTE' }, + 'create-servers-limit' => { 'verb' => 'POST', 'uri' => '*/servers', 'regex' => '^/servers', 'limit' => '50', 'interval' => 'DAY' }, + 'generic-put-limit' => { 'verb' => 'PUT', 'uri' => '*', 'regex' => '.*', 'limit' => '10', 'interval' => 'MINUTE' }, + 'changes-since-limit' => { 'verb' => 'GET', 'uri' => '*changes-since*', 'regex' => '.*changes-since.*', 'limit' => '3', 'interval' => 'MINUTE' }, + 'generic-delete-limit' => { 'verb' => 'DELETE', 'uri' => '*', 'regex' => '.*', 'limit' => '100', 'interval' => 'MINUTE' } } -# Keystone settings -default["openstack"]["compute"]["api"]["auth_strategy"] = "keystone" +# Metering settings +default['openstack']['compute']['metering'] = false -# Setting this to v2.0. See discussion on -# https://bugs.launchpad.net/openstack-chef/+bug/1207504 -default["openstack"]["compute"]["api"]["auth"]["version"] = "v2.0" +# Notification settings +if node['openstack']['compute']['metering'] + default['openstack']['compute']['config']['notification_drivers'] = ['nova.openstack.common.notifier.rpc_notifier', 'ceilometer.compute.nova_notifier'] + default['openstack']['compute']['config']['instance_usage_audit'] = 'True' + default['openstack']['compute']['config']['instance_usage_audit_period'] = 'hour' + default['openstack']['compute']['config']['notify_on_state_change'] = 'vm_and_task_state' +else + default['openstack']['compute']['config']['notification_drivers'] = [] + default['openstack']['compute']['config']['instance_usage_audit'] = 'False' + default['openstack']['compute']['config']['instance_usage_audit_period'] = 'month' + default['openstack']['compute']['config']['notify_on_state_change'] = '' +end + +# vncproxy setttings +default['openstack']['compute']['vnc']['vncserver_listen'] = '0.0.0.0' +# nova consoleauth token backend could be 'memcache' or nil: +# 'memcache' means consoleauth storage tokens into memcache, +# it can work on single or multiply consoleauth +# nil means consoleauth storage tokens in its local memory, +# only works on single node. +default['openstack']['compute']['consoleauth']['token']['backend'] = 'memcache' + +# Keystone settings +default['openstack']['compute']['api']['auth_strategy'] = 'keystone' + +default['openstack']['compute']['api']['auth']['version'] = node['openstack']['api']['auth']['version'] # Keystone PKI signing directories -default["openstack"]["compute"]["api"]["auth"]["cache_dir"] = "/var/cache/nova/api" +default['openstack']['compute']['api']['auth']['cache_dir'] = '/var/cache/nova/api' +# Conductor settings # Perform nova-conductor operations locally (boolean value) -default["openstack"]["compute"]["conductor"]["use_local"] = "False" +default['openstack']['compute']['conductor']['use_local'] = 'False' +# nova-conductor will default to number of cpus +default['openstack']['compute']['conductor']['workers'] = [6, node['cpu']['total'].to_i].min -case platform -when "fedora", "redhat", "centos", "suse" # :pragma-foodcritic: ~FC024 - won't fix this - default["openstack"]["compute"]["platform"] = { - "api_ec2_packages" => ["openstack-nova-api"], - "api_ec2_service" => "openstack-nova-api", - "api_os_compute_packages" => ["openstack-nova-api"], - "api_os_compute_service" => "openstack-nova-api", - "api_os_compute_process_name" => "nova-api", - # "neutron_python_packages" => ["python-quantumclient", "python-pyparsing"], - "neutron_python_packages" => ["python-quantumclient", "pyparsing"], - "memcache_python_packages" => ["python-memcached"], - "compute_api_metadata_packages" => ["openstack-nova-api"], - "compute_api_metadata_process_name" => "nova-api", - "compute_api_metadata_service" => "openstack-nova-api", - "compute_compute_packages" => ["openstack-nova-compute"], - "compute_compute_service" => "openstack-nova-compute", - "compute_network_packages" => ["iptables", "openstack-nova-network"], - "compute_network_service" => "openstack-nova-network", - "compute_scheduler_packages" => ["openstack-nova-scheduler"], - "compute_scheduler_service" => "openstack-nova-scheduler", - "compute_conductor_packages" => ["openstack-nova-conductor"], - "compute_conductor_service" => "openstack-nova-conductor", - "compute_vncproxy_packages" => ["openstack-nova-novncproxy"], # me thinks this is right? - "compute_vncproxy_service" => "openstack-nova-novncproxy", - "compute_vncproxy_console_packages" => ["openstack-nova-console"], - "compute_vncproxy_console_service" => "openstack-nova-console", - "compute_vncproxy_console_process_name" => "nova-console", - "compute_vncproxy_consoleauth_packages" => ["openstack-nova-console"], - "compute_vncproxy_consoleauth_service" => "openstack-nova-consoleauth", - "compute_vncproxy_consoleauth_process_name" => "nova-consoleauth", - "libvirt_packages" => ["libvirt"], - "libvirt_service" => "libvirtd", - "compute_cert_packages" => ["openstack-nova-cert"], - "compute_cert_service" => "openstack-nova-cert", - "mysql_service" => "mysqld", - "common_packages" => ["openstack-nova-common"], - "iscsi_helper" => "ietadm", - "nfs_packages" => ["nfs-utils", "nfs-utils-lib"], - "package_overrides" => "" +default['openstack']['compute']['network']['force_dhcp_release'] = true + +case platform_family +when 'fedora', 'rhel', 'suse' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['compute']['platform'] = { + 'mysql_python_packages' => ['MySQL-python'], + 'db2_python_packages' => ['python-ibm-db', 'python-ibm-db-sa'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'api_ec2_packages' => ['openstack-nova-api'], + 'api_ec2_service' => 'openstack-nova-api', + 'api_os_compute_packages' => ['openstack-nova-api'], + 'api_os_compute_service' => 'openstack-nova-api', + 'neutron_python_packages' => ['python-neutronclient', 'pyparsing'], + 'memcache_python_packages' => ['python-memcached'], + 'compute_api_metadata_packages' => ['openstack-nova-api'], + 'compute_api_metadata_service' => 'openstack-nova-api', + 'compute_client_packages' => ['python-novaclient'], + 'compute_compute_packages' => ['openstack-nova-compute'], + 'qemu_compute_packages' => [], + 'kvm_compute_packages' => [], + 'compute_compute_service' => 'openstack-nova-compute', + 'compute_network_packages' => ['iptables', 'openstack-nova-network'], + 'compute_network_service' => 'openstack-nova-network', + 'compute_scheduler_packages' => ['openstack-nova-scheduler'], + 'compute_scheduler_service' => 'openstack-nova-scheduler', + 'compute_conductor_packages' => ['openstack-nova-conductor'], + 'compute_conductor_service' => 'openstack-nova-conductor', + 'compute_vncproxy_packages' => ['openstack-nova-novncproxy'], + 'compute_vncproxy_service' => 'openstack-nova-novncproxy', + 'compute_vncproxy_consoleauth_packages' => ['openstack-nova-console'], + 'compute_vncproxy_consoleauth_service' => 'openstack-nova-consoleauth', + 'libvirt_packages' => ['libvirt', 'dmidecode'], + 'libvirt_service' => 'libvirtd', + 'libvirt_ceph_packages' => [], + 'dbus_service' => 'messagebus', + 'compute_cert_packages' => ['openstack-nova-cert'], + 'compute_cert_service' => 'openstack-nova-cert', + 'mysql_service' => 'mysqld', + 'common_packages' => ['openstack-nova-common'], + 'iscsi_helper' => 'ietadm', + 'nfs_packages' => ['nfs-utils', 'nfs-utils-lib'], + 'package_overrides' => '' } - if platform == "suse" - default["openstack"]["compute"]["platform"]["common_packages"] = ["openstack-nova"] - default["openstack"]["compute"]["platform"]["kvm_packages"] = ["kvm"] - default["openstack"]["compute"]["platform"]["xen_packages"] = ["kernel-xen", "xen", "xen-tools"] - default["openstack"]["compute"]["platform"]["lxc_packages"] = ["lxc"] - default["openstack"]["compute"]["platform"]["nfs_packages"] = ["nfs-utils"] + if platform_family == 'suse' + default['openstack']['compute']['platform']['mysql_python_packages'] = ['python-mysql'] + default['openstack']['compute']['platform']['dbus_service'] = 'dbus' + default['openstack']['compute']['platform']['neutron_python_packages'] = ['python-neutronclient', 'python-pyparsing'] + default['openstack']['compute']['platform']['common_packages'] = ['openstack-nova'] + default['openstack']['compute']['platform']['kvm_packages'] = ['kvm'] + default['openstack']['compute']['platform']['xen_packages'] = ['kernel-xen', 'xen', 'xen-tools'] + default['openstack']['compute']['platform']['lxc_packages'] = ['lxc'] + default['openstack']['compute']['platform']['nfs_packages'] = ['nfs-utils'] end -when "ubuntu" - default["openstack"]["compute"]["platform"] = { - "api_ec2_packages" => ["nova-api-ec2"], - "api_ec2_service" => "nova-api-ec2", - "api_os_compute_packages" => ["nova-api"], - "api_os_compute_process_name" => "nova-api", - "api_os_compute_service" => "nova-api", - "memcache_python_packages" => ["python-memcache"], - "neutron_python_packages" => ["python-quantumclient", "python-pyparsing"], - "compute_api_metadata_packages" => ["nova-api-metadata"], - "compute_api_metadata_service" => "nova-api-metadata", - "compute_api_metadata_process_name" => "nova-api-metadata", - "compute_compute_packages" => ["nova-compute"], - "compute_compute_service" => "nova-compute", - "compute_network_packages" => ["iptables", "nova-network"], - "compute_network_service" => "nova-network", - "compute_scheduler_packages" => ["nova-scheduler"], - "compute_scheduler_service" => "nova-scheduler", - "compute_conductor_packages" => ["nova-conductor"], - "compute_conductor_service" => "nova-conductor", + # Since the bug (https://bugzilla.redhat.com/show_bug.cgi?id=788485) not released in epel yet + # For 'fedora', 'redhat', 'centos', we need set the default value of force_dhcp_release is 'false' + default['openstack']['compute']['network']['force_dhcp_release'] = false +when 'debian' + default['openstack']['compute']['platform'] = { + 'mysql_python_packages' => ['python-mysqldb'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'api_ec2_packages' => ['nova-api-ec2'], + 'api_ec2_service' => 'nova-api-ec2', + 'api_os_compute_packages' => ['nova-api-os-compute'], + 'api_os_compute_service' => 'nova-api-os-compute', + 'memcache_python_packages' => ['python-memcache'], + 'neutron_python_packages' => ['python-neutronclient', 'python-pyparsing'], + 'compute_api_metadata_packages' => ['nova-api-metadata'], + 'compute_api_metadata_service' => 'nova-api-metadata', + 'compute_client_packages' => ['python-novaclient'], + 'compute_compute_packages' => ['nova-compute'], + 'qemu_compute_packages' => ['nova-compute-qemu'], + 'kvm_compute_packages' => ['nova-compute-kvm'], + 'compute_compute_service' => 'nova-compute', + 'compute_network_packages' => ['iptables', 'nova-network'], + 'compute_network_service' => 'nova-network', + 'compute_scheduler_packages' => ['nova-scheduler'], + 'compute_scheduler_service' => 'nova-scheduler', + 'compute_conductor_packages' => ['nova-conductor'], + 'compute_conductor_service' => 'nova-conductor', # Websockify is needed due to https://bugs.launchpad.net/ubuntu/+source/nova/+bug/1076442 - "compute_vncproxy_packages" => ["novnc", "websockify", "nova-novncproxy"], - "compute_vncproxy_service" => "nova-novncproxy", - "compute_vncproxy_console_packages" => ["nova-console"], - "compute_vncproxy_console_service" => "nova-console", - "compute_vncproxy_console_process_name" => "nova-console", - "compute_vncproxy_consoleauth_packages" => ["nova-consoleauth"], - "compute_vncproxy_consoleauth_service" => "nova-consoleauth", - "compute_vncproxy_consoleauth_process_name" => "nova-consoleauth", - "libvirt_packages" => ["libvirt-bin"], - "libvirt_service" => "libvirt-bin", - "compute_cert_packages" => ["nova-cert"], - "compute_cert_service" => "nova-cert", - "mysql_service" => "mysql", - "common_packages" => ["nova-common"], - "iscsi_helper" => "tgtadm", - "nfs_packages" => ["nfs-common"], - "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + 'compute_vncproxy_packages' => ['novnc', 'websockify', 'nova-novncproxy'], + 'compute_vncproxy_service' => 'nova-novncproxy', + 'compute_vncproxy_consoleauth_packages' => ['nova-consoleauth'], + 'compute_vncproxy_consoleauth_service' => 'nova-consoleauth', + 'libvirt_packages' => ['libvirt-bin', 'dmidecode'], + 'libvirt_service' => 'libvirt-bin', + 'libvirt_ceph_packages' => ['ceph-common'], + 'dbus_service' => 'dbus', + 'compute_cert_packages' => ['nova-cert'], + 'compute_cert_service' => 'nova-cert', + 'mysql_service' => 'mysql', + 'common_packages' => ['nova-common'], + 'iscsi_helper' => 'tgtadm', + 'nfs_packages' => ['nfs-common'], + 'package_overrides' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" } end # plugins -default["openstack"]["compute"]["plugins"] = nil +default['openstack']['compute']['plugins'] = nil + +# Array of options for `nova.conf` (e.g. ['option1=value1', 'option2=value2']) +default['openstack']['compute']['misc_nova'] = nil +# Array of options for `api-paste.ini` (e.g. ['option1=value1', ...]) +default['openstack']['compute']['misc_paste'] = nil + +# To disable the EC2 API endpoint, simply remove 'ec2,' from the list +# of enabled API services. +#default['openstack']['compute']['enabled_apis'] = 'ec2,osapi_compute,metadata' +default['openstack']['compute']['enabled_apis'] = 'osapi_compute' + +# VMware driver +default['openstack']['compute']['vmware']['secret_name'] = 'openstack_vmware_secret_name' +# URL for connection to VMware ESX/VC host. (string value) +default['openstack']['compute']['vmware']['host_ip'] = '' +# Username for connection to VMware ESX/VC host. (string value) +default['openstack']['compute']['vmware']['host_username'] = '' +# Name of a VMware Cluster ComputeResource. Used only if compute_driver is vmwareapi.VMwareVCDriver. (multi valued) +default['openstack']['compute']['vmware']['cluster_name'] = [] +# Regex to match the name of a datastore. (string value) +default['openstack']['compute']['vmware']['datastore_regex'] = nil +# The interval used for polling of remote tasks. (floating point value, default 0.5) +default['openstack']['compute']['vmware']['task_poll_interval'] = 0.5 +# The number of times we retry on failures, e.g., socket error, etc. (integer value, default 10) +default['openstack']['compute']['vmware']['api_retry_count'] = 10 +# VNC starting port (integer value, default 5900) +default['openstack']['compute']['vmware']['vnc_port'] = 5900 +# Total number of VNC ports (integer value, default 10000) +default['openstack']['compute']['vmware']['vnc_port_total'] = 10000 +# Whether to use linked clone (boolean value, default true) +default['openstack']['compute']['vmware']['use_linked_clone'] = true +# Physical ethernet adapter name for vlan networking (string value, default vmnic0) +default['openstack']['compute']['vmware']['vlan_interface'] = 'vmnic0' +# Optional VIM Service WSDL Location, you must specify this location of the WSDL files when you try to connect vSphere vCenter versions 5.0 and earlier. +default['openstack']['compute']['vmware']['wsdl_location'] = nil +# The maximum number of ObjectContent data objects that should be returned in a single result. (integer value, default 100) +default['openstack']['compute']['vmware']['maximum_objects'] = 100 +# Name of Integration Bridge (string value, default br-int) +default['openstack']['compute']['vmware']['integration_bridge'] = 'br-int' diff --git a/chef/cookbooks/openstack-compute/files/default/add_floaters.py b/chef/cookbooks/openstack-compute/files/default/add_floaters.py index ddd2443..dade2ea 100644 --- a/chef/cookbooks/openstack-compute/files/default/add_floaters.py +++ b/chef/cookbooks/openstack-compute/files/default/add_floaters.py @@ -21,7 +21,7 @@ import subprocess import netaddr -DESCRIPTION = "A `nova-manage floating create` and `quantum net create` wrapper." +DESCRIPTION = "A `nova-manage floating create` and `neutron net create` wrapper." class FloatingAddress(object): @@ -31,7 +31,7 @@ class FloatingAddress(object): network, nova-manage doesn't account for this. TODO(retr0h): This should really be added to nova-manage. - TODO(jaypipes): Instead of subprocess calls, just use the quantumclient + TODO(jaypipes): Instead of subprocess calls, just use the neutronclient """ def __init__(self, args): @@ -72,18 +72,18 @@ class FloatingAddress(object): cidr = netaddr.IPNetwork(cidr) # ensure we have a public network and we only ever create one - cmd = "if ! quantum net-show public; then quantum net-create %s -- --router:external=True; fi" % self._args.pool + cmd = "NETLIST=$(neutron net-list -c name); if [ $? -eq 0 ]; then if ! echo $NETLIST | grep -q %s; then neutron net-create %s -- --router:external=True; fi; fi;" % (self._args.pool, self._args.pool) try: subprocess.check_call(cmd, shell=True) except: # we failed to query the quanutm api, we'll ignore this error # and return now so any surrounding chef runs can continue - # since this script may actually be running on the quantum api - print "ERROR: Failed to query the quantum api for the public network" + # since this script may actually be running on the neutron api + print "ERROR: Failed to query the neutron api for the public network" return - cmd = "quantum subnet-list -Fcidr -fcsv --quote=none | grep '%s'" % cidr + cmd = "neutron subnet-list -Fcidr -fcsv --quote=none | grep '%s'" % cidr res = subprocess.call(cmd, shell=True) if res == 0: @@ -95,7 +95,7 @@ class FloatingAddress(object): ip_end = netaddr.IPAddress(cidr.last-1) # create a new subnet - cmd = "quantum subnet-create --allocation-pool start=%s,end=%s %s %s -- --enable_dhcp=False" % \ + cmd = "neutron subnet-create --allocation-pool start=%s,end=%s %s %s -- --enable_dhcp=False" % \ (ip_start, ip_end, self._args.pool, cidr) subprocess.check_call(cmd, shell=True) diff --git a/chef/cookbooks/openstack-compute/files/default/nova-compute.conf b/chef/cookbooks/openstack-compute/files/default/nova-compute.conf index 099e72c..81c5705 100644 --- a/chef/cookbooks/openstack-compute/files/default/nova-compute.conf +++ b/chef/cookbooks/openstack-compute/files/default/nova-compute.conf @@ -1,5 +1,10 @@ # This file autogenerated by Chef # Do not edit, changes will be overwritten # -# P.S. Ubuntu YOUR DOING IT WRONG!! +# On ubuntu systems, this file is populated (by the nova-compute package) with: # +# libvirt_type=kvm +# compute_driver=libvirt.LibvirtDriver +# +# In order to properly manage these options in the main nova.conf, we need to +# replace that file with this empty one. diff --git a/chef/cookbooks/openstack-compute/metadata.rb b/chef/cookbooks/openstack-compute/metadata.rb index 25abb68..5a3f6b6 100644 --- a/chef/cookbooks/openstack-compute/metadata.rb +++ b/chef/cookbooks/openstack-compute/metadata.rb @@ -1,33 +1,33 @@ -name "openstack-compute" -maintainer "Opscode, Inc." -maintainer_email "matt@opscode.com" -license "Apache 2.0" -description "The OpenStack Compute service Nova." +name 'openstack-compute' +maintainer 'Opscode, Inc.' +maintainer_email 'matt@opscode.com' +license 'Apache 2.0' +description 'The OpenStack Compute service Nova.' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "7.0.0" +version '9.2.2' -# recipe "openstack-compute::api-ec2", "Installs AWS EC2 compatible API" -# recipe "openstack-compute::api-metadata", "Installs the nova metadata package" -# recipe "openstack-compute::api-os-compute", "Installs OS API" -recipe "openstack-compute::compute", "nova-compute service" -recipe "openstack-compute::libvirt", "Installs libvirt, used by nova compute for management of the virtual machine environment" -# recipe "openstack-compute::identity_registration", "Registers the API and EC2 endpoints with Keystone" -# recipe "openstack-compute::network", "Installs nova network service" -# recipe "openstack-compute::nova-cert", "Installs nova-cert service" -recipe "openstack-compute::nova-common", "Builds the basic nova.conf config file with details of the rabbitmq, mysql, glance and keystone servers" -recipe "openstack-compute::nova-setup", "Sets up the nova database on the mysql server, including the initial schema and subsequent creation of the appropriate networks" -#recipe "openstack-compute::scheduler", "Installs nova scheduler service" -#recipe "openstack-compute::vncproxy", "Installs and configures the vncproxy service for console access to VMs" +recipe 'openstack-compute::api-ec2', 'Installs AWS EC2 compatible API' +recipe 'openstack-compute::api-metadata', 'Installs the nova metadata package' +recipe 'openstack-compute::api-os-compute', 'Installs OS API' +recipe 'openstack-compute::client', 'Install nova client packages' +recipe 'openstack-compute::compute', 'nova-compute service' +recipe 'openstack-compute::conductor', 'Installs nova conductor service' +recipe 'openstack-compute::libvirt', 'Installs libvirt, used by nova compute for management of the virtual machine environment' +recipe 'openstack-compute::identity_registration', 'Registers the API and EC2 endpoints with Keystone' +recipe 'openstack-compute::network', 'Installs nova network service' +recipe 'openstack-compute::nova-cert', 'Installs nova-cert service' +recipe 'openstack-compute::nova-common', 'Builds the basic nova.conf config file with details of the rabbitmq, mysql, glance and keystone servers' +recipe 'openstack-compute::nova-setup', 'Sets up the nova database on the mysql server, including the initial schema and subsequent creation of the appropriate networks' +recipe 'openstack-compute::scheduler', 'Installs nova scheduler service' +recipe 'openstack-compute::vncproxy', 'Installs and configures the vncproxy service for console access to VMs' %w{ ubuntu fedora redhat centos suse }.each do |os| supports os end -depends "openstack-common", "~> 0.4.0" -depends "openstack-identity", "~> 7.0.0" -depends "openstack-image", "~> 7.0.0" -depends "openstack-network", "~> 7.0.0" -depends "selinux" -depends "sysctl" -depends "yum" -depends "python" +depends 'openstack-common', '~> 9.4' +depends 'openstack-identity', '~> 9.0' +depends 'openstack-image', '~> 9.0' +depends 'openstack-network', '~> 9.0' +depends 'selinux', '~> 0.7' +depends 'python', '~> 1.4.6' diff --git a/chef/cookbooks/openstack-compute/recipes/api-ec2.rb b/chef/cookbooks/openstack-compute/recipes/api-ec2.rb index c792e5c..ecfadc4 100644 --- a/chef/cookbooks/openstack-compute/recipes/api-ec2.rb +++ b/chef/cookbooks/openstack-compute/recipes/api-ec2.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: api-ec2 @@ -17,71 +18,58 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -platform_options = node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] -directory "/var/lock/nova" do - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +directory '/var/lock/nova' do + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00700 action :create end -package "python-keystone" do +# NOTE(mrodden): required for keystone auth middleware +package 'python-keystoneclient' do action :upgrade end -platform_options["api_ec2_packages"].each do |pkg| +platform_options['api_ec2_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end -service "nova-api-ec2" do - service_name platform_options["api_ec2_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") +service 'nova-api-ec2' do + service_name platform_options['api_ec2_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') action :enable end -identity_endpoint = endpoint "identity-api" -identity_admin_endpoint = endpoint "identity-admin" -service_tenant_name = node['openstack']['identity']['compute']['tenant'] -service_user = node['openstack']['identity']['compute']['username'] -service_pass = service_password node['openstack']['identity']['compute']['password'] +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' +service_pass = get_password 'service', 'openstack-compute' -#TODO(jaypipes): Move this logic and stuff into the openstack-common -# library cookbook. -auth_uri = identity_endpoint.to_s -if node["openstack"]["compute"]["api"]["auth"]["version"] != "v2.0" - # The auth_uri should contain /v2.0 in most cases, but if the - # auth_version is v3.0, we leave it off. This is only necessary - # for environments that need to support V3 non-default-domain - # tokens, which is really the only reason to set version to - # something other than v2.0 (the default) - auth_uri = auth_uri.gsub('/v2.0', '') -end +auth_uri = auth_uri_transform identity_endpoint.to_s, node['openstack']['compute']['api']['auth']['version'] -template "/etc/nova/api-paste.ini" do - source "api-paste.ini.erb" - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +template '/etc/nova/api-paste.ini' do + source 'api-paste.ini.erb' + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00644 variables( - :auth_uri => auth_uri, - :identity_admin_endpoint => identity_admin_endpoint, - :service_tenant_name => service_tenant_name, - :service_user => service_user, - :service_pass => service_pass + auth_uri: auth_uri, + identity_admin_endpoint: identity_admin_endpoint, + service_pass: service_pass ) - notifies :restart, "service[nova-api-ec2]" + notifies :restart, 'service[nova-api-ec2]' end diff --git a/chef/cookbooks/openstack-compute/recipes/api-metadata.rb b/chef/cookbooks/openstack-compute/recipes/api-metadata.rb index 7733f22..1ccada3 100644 --- a/chef/cookbooks/openstack-compute/recipes/api-metadata.rb +++ b/chef/cookbooks/openstack-compute/recipes/api-metadata.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: api-metadata @@ -18,73 +19,60 @@ # limitations under the License. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -platform_options = node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] -directory "/var/lock/nova" do - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +directory '/var/lock/nova' do + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00700 action :create end -package "python-keystone" do +# NOTE(mrodden): required for keystone auth middleware +package 'python-keystoneclient' do action :upgrade end -platform_options["compute_api_metadata_packages"].each do |pkg| +platform_options['compute_api_metadata_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end -service "nova-api-metadata" do - service_name platform_options["compute_api_metadata_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") +service 'nova-api-metadata' do + service_name platform_options['compute_api_metadata_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') - action :enable + action [:enable, :start] end -identity_endpoint = endpoint "identity-api" -identity_admin_endpoint = endpoint "identity-admin" -service_tenant_name = node['openstack']['identity']['compute']['tenant'] -service_user = node['openstack']['identity']['compute']['username'] -service_pass = service_password node['openstack']['identity']['compute']['password'] +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' +service_pass = get_password 'service', 'openstack-compute' -#TODO(jaypipes): Move this logic and stuff into the openstack-common -# library cookbook. -auth_uri = identity_endpoint.to_s -if node["openstack"]["compute"]["api"]["auth"]["version"] != "v2.0" - # The auth_uri should contain /v2.0 in most cases, but if the - # auth_version is v3.0, we leave it off. This is only necessary - # for environments that need to support V3 non-default-domain - # tokens, which is really the only reason to set version to - # something other than v2.0 (the default) - auth_uri = auth_uri.gsub('/v2.0', '') -end +auth_uri = auth_uri_transform identity_endpoint.to_s, node['openstack']['compute']['api']['auth']['version'] -template "/etc/nova/api-paste.ini" do - source "api-paste.ini.erb" - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +template '/etc/nova/api-paste.ini' do + source 'api-paste.ini.erb' + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00644 variables( - :auth_uri => auth_uri, - :identity_admin_endpoint => identity_admin_endpoint, - :service_tenant_name => service_tenant_name, - :service_user => service_user, - :service_pass => service_pass + auth_uri: auth_uri, + identity_admin_endpoint: identity_admin_endpoint, + service_pass: service_pass ) - notifies :restart, "service[nova-api-metadata]" + notifies :restart, 'service[nova-api-metadata]' end diff --git a/chef/cookbooks/openstack-compute/recipes/api-os-compute.rb b/chef/cookbooks/openstack-compute/recipes/api-os-compute.rb index abc1338..be71369 100644 --- a/chef/cookbooks/openstack-compute/recipes/api-os-compute.rb +++ b/chef/cookbooks/openstack-compute/recipes/api-os-compute.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: api-os-compute @@ -17,75 +18,62 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -platform_options = node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] -directory "/var/lock/nova" do - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +directory '/var/lock/nova' do + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00700 end -directory ::File.dirname(node["openstack"]["compute"]["api"]["auth"]["cache_dir"]) do - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +directory ::File.dirname(node['openstack']['compute']['api']['auth']['cache_dir']) do + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00700 end -package "python-keystone" do +# NOTE(mrodden): required for keystone auth middleware +package 'python-keystoneclient' do action :upgrade end -platform_options["api_os_compute_packages"].each do |pkg| +platform_options['api_os_compute_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end -service "nova-api-os-compute" do - service_name platform_options["api_os_compute_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") +service 'nova-api-os-compute' do + service_name platform_options['api_os_compute_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') action [:enable, :start] end -identity_endpoint = endpoint "identity-api" -identity_admin_endpoint = endpoint "identity-admin" -service_tenant_name = node['openstack']['identity']['compute']['tenant'] -service_user = node['openstack']['identity']['compute']['username'] -service_pass = service_password node['openstack']['identity']['compute']['password'] +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' +service_pass = get_password 'service', 'openstack-compute' -#TODO(jaypipes): Move this logic and stuff into the openstack-common -# library cookbook. -auth_uri = identity_endpoint.to_s -if node["openstack"]["compute"]["api"]["auth"]["version"] != "v2.0" - # The auth_uri should contain /v2.0 in most cases, but if the - # auth_version is v3.0, we leave it off. This is only necessary - # for environments that need to support V3 non-default-domain - # tokens, which is really the only reason to set version to - # something other than v2.0 (the default) - auth_uri = auth_uri.gsub('/v2.0', '') -end +auth_uri = auth_uri_transform identity_endpoint.to_s, node['openstack']['compute']['api']['auth']['version'] -template "/etc/nova/api-paste.ini" do - source "api-paste.ini.erb" - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +template '/etc/nova/api-paste.ini' do + source 'api-paste.ini.erb' + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00644 variables( - :auth_uri => auth_uri, - :identity_admin_endpoint => identity_admin_endpoint, - :service_tenant_name => service_tenant_name, - :service_user => service_user, - :service_pass => service_pass + auth_uri: auth_uri, + identity_admin_endpoint: identity_admin_endpoint, + service_pass: service_pass ) - notifies :restart, "service[nova-api-os-compute]" + notifies :restart, 'service[nova-api-os-compute]' end diff --git a/chef/cookbooks/openstack-compute/recipes/client.rb b/chef/cookbooks/openstack-compute/recipes/client.rb new file mode 100644 index 0000000..b047e9e --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/client.rb @@ -0,0 +1,32 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-compute +# Recipe:: client +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +platform_options = node['openstack']['compute']['platform'] +platform_options['compute_client_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end diff --git a/chef/cookbooks/openstack-compute/recipes/compute-config-ceph.rb b/chef/cookbooks/openstack-compute/recipes/compute-config-ceph.rb new file mode 100644 index 0000000..4d48abf --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/compute-config-ceph.rb @@ -0,0 +1,108 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-compute +# Recipe:: libvirt_rbd +# +# Copyright 2014, x-ion GmbH +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +if node['openstack']['block-storage']['volume']['driver'] == 'cinder.volume.drivers.rbd.RBDDriver' + + include_recipe 'ceph::_common' + include_recipe 'ceph::mon_install' + include_recipe 'ceph::conf' + cluster = 'ceph' + + platform_options = node['openstack']['compute']['platform'] + + platform_options['libvirt_ceph_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade + end + end + + unless node['local_repo'].nil? or node['local_repo'].empty? + node.override['ceph']['rhel']['extras']['repository'] = "#{node['local_repo']}/compass_repo" + end + + execute "rpm -Uvh --force #{node['ceph']['rhel']['extras']['repository']}/qemu-kvm-0.12.1.2-2.415.el6.3ceph.x86_64.rpm #{node['ceph']['rhel']['extras']['repository']}/qemu-img-0.12.1.2-2.415.el6.3ceph.x86_64.rpm" do + not_if "rpm -qa | grep qemu | grep ceph" + end + + secret_uuid = node['openstack']['block-storage']['rbd_secret_uuid'] + rbd_user = node['openstack']['compute']['libvirt']['rbd']['rbd_user'] + + if mon_nodes.empty? + rbd_key = "" + elsif !mon_master['ceph'].has_key?('cinder-secret') + rbd_key = "" + else + rbd_key = mon_master['ceph']['cinder-secret'] + end + + template "/etc/ceph/ceph.client.#{rbd_user}.keyring" do + source 'ceph.client.keyring.erb' + cookbook 'openstack-common' + owner node['openstack']['block-storage']['user'] + group node['openstack']['block-storage']['group'] + mode '0644' + variables( + name: rbd_user, + key: rbd_key + ) + end + + require 'securerandom' + filename = SecureRandom.hex + + template "/tmp/#{filename}.xml" do + source 'secret.xml.erb' + user 'root' + group 'root' + mode '700' + variables( + uuid: secret_uuid, + client_name: node['openstack']['compute']['libvirt']['rbd']['rbd_user'] + ) + not_if "virsh secret-list | grep #{secret_uuid}" + end + + execute "virsh secret-define --file /tmp/#{filename}.xml" do + not_if "virsh secret-list | grep #{secret_uuid}" + end + + # this will update the key if necessary + execute 'set libvirt secret' do + command "virsh secret-set-value --secret #{secret_uuid} --base64 #{rbd_key}" + notifies :restart, 'service[nova-compute-ceph]', :immediately + end + + file "/tmp/#{filename}.xml" do + action :delete + end + + service 'nova-compute-ceph' do + service_name platform_options['compute_compute_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') + + action :restart + end +end diff --git a/chef/cookbooks/openstack-compute/recipes/compute.rb b/chef/cookbooks/openstack-compute/recipes/compute.rb index a5973f1..fbe5b00 100644 --- a/chef/cookbooks/openstack-compute/recipes/compute.rb +++ b/chef/cookbooks/openstack-compute/recipes/compute.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: compute @@ -18,40 +19,32 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -include_recipe "openstack-compute::nova-common" -# include_recipe "openstack-compute::api-metadata" -unless node.run_list.include? "openstack-network::server" - include_recipe "openstack-compute::network" +include_recipe 'openstack-compute::nova-common' +if node['openstack']['compute']['enabled_apis'].include?('metadata') + include_recipe 'openstack-compute::api-metadata' end +include_recipe 'openstack-compute::network' -platform_options = node["openstack"]["compute"]["platform"] -# Note(maoy): Make sure compute_compute_packages is not a node object. -# so that this is compatible with chef 11 when being changed later. -compute_compute_packages = Array.new(platform_options["compute_compute_packages"]) +platform_options = node['openstack']['compute']['platform'] -if platform?(%w(ubuntu)) - if node["openstack"]["compute"]["libvirt"]["virt_type"] == "kvm" - compute_compute_packages << "nova-compute-kvm" - elsif node["openstack"]["compute"]["libvirt"]["virt_type"] == "qemu" - compute_compute_packages << "nova-compute-qemu" - end -end - -if platform?(%w(centos, redhat, fedora)) - if node["openstack"]["compute"]["libvirt"]["virt_type"] == "qemu" - compute_compute_packages << "qemu-kvm" - elsif node["openstack"]["compute"]["libvirt"]["virt_type"] == "kvm" - compute_compute_packages << "qemu-kvm" - end -end - -compute_compute_packages.each do |pkg| +platform_options['compute_compute_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] + + action :upgrade + end +end + +virt_type = node['openstack']['compute']['libvirt']['virt_type'] + +platform_options["#{virt_type}_compute_packages"].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade end end @@ -59,34 +52,34 @@ end # Installing nfs client packages because in grizzly, cinder nfs is supported # Never had to install iscsi packages because nova-compute package depends it # So volume-attach 'just worked' before - alop -platform_options["nfs_packages"].each do |pkg| +platform_options['nfs_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end -directory "/var/lock/nova" do - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] - mode 00700 - action :create -end - -cookbook_file "/etc/nova/nova-compute.conf" do - source "nova-compute.conf" +cookbook_file '/etc/nova/nova-compute.conf' do + source 'nova-compute.conf' mode 00644 - retries 5 + action :create end -service "nova-compute" do - service_name platform_options["compute_compute_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") +service 'nova-compute' do + service_name platform_options['compute_compute_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') - action [ :enable, :restart ] + action [:enable, :start] end -include_recipe "openstack-compute::libvirt" +directory node['openstack']['compute']['instances_path'] do + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] + mode 00755 + recursive true +end + +include_recipe 'openstack-compute::libvirt' diff --git a/chef/cookbooks/openstack-compute/recipes/conductor.rb b/chef/cookbooks/openstack-compute/recipes/conductor.rb index 2a208bc..eea6fd2 100644 --- a/chef/cookbooks/openstack-compute/recipes/conductor.rb +++ b/chef/cookbooks/openstack-compute/recipes/conductor.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: nova # Recipe:: conductor @@ -18,20 +19,20 @@ # limitations under the License. # -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -platform_options = node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] -platform_options["compute_conductor_packages"].each do |pkg| +platform_options['compute_conductor_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end -service "nova-conductor" do - service_name platform_options["compute_conductor_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") - action [ :enable, :restart ] +service 'nova-conductor' do + service_name platform_options['compute_conductor_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') + action [:enable, :start] end diff --git a/chef/cookbooks/openstack-compute/recipes/default.rb b/chef/cookbooks/openstack-compute/recipes/default.rb index 6fa23bb..6e8d406 100644 --- a/chef/cookbooks/openstack-compute/recipes/default.rb +++ b/chef/cookbooks/openstack-compute/recipes/default.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: default diff --git a/chef/cookbooks/openstack-compute/recipes/identity_registration.rb b/chef/cookbooks/openstack-compute/recipes/identity_registration.rb index 64edf82..c8526b2 100644 --- a/chef/cookbooks/openstack-compute/recipes/identity_registration.rb +++ b/chef/cookbooks/openstack-compute/recipes/identity_registration.rb @@ -1,8 +1,10 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: identity_registration # # Copyright 2013, AT&T +# Copyright 2013, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,36 +19,36 @@ # limitations under the License. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -identity_admin_endpoint = endpoint "identity-admin" -bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +identity_admin_endpoint = endpoint 'identity-admin' +bootstrap_token = get_secret 'openstack_identity_bootstrap_token' auth_uri = ::URI.decode identity_admin_endpoint.to_s -service_pass = service_password node['openstack']['identity']['compute']['password'] -service_user = node['openstack']['identity']['compute']['username'] -service_role = node['openstack']['identity']['compute']['role'] -service_tenant_name = node['openstack']['identity']['compute']['tenant'] -nova_api_endpoint = endpoint "compute-api" -ec2_admin_endpoint = endpoint "compute-ec2-admin" -ec2_public_endpoint = endpoint "compute-ec2-api" -region = node["openstack"]["compute"]["region"] +service_pass = get_password 'service', 'openstack-compute' +service_user = node['openstack']['compute']['service_user'] +service_role = node['openstack']['compute']['service_role'] +service_tenant_name = node['openstack']['compute']['service_tenant_name'] +nova_api_endpoint = endpoint 'compute-api' +ec2_admin_endpoint = endpoint 'compute-ec2-admin' +ec2_public_endpoint = endpoint 'compute-ec2-api' +region = node['openstack']['compute']['region'] # Register Service Tenant -openstack_identity_register "Register Service Tenant" do +openstack_identity_register 'Register Service Tenant' do auth_uri auth_uri bootstrap_token bootstrap_token tenant_name service_tenant_name - tenant_description "Service Tenant" + tenant_description 'Service Tenant' action :create_tenant end # Register Service User -openstack_identity_register "Register Service User" do +openstack_identity_register 'Register Service User' do auth_uri auth_uri bootstrap_token bootstrap_token tenant_name service_tenant_name @@ -68,21 +70,21 @@ openstack_identity_register "Grant 'admin' Role to Service User for Service Tena end # Register Compute Service -openstack_identity_register "Register Compute Service" do +openstack_identity_register 'Register Compute Service' do auth_uri auth_uri bootstrap_token bootstrap_token - service_name "nova" - service_type "compute" - service_description "Nova Compute Service" + service_name 'nova' + service_type 'compute' + service_description 'Nova Compute Service' action :create_service end # Register Compute Endpoint -openstack_identity_register "Register Compute Endpoint" do +openstack_identity_register 'Register Compute Endpoint' do auth_uri auth_uri bootstrap_token bootstrap_token - service_type "compute" + service_type 'compute' endpoint_region region endpoint_adminurl ::URI.decode nova_api_endpoint.to_s endpoint_internalurl ::URI.decode nova_api_endpoint.to_s @@ -91,26 +93,28 @@ openstack_identity_register "Register Compute Endpoint" do action :create_endpoint end -# Register EC2 Service -openstack_identity_register "Register EC2 Service" do - auth_uri auth_uri - bootstrap_token bootstrap_token - service_name "ec2" - service_type "ec2" - service_description "EC2 Compatibility Layer" +if node['openstack']['compute']['enabled_apis'].include?('ec2') + # Register EC2 Service + openstack_identity_register 'Register EC2 Service' do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name 'ec2' + service_type 'ec2' + service_description 'EC2 Compatibility Layer' - action :create_service -end - -# Register EC2 Endpoint -openstack_identity_register "Register EC2 Endpoint" do - auth_uri auth_uri - bootstrap_token bootstrap_token - service_type "ec2" - endpoint_region region - endpoint_adminurl ::URI.decode ec2_admin_endpoint.to_s - endpoint_internalurl ::URI.decode ec2_public_endpoint.to_s - endpoint_publicurl ::URI.decode ec2_public_endpoint.to_s - - action :create_endpoint + action :create_service + end + + # Register EC2 Endpoint + openstack_identity_register 'Register EC2 Endpoint' do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type 'ec2' + endpoint_region region + endpoint_adminurl ::URI.decode ec2_admin_endpoint.to_s + endpoint_internalurl ::URI.decode ec2_public_endpoint.to_s + endpoint_publicurl ::URI.decode ec2_public_endpoint.to_s + + action :create_endpoint + end end diff --git a/chef/cookbooks/openstack-compute/recipes/libvirt.rb b/chef/cookbooks/openstack-compute/recipes/libvirt.rb index 86d7361..e3dda04 100644 --- a/chef/cookbooks/openstack-compute/recipes/libvirt.rb +++ b/chef/cookbooks/openstack-compute/recipes/libvirt.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: libvirt @@ -18,15 +19,18 @@ # limitations under the License. # -platform_options = node["openstack"]["compute"]["platform"] +require 'mixlib/shellout' -platform_options["libvirt_packages"].each do |pkg| +platform_options = node['openstack']['compute']['platform'] + +platform_options['libvirt_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end -def set_grub_default_kernel(flavor='default') +def update_grub_default_kernel(flavor = 'default') # rubocop:disable MethodLength default_boot, current_default = 0, nil # parse menu.lst, to find boot index for selected flavor @@ -50,156 +54,176 @@ def set_grub_default_kernel(flavor='default') # change default option for /boot/grub/menu.lst unless current_default.eql?(default_boot) - ::Chef::Log.info("Changed grub default to #{default_boot}") - %x[sed -i -e "s;^default.*;default #{default_boot};" /boot/grub/menu.lst] + ::Chef::Log.info('Changed grub default to #{default_boot}') + Mixlib::ShellOut.new('sed -i -e \'s;^default.*;default #{default_boot};\' /boot/grub/menu.lst').run_command end end -def set_grub2_default_kernel(flavor='default') +def update_grub2_default_kernel(flavor = 'default') boot_entry = "'openSUSE GNU/Linux, with Xen hypervisor'" - if system("grub2-set-default #{boot_entry}") + begin + Mixlib::ShellOut.new("grub2-set-default #{boot_entry}").run_command.error! ::Chef::Log.info("Changed grub2 default to #{boot_entry}") - else + rescue Mixlib::ShellOut::ShellCommandFailed => e ::Chef::Application.fatal!( - "Unable to change grub2 default to #{boot_entry}") + "Unable to change grub2 default to #{boot_entry} +#{e.message}") end end -def set_boot_kernel_and_trigger_reboot(flavor='default') +def update_boot_kernel_and_trigger_reboot(flavor = 'default') # rubocop:disable MethodLength # only default and xen flavor is supported by this helper right now - if File.exists?("/boot/grub/menu.lst") - set_grub_default_kernel(flavor) - elsif File.exists?("/etc/default/grub") - set_grub2_default_kernel(flavor) + if File.exists?('/boot/grub/menu.lst') + update_grub_default_kernel(flavor) + elsif File.exists?('/etc/default/grub') + update_grub2_default_kernel(flavor) else ::Chef::Application.fatal!( - "Unknown bootloader. Could not change boot kernel.") + 'Unknown bootloader. Could not change boot kernel.') end # trigger reboot through reboot_handler, if kernel-$flavor is not yet # running - unless %x[uname -r].include?(flavor) - node.run_state["reboot"] = true + unless Mixlib::ShellOut.new('uname -r').run_command.stdout.include?(flavor) + node.run_state['reboot'] = true end end # on suse nova-compute don't depends on any virtualization mechanism -case node["platform"] -when "suse" - case node["openstack"]["compute"]["libvirt"]["virt_type"] - when "kvm" - node["openstack"]["compute"]["platform"]["kvm_packages"].each do |pkg| +case node['platform_family'] +when 'suse' + case node['openstack']['compute']['libvirt']['virt_type'] + when 'kvm' + node['openstack']['compute']['platform']['kvm_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end - execute "loading kvm modules" do - command "grep -q vmx /proc/cpuinfo && /sbin/modprobe kvm-intel; grep -q svm /proc/cpuinfo && /sbin/modprobe kvm-amd; /sbin/modprobe vhost-net" + execute 'loading kvm modules' do + command 'grep -q vmx /proc/cpuinfo && /sbin/modprobe kvm-intel; grep -q svm /proc/cpuinfo && /sbin/modprobe kvm-amd; /sbin/modprobe vhost-net' end # NOTE(saschpe): Allow switching from XEN to KVM: - set_boot_kernel_and_trigger_reboot + update_boot_kernel_and_trigger_reboot - when "xen" - node["openstack"]["compute"]["platform"]["xen_packages"].each do |pkg| + when 'xen' + node['openstack']['compute']['platform']['xen_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end - set_boot_kernel_and_trigger_reboot('xen') + update_boot_kernel_and_trigger_reboot('xen') - when "qemu" - node["openstack"]["compute"]["platform"]["kvm_packages"].each do |pkg| + when 'qemu' + node['openstack']['compute']['platform']['kvm_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end - when "lxc" - node["openstack"]["compute"]["platform"]["lxc_packages"].each do |pkg| + when 'lxc' + node['openstack']['compute']['platform']['lxc_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end - service "boot.cgroup" do + service 'boot.cgroup' do action [:enable, :start] end end end -group node["openstack"]["compute"]["libvirt"]["group"] do +group node['openstack']['compute']['libvirt']['group'] do append true - members [node["openstack"]["compute"]["group"]] + members [node['openstack']['compute']['group']] action :create - only_if { platform? %w{suse fedora redhat centos} } + only_if { platform_family? %w{suse fedora rhel} } end # http://fedoraproject.org/wiki/Getting_started_with_OpenStack_EPEL#Installing_within_a_VM # ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-system-x86_64 -link "/usr/bin/qemu-system-x86_64" do - to "/usr/libexec/qemu-kvm" +link '/usr/bin/qemu-system-x86_64' do + to '/usr/libexec/qemu-kvm' - only_if { platform? %w{fedora redhat centos} } + only_if { platform_family? %w{fedora rhel} } end -if node['platform_family'] != "rhel" - service "dbus" do - action [:enable, :start] - end -end - -service "libvirt-bin" do - service_name platform_options["libvirt_service"] - supports :status => true, :restart => true +service 'dbus' do + service_name platform_options['dbus_service'] + supports status: true, restart: true action [:enable, :start] end -execute "Disabling default libvirt network" do - command "virsh net-autostart default --disable" +service 'libvirt-bin' do + service_name platform_options['libvirt_service'] + supports status: true, restart: true - only_if "virsh net-list | grep -q default" + action [:enable, :start] end -execute "Deleting default libvirt network" do - command "virsh net-destroy default" +execute 'Disabling default libvirt network' do + command 'virsh net-autostart default --disable' - only_if "virsh net-list | grep -q default" + only_if 'virsh net-list | grep -q default' +end + +execute 'Deleting default libvirt network' do + command 'virsh net-destroy default' + + only_if 'virsh net-list | grep -q default' +end + +# Host uuid +if node['openstack']['compute']['libvirt']['host_uuid'].nil? + ruby_block "set host uuid" do + block do + new_uuid = `uuidgen`.delete("\n") + node.set['openstack']['compute']['libvirt']['host_uuid'] = new_uuid + end + end end # TODO(breu): this section needs to be rewritten to support key privisioning -template "/etc/libvirt/libvirtd.conf" do - source "libvirtd.conf.erb" - owner "root" - group "root" +template '/etc/libvirt/libvirtd.conf' do + source 'libvirtd.conf.erb' + owner 'root' + group 'root' mode 00644 variables( - :auth_tcp => node["openstack"]["compute"]["libvirt"]["auth_tcp"], - :libvirt_group => node["openstack"]["compute"]["libvirt"]["group"] + auth_tcp: node['openstack']['compute']['libvirt']['auth_tcp'], + libvirt_group: node['openstack']['compute']['libvirt']['group'] ) - notifies :restart, "service[libvirt-bin]", :immediately - not_if { platform? "suse" } + notifies :restart, 'service[libvirt-bin]', :immediately + not_if { platform_family? 'suse' } end -template "/etc/default/libvirt-bin" do - source "libvirt-bin.erb" - owner "root" - group "root" +template '/etc/default/libvirt-bin' do + source 'libvirt-bin.erb' + owner 'root' + group 'root' mode 00644 - notifies :restart, "service[libvirt-bin]", :immediately + notifies :restart, 'service[libvirt-bin]', :immediately - only_if { platform? %w{ubuntu debian} } + only_if { platform_family? %w{debian} } end -template "/etc/sysconfig/libvirtd" do - source "libvirtd.erb" - owner "root" - group "root" +template '/etc/sysconfig/libvirtd' do + source 'libvirtd.erb' + owner 'root' + group 'root' mode 00644 - notifies :restart, "service[libvirt-bin]", :immediately + notifies :restart, 'service[libvirt-bin]', :immediately - only_if { platform? %w{fedora redhat centos} } + only_if { platform_family? %w{fedora rhel} } end + +volume_backend = node['openstack']['compute']['libvirt']['volume_backend'] +include_recipe "openstack-compute::libvirt_#{volume_backend}" unless volume_backend.nil? diff --git a/chef/cookbooks/openstack-compute/recipes/libvirt_rbd.rb b/chef/cookbooks/openstack-compute/recipes/libvirt_rbd.rb new file mode 100644 index 0000000..7474853 --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/libvirt_rbd.rb @@ -0,0 +1,68 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-compute +# Recipe:: libvirt_rbd +# +# Copyright 2014, x-ion GmbH +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +include_recipe 'openstack-common::ceph_client' + +platform_options = node['openstack']['compute']['platform'] + +platform_options['libvirt_ceph_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade + end +end + +# TODO(srenatus) there might be multiple secrets, cinder will tell nova-compute +# which one should be used for each single volume mount request +Chef::Log.info("rbd_secret_name: #{node['openstack']['compute']['libvirt']['rbd']['rbd_secret_name']}") +secret_uuid = get_secret node['openstack']['compute']['libvirt']['rbd']['rbd_secret_name'] +ceph_key = get_password 'service', 'rbd_block_storage' + +require 'securerandom' +filename = SecureRandom.hex + +template "/tmp/#{filename}.xml" do + source 'secret.xml.erb' + user 'root' + group 'root' + mode '700' + variables( + uuid: secret_uuid, + client_name: node['openstack']['compute']['libvirt']['rbd']['rbd_user'] + ) + not_if "virsh secret-list | grep #{secret_uuid}" +end + +execute "virsh secret-define --file /tmp/#{filename}.xml" do + not_if "virsh secret-list | grep #{secret_uuid}" +end + +# this will update the key if necessary +execute "virsh secret-set-value --secret #{secret_uuid} '#{ceph_key}'" do + not_if "virsh secret-get-value #{secret_uuid} | grep '#{ceph_key}'" +end + +file "/tmp/#{filename}.xml" do + action :delete +end diff --git a/chef/cookbooks/openstack-compute/recipes/network.rb b/chef/cookbooks/openstack-compute/recipes/network.rb index b94822b..44a411b 100644 --- a/chef/cookbooks/openstack-compute/recipes/network.rb +++ b/chef/cookbooks/openstack-compute/recipes/network.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: network @@ -18,34 +19,34 @@ # limitations under the License. # -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -platform_options = node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] # the only type of network we process here is nova, otherwise for -# quantum, the network will be setup by the inclusion of +# neutron, the network will be setup by the inclusion of # openstack-network recipes -if node["openstack"]["compute"]["network"]["service_type"] == "nova" +if node['openstack']['compute']['network']['service_type'] == 'nova' - platform_options["compute_network_packages"].each do |pkg| + platform_options['compute_network_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end - service "nova-network" do - service_name platform_options["compute_network_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") + service 'nova-network' do + service_name platform_options['compute_network_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') action :enable end else - node["openstack"]["compute"]["network"]["plugins"].each do |plugin| + node['openstack']['compute']['network']['plugins'].each do |plugin| include_recipe "openstack-network::#{plugin}" end diff --git a/chef/cookbooks/openstack-compute/recipes/nova-cert.rb b/chef/cookbooks/openstack-compute/recipes/nova-cert.rb index 6aec24d..9a33e96 100644 --- a/chef/cookbooks/openstack-compute/recipes/nova-cert.rb +++ b/chef/cookbooks/openstack-compute/recipes/nova-cert.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: nova-cert @@ -17,22 +18,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -platform_options=node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] -platform_options["compute_cert_packages"].each do |pkg| +platform_options['compute_cert_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end -service "nova-cert" do - service_name platform_options["compute_cert_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") +service 'nova-cert' do + service_name platform_options['compute_cert_service'] + supports statusi: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') - action [ :enable, :restart ] + action :enable end diff --git a/chef/cookbooks/openstack-compute/recipes/nova-common.rb b/chef/cookbooks/openstack-compute/recipes/nova-common.rb index f8fc49e..d8f5479 100644 --- a/chef/cookbooks/openstack-compute/recipes/nova-common.rb +++ b/chef/cookbooks/openstack-compute/recipes/nova-common.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: nova-common @@ -18,91 +19,87 @@ # limitations under the License. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -if platform?(%w(fedora redhat centos)) # :pragma-foodcritic: ~FC024 - won't fix this - include_recipe "yum::epel" -end -if node["openstack"]["compute"]["syslog"]["use"] - include_recipe "openstack-common::logging" -end +include_recipe 'openstack-common::logging' if node['openstack']['compute']['syslog']['use'] -platform_options = node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] -platform_options["common_packages"].each do |pkg| +platform_options['common_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] + action :upgrade + end +end +db_type = node['openstack']['db']['compute']['service_type'] +platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + options platform_options['package_overrides'] action :upgrade end end # required to run more than one consoleauth process -platform_options["memcache_python_packages"].each do |pkg| +platform_options['memcache_python_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end -directory "/etc/nova" do - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] - mode 00700 - - action :create -end - -directory "/etc/nova/rootwrap.d" do - # Must be root! - owner "root" - group "root" - mode 00700 - +directory '/etc/nova' do + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] + mode 00750 action :create end db_user = node['openstack']['db']['compute']['username'] -db_pass = db_password node['openstack']['db']['compute']['password'] -sql_connection = db_uri("compute", db_user, db_pass) +db_pass = get_password 'db', 'nova' +sql_connection = db_uri('compute', db_user, db_pass) -if node["openstack"]["compute"]["rabbit"]["ha"] - rabbit_hosts = node['openstack']['mq']['bind_address'] -end -rabbit_pass = user_password node['openstack']['mq']['password'] +mq_service_type = node['openstack']['mq']['compute']['service_type'] -identity_service_role = node["openstack"]["compute"]["identity_service_chef_role"] - -if node.run_list.roles.include?(identity_service_role) - # if role is on this node, just return the node hash - keystone = node -else - keystone = node - # otherwise go searching - # keystone = search_for(identity_service_role).first +if mq_service_type == 'rabbitmq' + node['openstack']['mq']['compute']['rabbit']['ha'] && (rabbit_hosts = rabbit_servers) + mq_password = get_password('user', \ + node['openstack']['mq']['user'], \ + node['openstack']['mq']['password']) +elsif mq_service_type == 'qpid' + mq_password = get_password('user', \ + node['openstack']['mq']['compute']['qpid']['username']) end -ksadmin_tenant_name = keystone["openstack"]["identity"]["admin_tenant_name"] -ksadmin_user = keystone["openstack"]["identity"]["admin_user"] -ksadmin_pass = user_password node['openstack']['identity']['admin_password'] - -memcache_servers = memcached_servers.join "," +if node['openstack']['compute']['consoleauth']['token']['backend'].eql?('memcache') + memcache_servers = memcached_servers('os-ops-caching').join ',' + # number of seconds to wait before sockets timeout when the memcached server is down + # the default number is 3, here is going to set it as 0.1 + ruby_block "Set memcache socket timeout" do + block do + `sed -i "s/_SOCKET_TIMEOUT = 3/_SOCKET_TIMEOUT = 0.1/g" /usr/lib/python[0-9].[0-9]/site-packages/memcache.py` + end + end +end # find the node attribute endpoint settings for the server holding a given role -identity_endpoint = endpoint "identity-api" -xvpvnc_endpoint = endpoint "compute-xvpvnc" || {} -novnc_endpoint = endpoint "compute-novnc" || {} -compute_api_endpoint = endpoint "compute-api" || {} -ec2_public_endpoint = endpoint "compute-ec2-api" || {} -network_endpoint = endpoint "network-api" || {} -image_endpoint = endpoint "image-api" +identity_endpoint = endpoint 'identity-api' +xvpvnc_endpoint = endpoint 'compute-xvpvnc' || {} +xvpvnc_bind = endpoint 'compute-xvpvnc-bind' || {} +novnc_endpoint = endpoint 'compute-novnc' || {} +novnc_bind = endpoint 'compute-novnc-bind' || {} +vnc_bind = endpoint 'compute-vnc-bind' || {} +compute_api_bind = endpoint 'compute-api-bind' || {} +compute_api_endpoint = endpoint 'compute-api' || {} +ec2_api_bind = endpoint 'compute-ec2-api-bind' || {} +ec2_public_endpoint = endpoint 'compute-ec2-api' || {} +network_endpoint = endpoint 'network-api' || {} +image_endpoint = endpoint 'image-api' -Chef::Log.debug("openstack-compute::nova-common:keystone|#{keystone}") -Chef::Log.debug("openstack-compute::nova-common:ksadmin_user|#{ksadmin_user}") -Chef::Log.debug("openstack-compute::nova-common:ksadmin_tenant_name|#{ksadmin_tenant_name}") Chef::Log.debug("openstack-compute::nova-common:identity_endpoint|#{identity_endpoint.to_s}") Chef::Log.debug("openstack-compute::nova-common:xvpvnc_endpoint|#{xvpvnc_endpoint.to_s}") Chef::Log.debug("openstack-compute::nova-common:novnc_endpoint|#{novnc_endpoint.to_s}") @@ -111,107 +108,70 @@ Chef::Log.debug("openstack-compute::nova-common:ec2_public_endpoint|#{ec2_public Chef::Log.debug("openstack-compute::nova-common:network_endpoint|#{network_endpoint.to_s}") Chef::Log.debug("openstack-compute::nova-common:image_endpoint|#{image_endpoint.to_s}") -vnc_bind_ip = address_for node['openstack']['networking']['control']['interface'] -xvpvnc_proxy_ip = address_for node["openstack"]["compute"]["xvpvnc_proxy"]["bind_interface"] -novnc_proxy_ip = address_for node["openstack"]["compute"]["novnc_proxy"]["bind_interface"] - -if node["openstack"]["compute"]["network"]["service_type"] == "quantum" - quantum_admin_password = service_password node['openstack']['identity']['network']['password'] - quantum_metadata_proxy_shared_secret = secret "secrets", node["openstack"]["network"]["metadata"]["secret_name"] -else - quantum_admin_password = nil - quantum_metadata_proxy_shared_secret = nil +if node['openstack']['compute']['network']['service_type'] == 'neutron' + neutron_admin_password = get_password 'service', 'openstack-network' + neutron_metadata_proxy_shared_secret = get_secret 'neutron_metadata_secret' end -# virtualization: '0' hardware support kvm, '1' hardware doesn't -virtualization = `egrep '(vmx|svm)' --color=always /proc/cpuinfo >/dev/null;echo $?`.delete("\n") -if virtualization.eql?("1") - node.override["openstack"]["compute"]["libvirt"]["virt_type"] = "qemu" -else - node.override["openstack"]["compute"]["libvirt"]["virt_type"] = "kvm" +if node['openstack']['compute']['libvirt']['images_type'] == 'rbd' + #rbd_secret_uuid = get_secret node['openstack']['compute']['libvirt']['rbd']['rbd_secret_name'] end -template "/etc/nova/nova.conf" do - source "nova.conf.erb" - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +vmware_host_pass = get_secret node['openstack']['compute']['vmware']['secret_name'] + +# qemu: '0' hardware support kvm; '1' server doesn't support kvm, qemu needed +qemu = `egrep '(vmx|svm)' /proc/cpuinfo >/dev/null;echo $?`.delete("\n") +if qemu.eql?("1") && node['openstack']['compute']['libvirt']['virt_type'].eql?('kvm') + node.override['openstack']['compute']['libvirt']['virt_type'] = 'qemu' +end + +template '/etc/nova/nova.conf' do + source 'nova.conf.erb' + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00644 variables( - :sql_connection => sql_connection, - :novncproxy_base_url => novnc_endpoint.to_s, - :xvpvncproxy_base_url => xvpvnc_endpoint.to_s, - :xvpvncproxy_bind_host => xvpvnc_proxy_ip, - :novncproxy_bind_host => novnc_proxy_ip, - :vncserver_listen => vnc_bind_ip, - :vncserver_proxyclient_address => vnc_bind_ip, - :memcache_servers => memcache_servers, - :rabbit_password => rabbit_pass, - :rabbit_hosts => rabbit_hosts, - :identity_endpoint => identity_endpoint, + sql_connection: sql_connection, + novncproxy_base_url: novnc_endpoint.to_s, + xvpvncproxy_base_url: xvpvnc_endpoint.to_s, + xvpvncproxy_bind_host: xvpvnc_bind.host, + xvpvncproxy_bind_port: xvpvnc_bind.port, + novncproxy_bind_host: novnc_bind.host, + novncproxy_bind_port: novnc_bind.port, + vncserver_listen: node['openstack']['compute']['vnc']['vncserver_listen'], + vncserver_proxyclient_address: vnc_bind.host, + memcache_servers: memcache_servers, + mq_service_type: mq_service_type, + mq_password: mq_password, + rabbit_hosts: rabbit_hosts, + identity_endpoint: identity_endpoint, # TODO(jaypipes): No support here for >1 image API servers # with the glance_api_servers configuration option... - :glance_api_ipaddress => image_endpoint.host, - :glance_api_port => image_endpoint.port, - :iscsi_helper => platform_options["iscsi_helper"], - :scheduler_default_filters => node["openstack"]["compute"]["scheduler"]["default_filters"].join(","), - :osapi_compute_link_prefix => compute_api_endpoint.to_s, - :network_endpoint => network_endpoint, - :quantum_admin_password => quantum_admin_password, - :quantum_metadata_proxy_shared_secret => quantum_metadata_proxy_shared_secret + glance_api_ipaddress: image_endpoint.host, + glance_api_port: image_endpoint.port, + iscsi_helper: platform_options['iscsi_helper'], + scheduler_default_filters: node['openstack']['compute']['scheduler']['default_filters'].join(','), + osapi_compute_link_prefix: compute_api_endpoint.to_s, + network_endpoint: network_endpoint, + neutron_admin_password: neutron_admin_password, + neutron_metadata_proxy_shared_secret: neutron_metadata_proxy_shared_secret, + compute_api_bind_ip: compute_api_bind.host, + compute_api_bind_port: compute_api_bind.port, + ec2_api_bind_ip: ec2_api_bind.host, + ec2_api_bind_port: ec2_api_bind.port, + #rbd_secret_uuid: rbd_secret_uuid, + vmware_host_pass: vmware_host_pass ) end -template "/etc/nova/rootwrap.conf" do - source "rootwrap.conf.erb" +template '/etc/nova/rootwrap.conf' do + source 'rootwrap.conf.erb' # Must be root! - owner "root" - group "root" + owner 'root' + group 'root' mode 00644 end -template "/etc/nova/rootwrap.d/api-metadata.filters" do - source "rootwrap.d/api-metadata.filters.erb" - # Must be root! - owner "root" - group "root" - mode 00644 -end - -template "/etc/nova/rootwrap.d/compute.filters" do - source "rootwrap.d/compute.filters.erb" - # Must be root! - owner "root" - group "root" - mode 00644 -end - -template "/etc/nova/rootwrap.d/network.filters" do - source "rootwrap.d/network.filters.erb" - # Must be root! - owner "root" - group "root" - mode 00644 -end - -# TODO: need to re-evaluate this for accuracy -# TODO(jaypipes): This should be moved into openstack-common -# and evaluated only on nodes with admin privs. -template "/root/openrc" do - source "openrc.erb" - # Must be root! - owner "root" - group "root" - mode 00600 - variables( - :user => ksadmin_user, - :tenant => ksadmin_tenant_name, - :password => ksadmin_pass, - :identity_endpoint => identity_endpoint, - :auth_strategy => "keystone", - :ec2_url => ec2_public_endpoint.to_s - ) -end - -execute "enable nova login" do - command "usermod -s /bin/sh #{node["openstack"]["compute"]["user"]}" +execute 'enable nova login' do + command "usermod -s /bin/sh #{node['openstack']['compute']['user']}" end diff --git a/chef/cookbooks/openstack-compute/recipes/nova-setup.rb b/chef/cookbooks/openstack-compute/recipes/nova-setup.rb index 8bc3d12..b59b399 100644 --- a/chef/cookbooks/openstack-compute/recipes/nova-setup.rb +++ b/chef/cookbooks/openstack-compute/recipes/nova-setup.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: nova-setup @@ -17,116 +18,112 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -execute "nova-manage db sync" do - command "nova-manage db sync" +nova_user = node['openstack']['compute']['user'] +nova_group = node['openstack']['compute']['group'] +execute 'nova-manage db sync' do + user nova_user + group nova_group + command 'nova-manage db sync' action :run end -case node["openstack"]["compute"]["network"]["service_type"] -when "nova" +case node['openstack']['compute']['network']['service_type'] +when 'nova' next_vlan = 100 - node["openstack"]["compute"]["networks"].each do |net| + node['openstack']['compute']['networks'].each do |net| execute "nova-manage network create --label=#{net['label']}" do + user nova_user + group nova_group + # The only two required keys in each network Hash - # are "label" and "ipv4_cidr". + # are 'label' and 'ipv4_cidr'. cmd = "nova-manage network create --label=#{net['label']} --fixed_range_v4=#{net['ipv4_cidr']}" - if net.has_key?("multi_host") - cmd += " --multi_host='#{net['multi_host']}'" + cmd += " --multi_host='#{net['multi_host']}'" if net.key?('multi_host') + %w{num_networks network_size bridge dns1 dns2}.each do |v| + cmd += " --#{v}=#{net[v]}" if net.key?(v) end - if net.has_key?("num_networks") - cmd += " --num_networks=#{net['num_networks']}" - end - if net.has_key?("network_size") - cmd += " --network_size=#{net['network_size']}" - end - if net.has_key?("bridge") - cmd += " --bridge=#{net['bridge']}" - end - # Older attributes have the key as "bridge_dev" instead - # of "bridge_interface"... - if net.has_key?("bridge_interface") or net.has_key?("bridge_dev") - val = net.has_key?("bridge_interface") ? net["bridge_interface"] : net["bridge_dev"] + # Older attributes have the key as 'bridge_dev' instead + # of 'bridge_interface'... + if net.key?('bridge_interface') || net.key?('bridge_dev') + val = net.key?('bridge_interface') ? net['bridge_interface'] : net['bridge_dev'] cmd += " --bridge_interface=#{val}" end - if net.has_key?("dns1") - cmd += " --dns1=#{net['dns1']}" - end - if net.has_key?("dns2") - cmd += " --dns2=#{net['dns2']}" - end - if net.has_key?("vlan") + if net.key?('vlan') cmd += " --vlan=#{net['vlan']}" - elsif node["openstack"]["compute"]["network"]["network_manager"] == "nova.network.manager.VlanManager" + elsif node['openstack']['compute']['network']['network_manager'] == 'nova.network.manager.VlanManager' cmd += " --vlan=#{next_vlan}" next_vlan = next_vlan + 1 end - command cmd - not_if "nova-manage network list | grep #{net['ipv4_cidr']}" + not_if "nova-manage network list | grep #{net['ipv4_cidr']}", user: nova_user, group: nova_group action :run end end - cookbook_file node["openstack"]["compute"]["floating_cmd"] do - source "add_floaters.py" + cookbook_file node['openstack']['compute']['floating_cmd'] do + source 'add_floaters.py' mode 00755 action :create end - floating = node["openstack"]["compute"]["network"]["floating"] - if floating && (floating["ipv4_cidr"] || floating["ipv4_range"]) - cmd = "" - if floating["ipv4_cidr"] - cmd = "#{node["openstack"]["compute"]["floating_cmd"]} nova --cidr=#{floating["ipv4_cidr"]}" - elsif floating["ipv4_range"] - cmd = "#{node["openstack"]["compute"]["floating_cmd"]} nova --ip-range=#{floating["ipv4_range"]}" + floating = node['openstack']['compute']['network']['floating'] + if floating && (floating['ipv4_cidr'] || floating['ipv4_range']) + cmd = '' + if floating['ipv4_cidr'] + cmd = "#{node['openstack']['compute']['floating_cmd']} nova --cidr=#{floating['ipv4_cidr']}" + elsif floating['ipv4_range'] + cmd = "#{node['openstack']['compute']['floating_cmd']} nova --ip-range=#{floating['ipv4_range']}" end - execute "nova-manage floating create" do + execute 'nova-manage floating create' do + user nova_user + group nova_group command cmd - not_if "nova-manage floating list |grep -E '.*([0-9]{1,3}[\.]){3}[0-9]{1,3}*'" + not_if "nova-manage floating list |grep -E '.*([0-9]{1,3}[\.]){3}[0-9]{1,3}*'", user: nova_user, group: nova_group action :run end end -when "quantum", "neutron" +when 'neutron' - platform_options = node["openstack"]["compute"]["platform"] + include_recipe 'openstack-common::openrc' - platform_options["neutron_python_packages"].each do |pkg| + platform_options = node['openstack']['compute']['platform'] + + platform_options['neutron_python_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end - cookbook_file node["openstack"]["compute"]["floating_cmd"] do - source "add_floaters.py" + cookbook_file node['openstack']['compute']['floating_cmd'] do + source 'add_floaters.py' mode 00755 action :create end - floating = node["openstack"]["compute"]["network"]["floating"] - if floating && floating["ipv4_cidr"] - cmd = ". /root/openrc && #{node["openstack"]["compute"]["floating_cmd"]} neutron --cidr=#{floating["ipv4_cidr"]} --pool=#{floating["public_network_name"]}" + floating = node['openstack']['compute']['network']['floating'] + if floating && floating['ipv4_cidr'] + cmd = ". /root/openrc && #{node['openstack']['compute']['floating_cmd']} neutron --cidr=#{floating['ipv4_cidr']} --pool=#{floating['public_network_name']}" - execute "quantum floating create" do + execute 'neutron floating create' do command cmd - not_if ". /root/openrc && quantum floatingip-list |grep -E '.*([0-9]{1,3}[\.]){3}[0-9]{1,3}*'" - only_if { File.exists?("/root/openrc") } + not_if ". /root/openrc && neutron floatingip-list |grep -E '.*([0-9]{1,3}[\.]){3}[0-9]{1,3}*'" + only_if { File.exists?('/root/openrc') } action :run end diff --git a/chef/cookbooks/openstack-compute/recipes/scheduler.rb b/chef/cookbooks/openstack-compute/recipes/scheduler.rb index ac09482..31050c3 100644 --- a/chef/cookbooks/openstack-compute/recipes/scheduler.rb +++ b/chef/cookbooks/openstack-compute/recipes/scheduler.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: scheduler @@ -18,30 +19,30 @@ # limitations under the License. # -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -platform_options = node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] -directory "/var/lock/nova" do - owner node["openstack"]["compute"]["user"] - group node["openstack"]["compute"]["group"] +directory '/var/lock/nova' do + owner node['openstack']['compute']['user'] + group node['openstack']['compute']['group'] mode 00700 action :create end -platform_options["compute_scheduler_packages"].each do |pkg| +platform_options['compute_scheduler_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end -service "nova-scheduler" do - service_name platform_options["compute_scheduler_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") +service 'nova-scheduler' do + service_name platform_options['compute_scheduler_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') - action [ :enable, :restart ] + action [:enable, :start] end diff --git a/chef/cookbooks/openstack-compute/recipes/vncproxy.rb b/chef/cookbooks/openstack-compute/recipes/vncproxy.rb index a694d9e..ea32cc4 100644 --- a/chef/cookbooks/openstack-compute/recipes/vncproxy.rb +++ b/chef/cookbooks/openstack-compute/recipes/vncproxy.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-compute # Recipe:: vncproxy @@ -18,53 +19,39 @@ # limitations under the License. # -include_recipe "openstack-compute::nova-common" +include_recipe 'openstack-compute::nova-common' -platform_options = node["openstack"]["compute"]["platform"] +platform_options = node['openstack']['compute']['platform'] -platform_options["compute_vncproxy_packages"].each do |pkg| +platform_options['compute_vncproxy_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] + options platform_options['package_overrides'] action :upgrade end end # required for vnc console authentication -platform_options["compute_vncproxy_console_packages"].each do |pkg| - package pkg do - action :upgrade - only_if { platform?("ubuntu") or platform?(%w(fedora redhat centos))} - end -end - -platform_options["compute_vncproxy_consoleauth_packages"].each do |pkg| +platform_options['compute_vncproxy_consoleauth_packages'].each do |pkg| package pkg do action :upgrade end end -proxy_service = platform_options["compute_vncproxy_service"] +proxy_service = platform_options['compute_vncproxy_service'] service proxy_service do service_name proxy_service - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') action [:enable, :start] end -service "nova-console" do - service_name platform_options["compute_vncproxy_console_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") - action [:enable, :start] -end - -service "nova-consoleauth" do - service_name platform_options["compute_vncproxy_consoleauth_service"] - supports :status => true, :restart => true - subscribes :restart, resources("template[/etc/nova/nova.conf]") +service 'nova-consoleauth' do + service_name platform_options['compute_vncproxy_consoleauth_service'] + supports status: true, restart: true + subscribes :restart, resources('template[/etc/nova/nova.conf]') action [:enable, :start] end diff --git a/chef/cookbooks/openstack-compute/spec/api-ec2-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/api-ec2-redhat_spec.rb index 4e4606c..2abef34 100644 --- a/chef/cookbooks/openstack-compute/spec/api-ec2-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/api-ec2-redhat_spec.rb @@ -1,19 +1,21 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::api-ec2" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::api-ec2" +require_relative 'spec_helper' + +describe 'openstack-compute::api-ec2' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades ec2 api packages' do + expect(chef_run).to upgrade_package 'openstack-nova-api' end - it "installs ec2 api packages" do - expect(@chef_run).to upgrade_package "openstack-nova-api" - end - - it "starts ec2 api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-api" + it 'starts ec2 api on boot' do + expect(chef_run).to enable_service 'openstack-nova-api' end end end diff --git a/chef/cookbooks/openstack-compute/spec/api-ec2_spec.rb b/chef/cookbooks/openstack-compute/spec/api-ec2_spec.rb index 2681ece..cefddac 100644 --- a/chef/cookbooks/openstack-compute/spec/api-ec2_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/api-ec2_spec.rb @@ -1,27 +1,26 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::api-ec2" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::api-ec2" +require_relative 'spec_helper' + +describe 'openstack-compute::api-ec2' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + include_examples 'expect_creates_nova_lock_dir' + include_examples 'expect_upgrades_python_keystoneclient' + + it 'upgrade ec2 api package' do + expect(chef_run).to upgrade_package 'nova-api-ec2' end - expect_runs_nova_common_recipe - - expect_creates_nova_lock_dir - - expect_installs_python_keystone - - it "installs ec2 api packages" do - expect(@chef_run).to upgrade_package "nova-api-ec2" + it 'starts ec2 api on boot' do + expect(chef_run).to enable_service 'nova-api-ec2' end - it "starts ec2 api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-api-ec2" - end - - expect_creates_api_paste "service[nova-api-ec2]" + expect_creates_api_paste 'service[nova-api-ec2]' end end diff --git a/chef/cookbooks/openstack-compute/spec/api-metadata-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/api-metadata-redhat_spec.rb index b00130a..e6b3ba7 100644 --- a/chef/cookbooks/openstack-compute/spec/api-metadata-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/api-metadata-redhat_spec.rb @@ -1,19 +1,21 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::api-metadata" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::api-metadata" +require_relative 'spec_helper' + +describe 'openstack-compute::api-metadata' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades metadata api packages' do + expect(chef_run).to upgrade_package 'openstack-nova-api' end - it "installs metadata api packages" do - expect(@chef_run).to upgrade_package "openstack-nova-api" - end - - it "starts metadata api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-api" + it 'starts metadata api on boot' do + expect(chef_run).to enable_service 'openstack-nova-api' end end end diff --git a/chef/cookbooks/openstack-compute/spec/api-metadata_spec.rb b/chef/cookbooks/openstack-compute/spec/api-metadata_spec.rb index 8b0cc84..49c3938 100644 --- a/chef/cookbooks/openstack-compute/spec/api-metadata_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/api-metadata_spec.rb @@ -1,27 +1,26 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::api-metadata" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::api-metadata" +require_relative 'spec_helper' + +describe 'openstack-compute::api-metadata' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + include_examples 'expect_creates_nova_lock_dir' + include_examples 'expect_upgrades_python_keystoneclient' + + it 'upgrades metadata api packages' do + expect(chef_run).to upgrade_package 'nova-api-metadata' end - expect_runs_nova_common_recipe - - expect_creates_nova_lock_dir - - expect_installs_python_keystone - - it "installs metadata api packages" do - expect(@chef_run).to upgrade_package "nova-api-metadata" + it 'starts metadata api on boot' do + expect(chef_run).to enable_service 'nova-api-metadata' end - it "starts metadata api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-api-metadata" - end - - expect_creates_api_paste "service[nova-api-metadata]" + expect_creates_api_paste 'service[nova-api-metadata]' end end diff --git a/chef/cookbooks/openstack-compute/spec/api-os-compute-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/api-os-compute-redhat_spec.rb index b957c5c..02cce76 100644 --- a/chef/cookbooks/openstack-compute/spec/api-os-compute-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/api-os-compute-redhat_spec.rb @@ -1,19 +1,25 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::api-os-compute" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::api-os-compute" +require_relative 'spec_helper' + +describe 'openstack-compute::api-os-compute' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades openstack api packages' do + expect(chef_run).to upgrade_package 'openstack-nova-api' end - it "installs openstack api packages" do - expect(@chef_run).to upgrade_package "openstack-nova-api" + it 'starts openstack api on boot' do + expect(chef_run).to enable_service 'openstack-nova-api' end - it "starts openstack api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-api" + it 'starts openstack api now' do + expect(chef_run).to start_service 'openstack-nova-api' end end end diff --git a/chef/cookbooks/openstack-compute/spec/api-os-compute_spec.rb b/chef/cookbooks/openstack-compute/spec/api-os-compute_spec.rb index d2b828f..2eb8e0e 100644 --- a/chef/cookbooks/openstack-compute/spec/api-os-compute_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/api-os-compute_spec.rb @@ -1,45 +1,38 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::api-os-compute" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::api-os-compute" +require_relative 'spec_helper' + +describe 'openstack-compute::api-os-compute' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + include_examples 'expect_creates_nova_lock_dir' + include_examples 'expect_upgrades_python_keystoneclient' + + it 'creates the /var/cache/nova directory' do + expect(chef_run).to create_directory('/var/cache/nova').with( + user: 'nova', + group: 'nova', + mode: 0700 + ) end - expect_runs_nova_common_recipe - - expect_creates_nova_lock_dir - - describe "/var/cache/nova" do - before do - @dir = @chef_run.directory "/var/cache/nova" - end - - it "has proper owner" do - expect(@dir).to be_owned_by "nova", "nova" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" - end + it 'upgrades openstack api packages' do + expect(chef_run).to upgrade_package 'nova-api-os-compute' end - expect_installs_python_keystone - - it "installs openstack api packages" do - expect(@chef_run).to upgrade_package "nova-api-os-compute" + it 'starts openstack api on boot' do + expect(chef_run).to enable_service 'nova-api-os-compute' end - it "starts openstack api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-api-os-compute" + it 'starts openstack api now' do + expect(chef_run).to start_service 'nova-api-os-compute' end - it "starts openstack api now" do - expect(@chef_run).to start_service "nova-api-os-compute" - end - - expect_creates_api_paste "service[nova-api-os-compute]" + expect_creates_api_paste 'service[nova-api-os-compute]' end end diff --git a/chef/cookbooks/openstack-compute/spec/client-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/client-redhat_spec.rb new file mode 100644 index 0000000..e2fd51d --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/client-redhat_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-compute::client' do + + describe 'redhat' do + + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades python-novaclient package' do + expect(chef_run).to upgrade_package('python-novaclient') + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/client_spec.rb b/chef/cookbooks/openstack-compute/spec/client_spec.rb new file mode 100644 index 0000000..3606904 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/client_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-compute::client' do + + describe 'ubuntu' do + + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades python-novaclient package' do + expect(chef_run).to upgrade_package('python-novaclient') + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/compute-opensuse_spec.rb b/chef/cookbooks/openstack-compute/spec/compute-opensuse_spec.rb deleted file mode 100644 index fa4c1cb..0000000 --- a/chef/cookbooks/openstack-compute/spec/compute-opensuse_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-compute::compute" do - before { compute_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-compute::compute" - end - - it "installs nfs client packages" do - expect(@chef_run).to upgrade_package "nfs-utils" - expect(@chef_run).not_to upgrade_package "nfs-utils-lib" - end - end -end diff --git a/chef/cookbooks/openstack-compute/spec/compute-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/compute-redhat_spec.rb index 48b4e91..2b8bcc5 100644 --- a/chef/cookbooks/openstack-compute/spec/compute-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/compute-redhat_spec.rb @@ -1,45 +1,43 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::compute" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::compute" +require_relative 'spec_helper' + +describe 'openstack-compute::compute' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it "does not upgrade kvm when virt_type is 'kvm'" do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'kvm' + + expect(chef_run).to_not upgrade_package('nova-compute-kvm') end - it "does not install kvm when virt_type is 'kvm'" do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - node = chef_run.node - node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "kvm" - chef_run.converge "openstack-compute::compute" - expect(chef_run).to_not upgrade_package "nova-compute-kvm" + it "does not upgrade qemu when virt_type is 'qemu'" do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'qemu' + + expect(chef_run).to_not upgrade_package('nova-compute-qemu') end - it "does not install qemu when virt_type is 'qemu'" do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - node = chef_run.node - node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "qemu" - chef_run.converge "openstack-compute::compute" - expect(chef_run).to_not upgrade_package "nova-compute-qemu" + it 'upgrades nova compute package' do + expect(chef_run).to upgrade_package('openstack-nova-compute') end - it "installs nova compute packages" do - expect(@chef_run).to upgrade_package "openstack-nova-compute" + it 'upgrades nfs client package' do + expect(chef_run).to upgrade_package('nfs-utils') + expect(chef_run).to upgrade_package('nfs-utils-lib') end - it "installs nfs client packages" do - expect(@chef_run).to upgrade_package "nfs-utils" - expect(@chef_run).to upgrade_package "nfs-utils-lib" + it 'starts nova compute on boot' do + expected = 'openstack-nova-compute' + expect(chef_run).to enable_service(expected) end - it "starts nova compute on boot" do - expected = "openstack-nova-compute" - expect(@chef_run).to set_service_to_start_on_boot expected - end - - it "starts nova compute" do - expect(@chef_run).to start_service "openstack-nova-compute" + it 'starts nova compute' do + expect(chef_run).to start_service('openstack-nova-compute') end end end diff --git a/chef/cookbooks/openstack-compute/spec/compute-suse_spec.rb b/chef/cookbooks/openstack-compute/spec/compute-suse_spec.rb new file mode 100644 index 0000000..a94e0cb --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/compute-suse_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-compute::compute' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades nfs client packages' do + expect(chef_run).to upgrade_package 'nfs-utils' + expect(chef_run).not_to upgrade_package 'nfs-utils-lib' + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/compute_spec.rb b/chef/cookbooks/openstack-compute/spec/compute_spec.rb index 42d14a6..ebb5c05 100644 --- a/chef/cookbooks/openstack-compute/spec/compute_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/compute_spec.rb @@ -1,84 +1,104 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::compute" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::compute" +require_relative 'spec_helper' + +describe 'openstack-compute::compute' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + + it 'includes api-metadata recipe' do + expect(chef_run).to include_recipe 'openstack-compute::api-metadata' end - expect_runs_nova_common_recipe - - it "runs api-metadata recipe" do - expect(@chef_run).to include_recipe "openstack-compute::api-metadata" - end - - it "runs network recipe" do - expect(@chef_run).to include_recipe "openstack-compute::network" - end - - it "doesn't run network recipe with openstack-network::server" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + it 'does not include api-metadata recipe' do + chef_run = ::ChefSpec::Runner.new ::UBUNTU_OPTS node = chef_run.node - node.run_list.stub("include?").and_return true - chef_run.converge "openstack-compute::compute" + node.set['openstack']['compute']['enabled_apis'] = 'ec2,osapi_compute' + chef_run.converge 'openstack-compute::compute' - expect(chef_run).not_to include_recipe "openstack-compute::network" + expect(chef_run).not_to include_recipe 'openstack-compute::api-metadata' end - it "installs nova compute packages" do - expect(@chef_run).to upgrade_package "nova-compute" + it 'runs network recipe' do + expect(chef_run).to include_recipe 'openstack-compute::network' end - it "installs nfs client packages" do - expect(@chef_run).to upgrade_package "nfs-common" + it 'upgrades nova compute package' do + expect(chef_run).to upgrade_package 'nova-compute' end - it "installs kvm when virt_type is 'kvm'" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "kvm" - chef_run.converge "openstack-compute::compute" - - expect(chef_run).to upgrade_package "nova-compute-kvm" - expect(chef_run).not_to upgrade_package "nova-compute-qemu" + it 'upgrades nfs client package' do + expect(chef_run).to upgrade_package 'nfs-common' end - it "installs qemu when virt_type is 'qemu'" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "qemu" - chef_run.converge "openstack-compute::compute" + it "upgrades kvm when virt_type is 'kvm'" do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'kvm' - expect(chef_run).to upgrade_package "nova-compute-qemu" - expect(chef_run).not_to upgrade_package "nova-compute-kvm" + expect(chef_run).to upgrade_package 'nova-compute-kvm' + expect(chef_run).not_to upgrade_package 'nova-compute-qemu' end - describe "nova-compute.conf" do - before do - @file = @chef_run.cookbook_file "/etc/nova/nova-compute.conf" - end + it 'honors the package options platform overrides for kvm' do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'kvm' + node.set['openstack']['compute']['platform']['package_overrides'] = '-o Dpkg::Options::=\'--force-confold\' -o Dpkg::Options::=\'--force-confdef\' --force-yes' - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end + expect(chef_run).to upgrade_package('nova-compute-kvm').with(options: '-o Dpkg::Options::=\'--force-confold\' -o Dpkg::Options::=\'--force-confdef\' --force-yes') + end - it "template contents" do - pending "TODO: implement" + it 'upgrades qemu when virt_type is qemu' do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'qemu' + + expect(chef_run).to upgrade_package 'nova-compute-qemu' + expect(chef_run).not_to upgrade_package 'nova-compute-kvm' + end + + it 'honors the package options platform overrides for qemu' do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'qemu' + node.set['openstack']['compute']['platform']['package_overrides'] = '-o Dpkg::Options::=\'--force-confold\' -o Dpkg::Options::=\'--force-confdef\' --force-yes' + + expect(chef_run).to upgrade_package('nova-compute-qemu').with(options: '-o Dpkg::Options::=\'--force-confold\' -o Dpkg::Options::=\'--force-confdef\' --force-yes') + end + + %w{qemu kvm}.each do |virt_type| + it "honors the package name platform overrides for #{virt_type}" do + node.set['openstack']['compute']['libvirt']['virt_type'] = virt_type + node.set['openstack']['compute']['platform']["#{virt_type}_compute_packages"] = ["my-nova-#{virt_type}"] + + expect(chef_run).to upgrade_package("my-nova-#{virt_type}") end end - it "starts nova compute on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-compute" + describe 'nova-compute.conf' do + let(:file) { chef_run.cookbook_file('/etc/nova/nova-compute.conf') } + + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq '644' + end end - it "starts nova compute" do - expect(@chef_run).to start_service "nova-compute" + it 'starts nova compute on boot' do + expect(chef_run).to enable_service 'nova-compute' end - it "runs libvirt recipe" do - expect(@chef_run).to include_recipe "openstack-compute::libvirt" + it 'starts nova compute' do + expect(chef_run).to start_service 'nova-compute' + end + + it 'runs libvirt recipe' do + expect(chef_run).to include_recipe 'openstack-compute::libvirt' + end + + it 'creates instances_path directory' do + expect(chef_run).to create_directory('/var/lib/nova/instances').with( + owner: 'nova', + group: 'nova', + mode: 0755 + ) end end end diff --git a/chef/cookbooks/openstack-compute/spec/conductor_redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/conductor_redhat_spec.rb index 8b2bbe6..1c5a9dd 100644 --- a/chef/cookbooks/openstack-compute/spec/conductor_redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/conductor_redhat_spec.rb @@ -1,25 +1,25 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::conductor" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::conductor" +require_relative 'spec_helper' + +describe 'openstack-compute::conductor' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades conductor package' do + expect(chef_run).to upgrade_package 'openstack-nova-conductor' end - expect_runs_nova_common_recipe - - it "installs conductor packages" do - expect(@chef_run).to upgrade_package "openstack-nova-conductor" + it 'starts nova-conductor on boot' do + expect(chef_run).to enable_service 'openstack-nova-conductor' end - it "starts nova-conductor on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-conductor" - end - - it "starts nova-conductor" do - expect(@chef_run).to start_service "openstack-nova-conductor" + it 'starts nova-conductor' do + expect(chef_run).to start_service 'openstack-nova-conductor' end end end diff --git a/chef/cookbooks/openstack-compute/spec/conductor_spec.rb b/chef/cookbooks/openstack-compute/spec/conductor_spec.rb index 894b7be..896bc96 100644 --- a/chef/cookbooks/openstack-compute/spec/conductor_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/conductor_spec.rb @@ -1,25 +1,26 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::conductor" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::conductor" +require_relative 'spec_helper' + +describe 'openstack-compute::conductor' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + + it 'upgrades conductor package' do + expect(chef_run).to upgrade_package 'nova-conductor' end - expect_runs_nova_common_recipe - - it "installs conductor packages" do - expect(@chef_run).to upgrade_package "nova-conductor" + it 'starts nova-conductor on boot' do + expect(chef_run).to enable_service 'nova-conductor' end - it "starts nova-conductor on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-conductor" - end - - it "starts nova-conductor" do - expect(@chef_run).to start_service "nova-conductor" + it 'starts nova-conductor' do + expect(chef_run).to start_service 'nova-conductor' end end end diff --git a/chef/cookbooks/openstack-compute/spec/default_spec.rb b/chef/cookbooks/openstack-compute/spec/default_spec.rb index dbbacb8..2890992 100644 --- a/chef/cookbooks/openstack-compute/spec/default_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/default_spec.rb @@ -1,4 +1,6 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::default" do +require_relative 'spec_helper' + +describe 'openstack-compute::default' do end diff --git a/chef/cookbooks/openstack-compute/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-compute/spec/identity_registration_spec.rb index 4666373..c4e73c0 100644 --- a/chef/cookbooks/openstack-compute/spec/identity_registration_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/identity_registration_spec.rb @@ -1,124 +1,136 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::identity_registration" do - before do - compute_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::identity_registration" - end +require_relative 'spec_helper' - it "registers service tenant" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Service Tenant" - ).to_hash +describe 'openstack-compute::identity_registration' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :tenant_description => "Service Tenant", - :action => [:create_tenant] - ) - end + include_context 'compute_stubs' - it "registers service user" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Service User" - ).to_hash + it 'registers service tenant' do + expect(chef_run).to create_tenant_openstack_identity_register( + 'Register Service Tenant' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + tenant_description: 'Service Tenant' + ) + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :user_name => "nova", - :user_pass => "nova-pass", - :action => [:create_user] - ) - end + it 'registers service user' do + expect(chef_run).to create_user_openstack_identity_register( + 'Register Service User' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'nova', + user_pass: 'nova-pass' + ) + end - it "grants admin role to service user for service tenant" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Grant 'admin' Role to Service User for Service Tenant" - ).to_hash + it 'grants admin role to service user for service tenant' do + expect(chef_run).to grant_role_openstack_identity_register( + "Grant 'admin' Role to Service User for Service Tenant" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'nova', + role_name: 'admin' + ) + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :user_name => "nova", - :role_name => "admin", - :action => [:grant_role] - ) - end + it 'registers compute service' do + expect(chef_run).to create_service_openstack_identity_register( + 'Register Compute Service' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_name: 'nova', + service_type: 'compute', + service_description: 'Nova Compute Service' + ) + end - it "registers compute service" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Compute Service" - ).to_hash + context 'registers compute endpoint' do + it 'with default values' do + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Compute Endpoint' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'compute', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:8774/v2/%(tenant_id)s', + endpoint_internalurl: 'http://127.0.0.1:8774/v2/%(tenant_id)s', + endpoint_publicurl: 'http://127.0.0.1:8774/v2/%(tenant_id)s' + ) + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_name => "nova", - :service_type => "compute", - :service_description => "Nova Compute Service", - :action => [:create_service] - ) - end + it 'with custom region override' do + node.set['openstack']['compute']['region'] = 'computeRegion' + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Compute Endpoint' + ).with(endpoint_region: 'computeRegion') + end + end - it "registers compute endpoint" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Compute Endpoint" - ).to_hash + it 'registers ec2 service' do + expect(chef_run).to create_service_openstack_identity_register( + 'Register EC2 Service' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_name: 'ec2', + service_type: 'ec2', + service_description: 'EC2 Compatibility Layer' + ) + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_type => "compute", - :endpoint_region => "RegionOne", - :endpoint_adminurl => "http://127.0.0.1:8774/v2/%(tenant_id)s", - :endpoint_internalurl => "http://127.0.0.1:8774/v2/%(tenant_id)s", - :endpoint_publicurl => "http://127.0.0.1:8774/v2/%(tenant_id)s", - :action => [:create_endpoint] - ) - end + context 'registers ec2 endpoint' do + it 'with default values' do + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register EC2 Endpoint' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'ec2', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:8773/services/Admin', + endpoint_internalurl: 'http://127.0.0.1:8773/services/Cloud', + endpoint_publicurl: 'http://127.0.0.1:8773/services/Cloud' + ) + end - it "registers ec2 service" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register EC2 Service" - ).to_hash + it 'with customer region override' do + node.set['openstack']['compute']['region'] = 'ec2Region' + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register EC2 Endpoint' + ).with(endpoint_region: 'ec2Region') + end + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_name => "ec2", - :service_type => "ec2", - :service_description => "EC2 Compatibility Layer", - :action => [:create_service] - ) - end + describe "when 'ec2' is not in the list of enabled_apis" do + before do + node.set['openstack']['compute']['enabled_apis'] = 'osapi_compute,metadata' + end - it "registers ec2 endpoint" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register EC2 Endpoint" - ).to_hash + it 'does not register ec2 service' do + expect(chef_run).not_to create_service_openstack_identity_register( + 'Register EC2 Service' + ) + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_type => "ec2", - :endpoint_region => "RegionOne", - :endpoint_adminurl => "http://127.0.0.1:8773/services/Admin", - :endpoint_internalurl => "http://127.0.0.1:8773/services/Cloud", - :endpoint_publicurl => "http://127.0.0.1:8773/services/Cloud", - :action => [:create_endpoint] - ) + it 'does not register ec2 endpoint' do + expect(chef_run).not_to create_endpoint_openstack_identity_register( + 'Register EC2 Endpoint' + ) + end + end end end diff --git a/chef/cookbooks/openstack-compute/spec/libvirt-opensuse_spec.rb b/chef/cookbooks/openstack-compute/spec/libvirt-opensuse_spec.rb deleted file mode 100644 index 5b66c1f..0000000 --- a/chef/cookbooks/openstack-compute/spec/libvirt-opensuse_spec.rb +++ /dev/null @@ -1,96 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-compute::libvirt" do - before do - compute_stubs - - # This is stubbed b/c systems without '/boot/grub/menul.lst`, - # fail to pass tests. This can be removed if a check verifies - # the files existence prior to File#open. - ::File.stub(:open).and_call_original - end - - describe "suse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-compute::libvirt" - end - - it "installs libvirt packages" do - expect(@chef_run).to install_package "libvirt" - end - - it "starts libvirt" do - expect(@chef_run).to start_service "libvirtd" - end - - it "starts libvirt on boot" do - expect(@chef_run).to set_service_to_start_on_boot "libvirtd" - end - - describe "libvirtd" do - before do - @file = @chef_run.template "/etc/sysconfig/libvirtd" - end - - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - - it "notifies libvirt-bin restart" do - expect(@file).to notify "service[libvirt-bin]", :restart - end - end - - it "installs kvm packages" do - expect(@chef_run).to install_package "kvm" - end - - it "installs qemu packages" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |node| - node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "qemu" - end - chef_run.converge "openstack-compute::libvirt" - expect(chef_run).to install_package "kvm" - end - - it "installs xen packages" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |node| - node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "xen" - end - chef_run.converge "openstack-compute::libvirt" - ["kernel-xen", "xen", "xen-tools"].each do |pkg| - expect(chef_run).to install_package pkg - end - end - - describe "lxc" do - before do - @chef_run_lxc = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |node| - node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "lxc" - end - @chef_run_lxc.converge "openstack-compute::libvirt" - end - - it "installs packages" do - expect(@chef_run_lxc).to install_package "lxc" - end - - it "starts boot.cgroupslxc" do - expect(@chef_run_lxc).to start_service "boot.cgroup" - end - - it "starts boot.cgroups on boot" do - expect(@chef_run_lxc).to set_service_to_start_on_boot "boot.cgroup" - end - end - end -end diff --git a/chef/cookbooks/openstack-compute/spec/libvirt-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/libvirt-redhat_spec.rb index 47fa8e7..da6f33a 100644 --- a/chef/cookbooks/openstack-compute/spec/libvirt-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/libvirt-redhat_spec.rb @@ -1,59 +1,60 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::libvirt" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::libvirt" +require_relative 'spec_helper' + +describe 'openstack-compute::libvirt' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades libvirt packages' do + expect(chef_run).to upgrade_package 'libvirt' end - it "installs libvirt packages" do - expect(@chef_run).to install_package "libvirt" + it 'creates libvirtd group and adds nova as a member' do + expect(chef_run).to create_group 'libvirtd' + libvirt_group = chef_run.group('libvirtd') + libvirt_group.members.should == ['nova'] end - it "creates libvirtd group and adds nova as a member" do - expect(@chef_run).to create_group "libvirtd" - libvirt_group = @chef_run.group("libvirtd") - libvirt_group.members.should == ["nova"] + it 'symlinks qemu-kvm' do + link = chef_run.link '/usr/bin/qemu-system-x86_64' + expect(link).to link_to '/usr/libexec/qemu-kvm' end - it "symlinks qemu-kvm" do - link = @chef_run.link "/usr/bin/qemu-system-x86_64" - expect(link).to link_to "/usr/libexec/qemu-kvm" + it 'starts libvirt' do + expect(chef_run).to start_service 'libvirtd' end - it "starts libvirt" do - expect(@chef_run).to start_service "libvirtd" + it 'starts libvirt on boot' do + expect(chef_run).to enable_service 'libvirtd' end - it "starts libvirt on boot" do - expect(@chef_run).to set_service_to_start_on_boot "libvirtd" + it 'does not create /etc/default/libvirt-bin' do + pending 'TODO: how to test this' end - it "does not create /etc/default/libvirt-bin" do - pending "TODO: how to test this" - end + describe '/etc/sysconfig/libvirtd' do + let(:file) { chef_run.template('/etc/sysconfig/libvirtd') } - describe "libvirtd" do - before do - @file = @chef_run.template "/etc/sysconfig/libvirtd" + it 'creates the /etc/sysconfig/libvirtd file' do + expect(chef_run).to create_template(file.name).with( + owner: 'root', + group: 'root', + mode: 0644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" + it 'template contents' do + expect(chef_run).to render_file(file.name) + .with_content(/^LIBVIRTD_ARGS="--listen"$/) end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - - it "notifies libvirt-bin restart" do - expect(@file).to notify "service[libvirt-bin]", :restart + it 'notifies libvirt-bin restart' do + expect(file).to notify('service[libvirt-bin]').to(:restart) end end end diff --git a/chef/cookbooks/openstack-compute/spec/libvirt-suse_spec.rb b/chef/cookbooks/openstack-compute/spec/libvirt-suse_spec.rb new file mode 100644 index 0000000..94d7704 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/libvirt-suse_spec.rb @@ -0,0 +1,70 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-compute::libvirt' do + before do + # This is stubbed b/c systems without '/boot/grub/menul.lst`, + # fail to pass tests. This can be removed if a check verifies + # the files existence prior to File#open. + ::File.stub(:open).and_call_original + end + + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrade libvirt package' do + expect(chef_run).to upgrade_package 'libvirt' + end + + it 'starts libvirt' do + expect(chef_run).to start_service 'libvirtd' + end + + it 'starts libvirt on boot' do + expect(chef_run).to enable_service 'libvirtd' + end + + it 'does not install /etc/sysconfig/libvirtd' do + expect(chef_run).not_to create_template('/etc/sysconfig/libvirtd') + end + + it 'upgrade kvm package' do + expect(chef_run).to upgrade_package 'kvm' + end + + it 'upgrade kvm package' do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'qemu' + expect(chef_run).to upgrade_package 'kvm' + end + + it 'upgrade xen packages' do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'xen' + ['kernel-xen', 'xen', 'xen-tools'].each do |pkg| + expect(chef_run).to upgrade_package pkg + end + end + + describe 'lxc' do + before do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'lxc' + end + + it 'upgrade lxc package' do + expect(chef_run).to upgrade_package 'lxc' + end + + it 'starts boot.cgroupslxc' do + expect(chef_run).to start_service 'boot.cgroup' + end + + it 'starts boot.cgroups on boot' do + expect(chef_run).to enable_service 'boot.cgroup' + end + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/libvirt_rbd_spec.rb b/chef/cookbooks/openstack-compute/spec/libvirt_rbd_spec.rb new file mode 100644 index 0000000..47e6b72 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/libvirt_rbd_spec.rb @@ -0,0 +1,64 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-compute::libvirt_rbd' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['libvirt']['volume_backend'] = 'rbd' + + runner.converge(described_recipe) + end + + include_context 'compute_stubs' + + it 'includes the openstack-common::ceph_client recipe' do + expect(chef_run).to include_recipe('openstack-common::ceph_client') + end + + it 'upgrades rbd packages' do + expect(chef_run).to upgrade_package 'ceph-common' + end + + describe 'if there was no secret with this uuid defined' do + let(:file) { chef_run.template('/tmp/ad3313264ea51d8c6a3d1c5b140b9883.xml') } + + it 'creates the temporary secret xml file' do + expect(chef_run).to create_template(file.name).with( + owner: 'root', + group: 'root', + mode: '700' + ) + # TODO(srenatus) cannot check for its contents because it's deleted at + # the end of the (chefspec) chef run. + # [/client\.cinder secret/, + # /00000000-0000-0000-0000-000000000000/].each do |content| + # expect(@chef_run).to render_file(@filename).with_content(content) + # end + end + + it 'defines the secret' do + expect(chef_run).to run_execute("virsh secret-define --file #{file.name}") + end + + it 'sets the secret value to the password' do + expect(chef_run).to run_execute("virsh secret-set-value --secret 00000000-0000-0000-0000-000000000000 'cinder-rbd-pass'") + end + + it 'deletes the temporary secret xml file' do + expect(chef_run).to delete_file(file.name) + end + + end + + # TODO(srenatus) negative tests? + # describe 'if the secret was already defined' do + # before do + # stub_command('virsh secret-list | grep 00000000-0000-0000-0000-000000000000').and_return(true) + # stub_command('virsh secret-get-value 00000000-0000-0000-0000-000000000000 | grep \'cinder-rbd-pass\'').and_return(true) + # end + # end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/libvirt_spec.rb b/chef/cookbooks/openstack-compute/spec/libvirt_spec.rb index 90d66a2..799d310 100644 --- a/chef/cookbooks/openstack-compute/spec/libvirt_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/libvirt_spec.rb @@ -1,97 +1,110 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::libvirt" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::libvirt" +require_relative 'spec_helper' + +describe 'openstack-compute::libvirt' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades libvirt packages' do + expect(chef_run).to upgrade_package 'libvirt-bin' end - it "installs libvirt packages" do - expect(@chef_run).to install_package "libvirt-bin" + it 'does not create libvirtd group and add to nova' do + pending 'TODO: how to test this' end - it "does not create libvirtd group and add to nova" do - pending "TODO: how to test this" + it 'does not symlink qemu-kvm' do + pending 'TODO: how to test this' end - it "does not symlink qemu-kvm" do - pending "TODO: how to test this" + it 'starts dbus' do + expect(chef_run).to start_service 'dbus' end - it "starts dbus" do - expect(@chef_run).to start_service "dbus" + it 'starts dbus on boot' do + expect(chef_run).to enable_service 'dbus' end - it "starts dbus on boot" do - expect(@chef_run).to set_service_to_start_on_boot "dbus" + it 'starts libvirt' do + expect(chef_run).to start_service 'libvirt-bin' end - it "starts libvirt" do - expect(@chef_run).to start_service "libvirt-bin" + it 'starts libvirt on boot' do + expect(chef_run).to enable_service 'libvirt-bin' end - it "starts libvirt on boot" do - expect(@chef_run).to set_service_to_start_on_boot "libvirt-bin" + it 'disables default libvirt network' do + expect(chef_run).to run_execute('virsh net-autostart default --disable') end - it "disables default libvirt network" do - cmd = "virsh net-autostart default --disable" - expect(@chef_run).to execute_command cmd + it 'deletes default libvirt network' do + expect(chef_run).to run_execute('virsh net-destroy default') end - it "deletes default libvirt network" do - cmd = "virsh net-destroy default" - expect(@chef_run).to execute_command cmd - end - - describe "libvirtd.conf" do + describe 'rbd/ceph volume storage' do before do - @file = @chef_run.template "/etc/libvirt/libvirtd.conf" + node.set['openstack']['compute']['libvirt']['volume_backend'] = 'rbd' end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - - it "notifies libvirt-bin restart" do - expect(@file).to notify "service[libvirt-bin]", :restart + it 'includes the libvirt_rbd recipe if it is the selected volume backend' do + expect(chef_run).to include_recipe('openstack-compute::libvirt_rbd') end end - describe "libvirt-bin" do - before do - @file = @chef_run.template "/etc/default/libvirt-bin" + describe '/etc/libvirt/libvirtd.conf' do + let(:file) { chef_run.template('/etc/libvirt/libvirtd.conf') } + + it 'creates the /etc/libvirt/libvirtd.conf file' do + expect(chef_run).to create_template(file.name).with( + owner: 'root', + group: 'root', + mode: 0644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" + it 'has proper processing controls' do + [/^max_clients = 20$/, /^max_workers = 20$/, /^max_requests = 20$/, /^max_client_requests = 5$/].each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - - it "notifies libvirt-bin restart" do - expect(@file).to notify "service[libvirt-bin]", :restart + it 'notifies libvirt-bin restart' do + expect(file).to notify('service[libvirt-bin]').to(:restart) end end - it "does not create /etc/sysconfig/libvirtd" do - pending "TODO: how to test this" + describe '/etc/default/libvirt-bin' do + let(:file) { chef_run.template('/etc/default/libvirt-bin') } + + it 'creates the /etc/default/libvirt-bin file' do + expect(chef_run).to create_template(file.name).with( + owner: 'root', + group: 'root', + mode: 0644 + ) + end + + it 'template contents' do + [ + /^start_libvirtd="yes"$/, + /^libvirtd_opts="-d -l"$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'notifies libvirt-bin restart' do + expect(file).to notify('service[libvirt-bin]').to(:restart) + end + end + + it 'does not create /etc/sysconfig/libvirtd' do + pending 'TODO: how to test this' end end end diff --git a/chef/cookbooks/openstack-compute/spec/network-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/network-redhat_spec.rb index 599aae8..d51d6aa 100644 --- a/chef/cookbooks/openstack-compute/spec/network-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/network-redhat_spec.rb @@ -1,21 +1,22 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::network" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::network" +require_relative 'spec_helper' + +describe 'openstack-compute::network' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades nova network packages' do + expect(chef_run).to upgrade_package('iptables') + expect(chef_run).to upgrade_package('openstack-nova-network') end - it "installs nova network packages" do - expect(@chef_run).to upgrade_package "iptables" - expect(@chef_run).to upgrade_package "openstack-nova-network" - end - - it "starts nova network on boot" do - expected = "openstack-nova-network" - expect(@chef_run).to set_service_to_start_on_boot expected + it 'starts nova network on boot' do + expect(chef_run).to enable_service('openstack-nova-network') end end end diff --git a/chef/cookbooks/openstack-compute/spec/network_spec.rb b/chef/cookbooks/openstack-compute/spec/network_spec.rb index 20066f5..c0f6c88 100644 --- a/chef/cookbooks/openstack-compute/spec/network_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/network_spec.rb @@ -1,34 +1,40 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::network" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set["openstack"]["compute"]["network"]["service_type"] = "nova" - @chef_run.converge "openstack-compute::network" +require_relative 'spec_helper' + +describe 'openstack-compute::network' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + + context "when service_type is 'nova'" do + before do + node.set['openstack']['compute']['network']['service_type'] = 'nova' + end + + it 'upgrades nova network packages' do + expect(chef_run).to upgrade_package('iptables') + expect(chef_run).to upgrade_package('nova-network') + end + + it 'starts nova network on boot' do + expect(chef_run).to enable_service('nova-network') + end end - expect_runs_nova_common_recipe + context "when service_type is 'neutron'" do + before do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['compute']['network']['plugins'] = ['openvswitch'] + end - it "installs nova network packages" do - expect(@chef_run).to upgrade_package "iptables" - expect(@chef_run).to upgrade_package "nova-network" + it 'includes openstack-network recipes for neutron when service type is neutron' do + expect(chef_run).to include_recipe('openstack-network::openvswitch') + end end - - it "starts nova network on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-network" - end - - it "includes openstack-network recipes for quantum when service type is quantum" do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set["openstack"]["compute"]["network"]["service_type"] = "quantum" - @chef_run.converge "openstack-compute::network" - expect(@chef_run).to include_recipe "openstack-network::openvswitch" - expect(@chef_run).to include_recipe "openstack-network::dhcp_agent" - end - end end diff --git a/chef/cookbooks/openstack-compute/spec/nova-cert-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-cert-redhat_spec.rb index becd699..41ae8a5 100644 --- a/chef/cookbooks/openstack-compute/spec/nova-cert-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/nova-cert-redhat_spec.rb @@ -1,19 +1,21 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::nova-cert" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::nova-cert" +require_relative 'spec_helper' + +describe 'openstack-compute::nova-cert' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades nova cert package' do + expect(chef_run).to upgrade_package 'openstack-nova-cert' end - it "installs nova cert packages" do - expect(@chef_run).to upgrade_package "openstack-nova-cert" - end - - it "starts nova cert on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-cert" + it 'starts nova cert on boot' do + expect(chef_run).to enable_service 'openstack-nova-cert' end end end diff --git a/chef/cookbooks/openstack-compute/spec/nova-cert_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-cert_spec.rb index 8dc8458..60a9020 100644 --- a/chef/cookbooks/openstack-compute/spec/nova-cert_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/nova-cert_spec.rb @@ -1,21 +1,22 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::nova-cert" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::nova-cert" +require_relative 'spec_helper' + +describe 'openstack-compute::nova-cert' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + + it 'upgrades nova cert package' do + expect(chef_run).to upgrade_package('nova-cert') end - expect_runs_nova_common_recipe - - it "installs nova cert packages" do - expect(@chef_run).to upgrade_package "nova-cert" - end - - it "starts nova cert on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-cert" + it 'starts nova cert on boot' do + expect(chef_run).to enable_service('nova-cert') end end end diff --git a/chef/cookbooks/openstack-compute/spec/nova-common-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-common-redhat_spec.rb index 1cb0ad7..67e552f 100644 --- a/chef/cookbooks/openstack-compute/spec/nova-common-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/nova-common-redhat_spec.rb @@ -1,41 +1,41 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::nova-common" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::nova-common" +require_relative 'spec_helper' + +describe 'openstack-compute::nova-common' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades nova common package' do + expect(chef_run).to upgrade_package 'openstack-nova-common' end - it "runs epel recipe" do - expect(@chef_run).to include_recipe "yum::epel" + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package 'MySQL-python' end - it "installs nova common packages" do - expect(@chef_run).to upgrade_package "openstack-nova-common" - end - - it "installs memcache python packages" do - expect(@chef_run).to install_package "python-memcached" - end - - describe "nova.conf" do - before do - @file = @chef_run.template "/etc/nova/nova.conf" - # README(shep) need this to evaluate nova.conf.erb template - @chef_run.node.set['cpu'] = Hash.new() - @chef_run.node.set.cpu.total = "2" + it 'upgrades db2 python packages if explicitly told' do + node.set['openstack']['db']['compute']['service_type'] = 'db2' + ['python-ibm-db', 'python-ibm-db-sa'].each do |pkg| + expect(chef_run).to upgrade_package pkg end + end - it "has correct force_dhcp_release value" do - expect(@chef_run).to create_file_with_content "/etc/nova/nova.conf", - "force_dhcp_release=false" - end + it 'upgrades memcache python packages' do + expect(chef_run).to upgrade_package 'python-memcached' + end - it "has ec2_private_dns_show_ip enabled" do - expect(@chef_run).to create_file_with_content "/etc/nova/nova.conf", - "ec2_private_dns_show_ip=True" + describe 'nova.conf' do + let(:file) { chef_run.template('/etc/nova/nova.conf') } + + [/^ec2_private_dns_show_ip=True$/, /^force_dhcp_release=false$/].each do |content| + it "has a #{content.source[1...-1]} line" do + expect(chef_run).to render_file(file.name).with_content(content) + end end end end diff --git a/chef/cookbooks/openstack-compute/spec/nova-common_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-common_spec.rb index 8b11208..6dd5cc0 100644 --- a/chef/cookbooks/openstack-compute/spec/nova-common_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/nova-common_spec.rb @@ -1,341 +1,545 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::nova-common" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| - n.set["openstack"]["mq"] = { - "host" => "127.0.0.1" - } - n.set["openstack"]["compute"]["syslog"]["use"] = true - end - @chef_run.converge "openstack-compute::nova-common" +require_relative 'spec_helper' + +describe 'openstack-compute::nova-common' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['mq'] = { + 'host' => '127.0.0.1' + } + + runner.converge(described_recipe) end - it "doesn't run epel recipe" do - expect(@chef_run).to_not include_recipe 'yum::epel' + include_context 'compute_stubs' + + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package 'python-mysqldb' end - it "runs logging recipe if node attributes say to" do - expect(@chef_run).to include_recipe "openstack-common::logging" + it 'upgrades nova common package' do + expect(chef_run).to upgrade_package 'nova-common' end - it "doesn't run logging recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-compute::nova-common" - expect(chef_run).not_to include_recipe "openstack-common::logging" + it 'upgrades memcache python package' do + expect(chef_run).to upgrade_package 'python-memcache' end - it "can converge with quantum service type" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["compute"]["network"]["service_type"] = "quantum" - chef_run.converge "openstack-compute::nova-common" + it 'creates the /etc/nova directory' do + expect(chef_run).to create_directory('/etc/nova').with( + owner: 'nova', + group: 'nova', + mode: 0750 + ) end - it "installs nova common packages" do - expect(@chef_run).to upgrade_package "nova-common" - end - - it "installs memcache python packages" do - expect(@chef_run).to install_package "python-memcache" - end - - describe "/etc/nova" do + context 'with logging enabled' do before do - @dir = @chef_run.directory "/etc/nova" + node.set['openstack']['compute']['syslog']['use'] = true end - it "has proper owner" do - expect(@dir).to be_owned_by "nova", "nova" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" + it 'runs logging recipe if node attributes say to' do + expect(chef_run).to include_recipe 'openstack-common::logging' end end - describe "/etc/nova/rootwrap.d" do + context 'with logging disabled' do before do - @dir = @chef_run.directory "/etc/nova/rootwrap.d" + node.set['openstack']['compute']['syslog']['use'] = false end - it "has proper owner" do - expect(@dir).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" + it "doesn't run logging recipe" do + expect(chef_run).not_to include_recipe 'openstack-common::logging' end end - describe "nova.conf" do - before do - @file = @chef_run.template "/etc/nova/nova.conf" - # README(shep) need this to evaluate nova.conf.erb template - @chef_run.node.set['cpu'] = Hash.new() - @chef_run.node.set.cpu.total = "2" + describe 'nova.conf' do + let(:file) { chef_run.template('/etc/nova/nova.conf') } + + it 'creates the file' do + expect(chef_run).to create_template(file.name).with( + owner: 'nova', + group: 'nova', + mode: 0644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "nova", "nova" + it 'has default *_path options set' do + [%r{^log_dir=/var/log/nova$}, + %r{^state_path=/var/lib/nova$}, + %r{^instances_path=/var/lib/nova/instances$}, + %r{^lock_path=/var/lib/nova/lock$}].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'has an instance_name_template setting' do + expect(chef_run).to render_file(file.name).with_content( + /^instance_name_template=instance-%08x$/) end - it "has rabbit_user" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_userid=guest" + it 'has compute driver attributes defaults set' do + [/^compute_driver=libvirt.LibvirtDriver$/, + /^preallocate_images=none$/, + /^use_cow_images=true$/, + /^vif_plugging_is_fatal=true$/, + /^vif_plugging_timeout=300$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end end - it "has rabbit_password" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_password=rabbit-pass" + it 'does not have compute driver attribute default_ephemeral_format set by default' do + expect(chef_run).not_to render_file(file.name).with_content(/^default_ephemeral_format=$/) end - it "has rabbit_virtual_host" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_virtual_host=/" + it 'allows override for compute driver attribute default_ephemeral_format' do + node.set['openstack']['compute']['default_ephemeral_format'] = 'someformat' + expect(chef_run).to render_file(file.name).with_content(/^default_ephemeral_format=someformat$/) end - it "has rabbit_host" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_host=127.0.0.1" + it 'has default network_allocate_retries set' do + line = /^network_allocate_retries=0$/ + expect(chef_run).to render_file(file.name).with_content(line) end - it "does not have rabbit_hosts" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_hosts=" + it 'has default RPC/AMQP options set' do + [/^rpc_backend=nova.openstack.common.rpc.impl_kombu$/, + /^rpc_thread_pool_size=64$/, + /^rpc_conn_pool_size=30$/, + /^rpc_response_timeout=60$/, + /^amqp_durable_queues=false$/, + /^amqp_auto_delete=false$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end end - it "does not have rabbit_ha_queues" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_ha_queues=" + it 'has default compute ip and port options set' do + [/^osapi_compute_listen=127.0.0.1$/, + /^osapi_compute_listen_port=8774$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end end - it "has rabbit_port" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_port=5672" + it 'has default ec2 ip and port options set' do + [/^ec2_listen=127.0.0.1$/, + /^ec2_listen_port=8773$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end end - describe "rabbit ha" do - before do - @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| - n.set["openstack"]["compute"]["rabbit"]["ha"] = true - n.set["cpu"] = { - "total" => "2" - } + it 'confirms default min value for workers' do + [/^ec2_workers=/, + /^osapi_compute_workers=/, + /^metadata_workers=/, + /^workers=/].each do |line| + expect(chef_run).to_not render_file(file.name).with_content(line) + end + end + + describe 'allow overrides for workers' do + it 'has worker overrides' do + node.set['openstack']['compute']['ec2_workers'] = 123 + node.set['openstack']['compute']['osapi_compute_workers'] = 456 + node.set['openstack']['compute']['metadata_workers'] = 789 + node.set['openstack']['compute']['conductor']['workers'] = 321 + [/^ec2_workers=123$/, + /^osapi_compute_workers=456$/, + /^metadata_workers=789$/, + /^workers=321$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) end - @chef_run.converge "openstack-compute::nova-common" - end - - it "has rabbit_hosts" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_hosts=1.1.1.1:5672,2.2.2.2:5672" - end - - it "has rabbit_ha_queues" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_ha_queues=True" - end - - it "does not have rabbit_host" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_host=127.0.0.1" - end - - it "does not have rabbit_port" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_port=5672" end end - it "has vncserver_listen" do - expect(@chef_run).to create_file_with_content @file.name, - "vncserver_listen=127.0.1.1" + context 'rabbit mq backend' do + before do + node.set['openstack']['mq']['compute']['service_type'] = 'rabbitmq' + end + + describe 'ha rabbit disabled' do + before do + # README(galstrom21): There is a order of operations issue here + # if you use node.set, these tests will fail. + node.override['openstack']['mq']['compute']['rabbit']['ha'] = false + end + + it 'has default rabbit_* options set' do + [/^rabbit_userid=guest$/, /^rabbit_password=mq-pass$/, + /^rabbit_virtual_host=\/$/, /^rabbit_host=127.0.0.1$/, + /^rabbit_port=5672$/, /^rabbit_use_ssl=false$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'does not have ha rabbit options set' do + [/^rabbit_hosts=/, /^rabbit_ha_queues=/, + /^ec2_private_dns_show_ip$/].each do |line| + expect(chef_run).not_to render_file(file.name).with_content(line) + end + end + end + + describe 'ha rabbit enabled' do + before do + # README(galstrom21): There is a order of operations issue here + # if you use node.set, these tests will fail. + node.override['openstack']['mq']['compute']['rabbit']['ha'] = true + end + + it 'sets ha rabbit options correctly' do + [ + /^rabbit_hosts=1.1.1.1:5672,2.2.2.2:5672$/, + /^rabbit_ha_queues=True$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'does not have non-ha rabbit options set' do + [/^rabbit_host=127\.0\.0\.1$/, /^rabbit_port=5672$/].each do |line| + expect(chef_run).not_to render_file(file.name).with_content(line) + end + end + end end - it "has vncserver_proxyclient_address" do - expect(@chef_run).to create_file_with_content @file.name, - "vncserver_proxyclient_address=127.0.1.1" + context 'qpid mq backend' do + before do + # README(galstrom21): There is a order of operations issue here + # if you use node.set, these tests will fail. + node.override['openstack']['mq']['compute']['service_type'] = 'qpid' + node.override['openstack']['mq']['compute']['qpid']['username'] = 'guest' + end + + it 'has default qpid_* options set' do + [ + /^qpid_hostname=127.0.0.1$/, + /^qpid_port=5672$/, + /^qpid_username=guest$/, + /^qpid_password=mq-pass$/, + /^qpid_sasl_mechanisms=$/, + /^qpid_reconnect_timeout=0$/, + /^qpid_reconnect_limit=0$/, + /^qpid_reconnect_interval_min=0$/, + /^qpid_reconnect_interval_max=0$/, + /^qpid_reconnect_interval=0$/, + /^qpid_heartbeat=60$/, + /^qpid_protocol=tcp$/, + /^qpid_tcp_nodelay=true$/, + /^qpid_topology_version=1$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end end - it "has xvpvncproxy_host" do - expect(@chef_run).to create_file_with_content @file.name, - "xvpvncproxy_host=127.0.1.1" + it 'has default vncserver_* options set' do + node.set['openstack']['endpoints']['compute-vnc-bind']['bind_interface'] = 'lo' + + [/^vncserver_listen=127.0.1.1$/, + /^vncserver_proxyclient_address=127.0.1.1$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end end - it "has novncproxy_host" do - expect(@chef_run).to create_file_with_content @file.name, - "novncproxy_host=127.0.1.1" + it 'has default *vncproxy_* options set' do + [/^xvpvncproxy_host=127.0.0.1$/, + /^xvpvncproxy_port=6081$/, + /^novncproxy_host=127.0.0.1$/, + /^novncproxy_port=6080$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end end - it "has correct force_dhcp_release value" do - expect(@chef_run).to create_file_with_content @file.name, - "force_dhcp_release=true" + it 'has default nova.config options set' do + [/^allow_resize_to_same_host=false$/, + /^force_dhcp_release=true$/, + /^mkisofs_cmd=genisoimage$/, + %r{^injected_network_template=\$pybasedir/nova/virt/interfaces.template$}].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end end - it "has virtio enabled" do - expect(@chef_run).to create_file_with_content @file.name, - "libvirt_use_virtio_for_bridges=true" + it 'has a force_config_drive setting' do + chef_run.node.set['openstack']['compute']['config']['force_config_drive'] = 'always' + expect(chef_run).to render_file(file.name).with_content( + /^force_config_drive=always$/) end - it "does not have ec2_private_dns_show_ip option" do - expect(@chef_run).to_not create_file_with_content @file.name, - "ec2_private_dns_show_ip" + it 'has a os_region_name setting' do + chef_run.node.set['openstack']['node'] = 'RegionOne' + expect(chef_run).to render_file(file.name).with_content( + /^os_region_name=RegionOne$/) + end + + it 'has a disk_cachemodes setting' do + chef_run.node.set['openstack']['compute']['config']['disk_cachemodes'] = 'disk:writethrough' + expect(chef_run).to render_file(file.name).with_content( + /^disk_cachemodes=disk:writethrough$/) + end + + context 'metering' do + describe 'metering disabled' do + it 'leaves default audit options' do + ['instance_usage_audit=False', + 'instance_usage_audit_period=month'].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'does not configure metering notification' do + ['notification_driver', + 'notify_on_state_change'].each do |line| + expect(chef_run).not_to render_file(file.name).with_content(line) + end + end + end + + describe 'notification enabled' do + before do + node.override['openstack']['compute']['metering'] = true + end + + it 'sets audit and notification options correctly' do + ['notification_driver=nova.openstack.common.notifier.rpc_notifier', + 'notification_driver=ceilometer.compute.nova_notifier', + 'instance_usage_audit=True', + 'instance_usage_audit_period=hour', + 'notify_on_state_change=vm_and_task_state' + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + end + end + + context 'libvirt configuration' do + it 'has default libvirt_* options set' do + [/^use_virtio_for_bridges=true$/, + /^images_type=default$/, + /^inject_key=true$/, + /^inject_password=false$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it "the libvirt cpu_mode is none when virt_type is 'qemu'" do + node.set['openstack']['compute']['libvirt']['virt_type'] = 'qemu' + + expect(chef_run).to render_file(file.name).with_content( + 'cpu_mode=none') + end + + it 'has a configurable inject_key setting' do + node.set['openstack']['compute']['libvirt']['libvirt_inject_key'] = false + + expect(chef_run).to render_file(file.name).with_content( + /^inject_key=false$/) + end + + it 'has a configurable inject_password setting' do + node.set['openstack']['compute']['libvirt']['libvirt_inject_password'] = true + + expect(chef_run).to render_file(file.name).with_content( + /^inject_password=true$/) + end + end + + context 'vmware' do + before do + # README(galstrom21): There is a order of operations issue here + # if you use node.set, these tests will fail. + node.override['openstack']['compute']['driver'] = 'vmwareapi.VMwareVCDriver' + end + + it 'has default vmware config options set' do + [ + /^host_ip = $/, + /^host_username = $/, + /^host_password = vmware_secret_name$/, + /^task_poll_interval = 0.5$/, + /^api_retry_count = 10$/, + /^vnc_port = 5900$/, + /^vnc_port_total = 10000$/, + /^use_linked_clone = true$/, + /^vlan_interface = vmnic0$/, + /^maximum_objects = 100$/, + /^integration_bridge = br-int$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'has no datastore_regex line' do + expect(chef_run).not_to render_file(file.name).with_content('datastore_regex = ') + end + + it 'has no wsdl_location line' do + expect(chef_run).not_to render_file(file.name).with_content('wsdl_location = ') + end + end + + context 'vmware cluster name' do + before do + # README(galstrom21): There is a order of operations issue here + # if you use node.set, these tests will fail. + node.override['openstack']['compute']['driver'] = 'vmwareapi.VMwareVCDriver' + node.override['openstack']['compute']['vmware']['cluster_name'] = ['cluster1', 'cluster2'] + node.override['openstack']['compute']['vmware']['datastore_regex'] = '*.' + node.override['openstack']['compute']['vmware']['wsdl_location'] = 'http://127.0.0.1/' + end + + it 'has multiple cluster name lines' do + expect(chef_run).to render_file(file.name).with_content('cluster_name = cluster1') + expect(chef_run).to render_file(file.name).with_content('cluster_name = cluster2') + end + + it 'has datastore_regex line' do + expect(chef_run).to render_file(file.name).with_content('datastore_regex = *.') + end + + it 'has wsdl_location line' do + expect(chef_run).to render_file(file.name).with_content('wsdl_location = http://127.0.0.1/') + end + end + + it 'has disk_allocation_ratio when the right filter is set' do + node.set['openstack']['compute']['scheduler']['default_filters'] = %w( + AvailabilityZoneFilter + DiskFilter + RamFilter + ComputeFilter + CoreFilter + SameHostFilter + DifferentHostFilter + ) + expect(chef_run).to render_file(file.name).with_content( + 'disk_allocation_ratio=1.0') + end + + it 'has no auto_assign_floating_ip' do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + expect(chef_run).not_to render_file(file.name).with_content( + 'auto_assign_floating_ip=false') + end + + it 'templates misc_nova array correctly' do + node.set['openstack']['compute']['misc_nova'] = ['MISC_OPTION', 'FOO'] + expect(chef_run).to render_file(file.name).with_content( + 'MISC_OPTION') + end + + context 'rbd backend' do + before do + node.set['openstack']['compute']['libvirt']['images_type'] = 'rbd' + end + + describe 'default rdb settings' do + it 'sets the libvirt * options correctly' do + [ + /^images_type=rbd$/, + /^images_rbd_pool=rbd$/, + %r{^images_rbd_ceph_conf=/etc/ceph/ceph.conf$}, + /^rbd_user=cinder$/, + /^rbd_secret_uuid=00000000-0000-0000-0000-000000000000$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + end + + describe 'override rbd settings' do + before do + node.set['openstack']['compute']['libvirt']['images_type'] = 'rbd' + node.set['openstack']['compute']['libvirt']['images_rbd_pool'] = 'myrbd' + node.set['openstack']['compute']['libvirt']['images_rbd_ceph_conf'] = '/etc/myceph/ceph.conf' + end + + it 'sets the overridden libvirt options correctly' do + [ + /^images_type=rbd$/, + /^images_rbd_pool=myrbd$/, + %r{^images_rbd_ceph_conf=/etc/myceph/ceph.conf$} + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + end + end + + context 'lvm backend' do + before do + node.set['openstack']['compute']['libvirt']['images_type'] = 'lvm' + node.set['openstack']['compute']['libvirt']['volume_group'] = 'instances' + end + + it 'sets the lvm options correctly' do + [ + /^images_type=lvm$/, + /^images_volume_group=instances$/, + /^sparse_logical_volumes=false$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + describe 'override settings' do + before do + node.set['openstack']['compute']['libvirt']['images_type'] = 'lvm' + node.set['openstack']['compute']['libvirt']['volume_group'] = 'instances' + node.set['openstack']['compute']['libvirt']['sparse_logical_volumes'] = true + end + + it 'sets the overridden lvm options correctly' do + [ + /^images_type=lvm$/, + /^images_volume_group=instances$/, + /^sparse_logical_volumes=true$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + end end end + describe 'rootwrap.conf' do + let(:file) { chef_run.template('/etc/nova/rootwrap.conf') } -# describe "identity role local node" do -# before do -# @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| -# n.set["openstack"]["identity"]["admin_tenant_name"] = "admin-tenant" -# n.set["openstack"]["identity"]["admin_user"] = "admin-user" -# end -# @chef_run.converge 'role[os-identity]', "openstack-compute::nova-common" -# end -# it "has keystone_hash" do -# expect(@chef_run).to log 'openstack-compute::nova-common:keystone|node[???]' -# end -# it "has ksadmin_user" do -# expect(@chef_run).to log 'openstack-compute::nova-common:ksadmin_user|admin-user' -# end -# it "has ksadmin_tenant_name" do -# expect(@chef_run).to log 'openstack-compute::nova-common:ksadmin_tenant_name|admin-tenant' -# end -# end - - -# describe "identity role search" do -# before do -# @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| -# n.set["openstack"]["compute"]["identity_service_chef_role"] = "os-identity" -# end -# @chef_run.converge "openstack-compute::nova-common" -# end -# it "has keystone_hash" do -# expect(@chef_run).to log 'openstack-compute::nova-common:keystone|node[???]' -# end -# it "has ksadmin_user" do -# expect(@chef_run).to log 'openstack-compute::nova-common:ksadmin_user|admin-user' -# end -# it "has ksadmin_tenant_name" do -# expect(@chef_run).to log 'openstack-compute::nova-common:ksadmin_tenant_name|admin-tenant' -# end -# end - - describe "rootwrap.conf" do - before do - @file = @chef_run.template "/etc/nova/rootwrap.conf" + it 'creates the /etc/nova/rootwrap.conf file' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 0644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end + context 'template contents' do + it 'shows the custom banner' do + node.set['openstack']['compute']['custom_template_banner'] = 'banner' - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end + expect(chef_run).to render_file(file.name).with_content(/^banner$/) + end - it "template contents" do - pending "TODO: implement" + it 'sets the default attributes' do + [ + %r(^filters_path=/etc/nova/rootwrap.d,/usr/share/nova/rootwrap$), + %r(^exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin$), + /^use_syslog=False$/, + /^syslog_log_facility=syslog$/, + /^syslog_log_level=ERROR$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end end end - describe "api-metadata.filters" do - before do - @file = @chef_run.template "/etc/nova/rootwrap.d/api-metadata.filters" - end - - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - end - - describe "compute.filters" do - before do - @file = @chef_run.template "/etc/nova/rootwrap.d/compute.filters" - end - - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - end - - describe "network.filters" do - before do - @file = @chef_run.template "/etc/nova/rootwrap.d/network.filters" - end - - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - end - - describe "openrc" do - before do - @file = @chef_run.template "/root/openrc" - end - - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "600" - end - - it "contains ksadmin_user" do - expect(@chef_run).to create_file_with_content @file.name, - "export OS_USERNAME=admin-user" - end - - it "contains ksadmin_tenant_name" do - expect(@chef_run).to create_file_with_content @file.name, - "export OS_TENANT_NAME=admin-tenant" - end - - it "contains ksadmin_pass" do - expect(@chef_run).to create_file_with_content @file.name, - "export OS_PASSWORD=admin-pass" - end - - it "rest of template contents" do - pending "TODO: implement" - end - end - - it "enables nova login" do - cmd = "usermod -s /bin/sh nova" - expect(@chef_run).to execute_command cmd + it 'enables nova login' do + expect(chef_run).to run_execute('usermod -s /bin/sh nova') end end end diff --git a/chef/cookbooks/openstack-compute/spec/nova-setup_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-setup_spec.rb index 01c88a1..1359428 100644 --- a/chef/cookbooks/openstack-compute/spec/nova-setup_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/nova-setup_spec.rb @@ -1,71 +1,75 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::nova-setup" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::nova-setup" +require_relative 'spec_helper' + +describe 'openstack-compute::nova-setup' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + + it 'runs db migrations' do + expect(chef_run).to run_execute('nova-manage db sync').with(user: 'nova', group: 'nova') end - expect_runs_nova_common_recipe - - it "runs db migrations" do - cmd = "nova-manage db sync" - expect(@chef_run).to execute_command cmd + it 'adds nova network ipv4 addresses' do + cmd = ['nova-manage network create --label=public', + '--fixed_range_v4=192.168.100.0/24', + "--multi_host='T'", + '--num_networks=1', + '--network_size=255', + '--bridge=br100', + '--dns1=8.8.8.8', + '--dns2=8.8.4.4', + '--bridge_interface=eth2'].join(' ') + expect(chef_run).to run_execute(cmd).with(user: 'nova', group: 'nova') end - it "adds nova network ipv4 addresses" do - cmd = ["nova-manage network create --label=public", - "--fixed_range_v4=192.168.100.0/24", - "--multi_host='T'", - "--num_networks=1", - "--network_size=255", - "--bridge=br100", - "--bridge_interface=eth2", - "--dns1=8.8.8.8", - "--dns2=8.8.4.4"].join(' ') - expect(@chef_run).to execute_command cmd + it 'add_floaters.py has proper modes' do + file = chef_run.cookbook_file('/usr/local/bin/add_floaters.py') + expect(sprintf('%o', file.mode)).to eq '755' end - it "add_floaters.py has proper modes" do - file = @chef_run.cookbook_file "/usr/local/bin/add_floaters.py" - expect(sprintf("%o", file.mode)).to eq "755" + it 'adds cidr range of floating ipv4 addresses' do + node.set['openstack']['compute']['network']['floating']['ipv4_cidr'] = '10.10.10.0/24' + + expect(chef_run).to run_execute( + '/usr/local/bin/add_floaters.py nova --cidr=10.10.10.0/24') end - it "adds cidr range of floating ipv4 addresses" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["compute"]["network"]["floating"]["ipv4_cidr"] = "10.10.10.0/24" - chef_run.converge "openstack-compute::nova-setup" - - cmd = "/usr/local/bin/add_floaters.py nova --cidr=10.10.10.0/24" - expect(chef_run).to execute_command cmd - end - - it "adds range of floating ipv4 addresses" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["compute"]["network"] = { - "floating" => { - "ipv4_range" => "10.10.10.1,10.10.10.5" + it 'adds range of floating ipv4 addresses' do + node.set['openstack']['compute']['network'] = { + 'floating' => { + 'ipv4_range' => '10.10.10.1,10.10.10.5' } } - chef_run.converge "openstack-compute::nova-setup" - cmd = "/usr/local/bin/add_floaters.py nova --ip-range=10.10.10.1,10.10.10.5" - expect(chef_run).to execute_command cmd + expect(chef_run).to run_execute('/usr/local/bin/add_floaters.py nova --ip-range=10.10.10.1,10.10.10.5') end - it "adds cidr range of floating ipv4 addresses to neutron" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["compute"]["network"]["service_type"] = "neutron" - node.set["openstack"]["compute"]["network"]["floating"]["ipv4_cidr"] = "10.10.10.0/24" - node.set["openstack"]["compute"]["network"]["floating"]["public_network_name"] = "public" - chef_run.converge "openstack-compute::nova-setup" - cmd = ". /root/openrc && /usr/local/bin/add_floaters.py neutron --cidr=10.10.10.0/24 --pool=public" - expect(chef_run).to execute_command cmd + context 'when neutron is used' do + before do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['compute']['network']['floating']['ipv4_cidr'] = '10.10.10.0/24' + node.set['openstack']['compute']['network']['floating']['public_network_name'] = 'public' + end + + it 'upgrades the neutron python packages' do + expect(chef_run).to upgrade_package('python-neutronclient') + expect(chef_run).to upgrade_package('python-pyparsing') + end + + it 'include common openrc recipe' do + expect(chef_run).to include_recipe('openstack-common::openrc') + end + + it 'adds cidr range of floating ipv4 addresses to neutron' do + resource = chef_run.find_resource('execute', 'neutron floating create').to_hash + expect(resource).to include(action: [:run], command: '. /root/openrc && /usr/local/bin/add_floaters.py neutron --cidr=10.10.10.0/24 --pool=public') + end end end end diff --git a/chef/cookbooks/openstack-compute/spec/scheduler-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/scheduler-redhat_spec.rb index a66ec01..4a48676 100644 --- a/chef/cookbooks/openstack-compute/spec/scheduler-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/scheduler-redhat_spec.rb @@ -1,24 +1,25 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::scheduler" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::scheduler" +require_relative 'spec_helper' + +describe 'openstack-compute::scheduler' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades nova scheduler package' do + expect(chef_run).to upgrade_package('openstack-nova-scheduler') end - it "installs nova scheduler packages" do - expect(@chef_run).to upgrade_package "openstack-nova-scheduler" + it 'starts nova scheduler' do + expect(chef_run).to start_service('openstack-nova-scheduler') end - it "starts nova scheduler" do - expect(@chef_run).to start_service "openstack-nova-scheduler" - end - - it "starts nova scheduler on boot" do - expected = "openstack-nova-scheduler" - expect(@chef_run).to set_service_to_start_on_boot expected + it 'starts nova scheduler on boot' do + expect(chef_run).to enable_service('openstack-nova-scheduler') end end end diff --git a/chef/cookbooks/openstack-compute/spec/scheduler_spec.rb b/chef/cookbooks/openstack-compute/spec/scheduler_spec.rb index bd1b79c..4f3864a 100644 --- a/chef/cookbooks/openstack-compute/spec/scheduler_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/scheduler_spec.rb @@ -1,27 +1,27 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::scheduler" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::scheduler" +require_relative 'spec_helper' + +describe 'openstack-compute::scheduler' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + include_examples 'expect_creates_nova_lock_dir' + + it 'upgrades nova scheduler package' do + expect(chef_run).to upgrade_package('nova-scheduler') end - expect_runs_nova_common_recipe - - expect_creates_nova_lock_dir - - it "installs nova scheduler packages" do - expect(@chef_run).to upgrade_package "nova-scheduler" + it 'starts nova scheduler' do + expect(chef_run).to start_service('nova-scheduler') end - it "starts nova scheduler" do - expect(@chef_run).to start_service "nova-scheduler" - end - - it "starts nova scheduler on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-scheduler" + it 'starts nova scheduler on boot' do + expect(chef_run).to enable_service('nova-scheduler') end end end diff --git a/chef/cookbooks/openstack-compute/spec/spec_helper.rb b/chef/cookbooks/openstack-compute/spec/spec_helper.rb index e75a2aa..49b728e 100644 --- a/chef/cookbooks/openstack-compute/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-compute/spec/spec_helper.rb @@ -1,111 +1,179 @@ -require "chefspec" +# encoding: UTF-8 -::LOG_LEVEL = :fatal -::OPENSUSE_OPTS = { - :platform => "opensuse", - :version => "12.3", - :log_level => ::LOG_LEVEL +require 'chefspec' +require 'chefspec/berkshelf' + +ChefSpec::Coverage.start! { add_filter 'openstack-compute' } + +require 'chef/application' +require 'securerandom' + +LOG_LEVEL = :fatal +SUSE_OPTS = { + platform: 'suse', + version: '11.03', + log_level: LOG_LEVEL } -::REDHAT_OPTS = { - :platform => "redhat", - :version => "6.3", - :log_level => ::LOG_LEVEL +REDHAT_OPTS = { + platform: 'redhat', + version: '6.3', + log_level: LOG_LEVEL } -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } -def compute_stubs - ::Chef::Recipe.any_instance.stub(:rabbit_servers). - and_return "1.1.1.1:5672,2.2.2.2:5672" - ::Chef::Recipe.any_instance.stub(:address_for). - with("lo"). - and_return "127.0.1.1" - ::Chef::Recipe.any_instance.stub(:search_for). - with("os-identity").and_return( - [{ - 'openstack' => { - 'identity' => { - 'admin_tenant_name' => 'admin-tenant', - 'admin_user' => 'admin-user' +shared_context 'compute_stubs' do + before do + Chef::Recipe.any_instance.stub(:rabbit_servers) + .and_return '1.1.1.1:5672,2.2.2.2:5672' + Chef::Recipe.any_instance.stub(:address_for) + .with('lo') + .and_return '127.0.1.1' + Chef::Recipe.any_instance.stub(:search_for) + .with('os-identity').and_return( + [{ + 'openstack' => { + 'identity' => { + 'admin_tenant_name' => 'admin', + 'admin_user' => 'admin' + } } - } - }] + }] + ) + Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_identity_bootstrap_token') + .and_return('bootstrap-token') + Chef::Recipe.any_instance.stub(:get_secret) + .with('neutron_metadata_secret') + .and_return('metadata-secret') + Chef::Recipe.any_instance.stub(:get_secret) # this is the rbd_uuid default name + .with('rbd_secret_uuid') + .and_return '00000000-0000-0000-0000-000000000000' + Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_vmware_secret_name') + .and_return 'vmware_secret_name' + Chef::Recipe.any_instance.stub(:get_password) + .with('db', anything) + .and_return('') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'guest') + .and_return('mq-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'admin') + .and_return('admin') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack-compute') + .and_return('nova-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack-network') + .and_return('neutron-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'rbd_block_storage') + .and_return 'cinder-rbd-pass' + Chef::Recipe.any_instance.stub(:memcached_servers).and_return [] + Chef::Recipe.any_instance.stub(:system) + .with("grub2-set-default 'openSUSE GNU/Linux, with Xen hypervisor'") + .and_return(true) + Chef::Application.stub(:fatal!) + SecureRandom.stub(:hex) { 'ad3313264ea51d8c6a3d1c5b140b9883' } + stub_command('nova-manage network list | grep 192.168.100.0/24').and_return(false) + stub_command('nova-manage network list | grep 192.168.200.0/24').and_return(false) + stub_command("nova-manage floating list |grep -E '.*([0-9]{1,3}[.]){3}[0-9]{1,3}*'").and_return(false) + stub_command('virsh net-list | grep -q default').and_return(true) + stub_command('ovs-vsctl br-exists br-int').and_return(true) + stub_command('ovs-vsctl br-exists br-tun').and_return(true) + stub_command('virsh secret-list | grep 00000000-0000-0000-0000-000000000000').and_return(false) + stub_command("virsh secret-get-value 00000000-0000-0000-0000-000000000000 | grep 'cinder-rbd-pass'").and_return(false) + end +end + +shared_examples 'expect_runs_nova_common_recipe' do + it 'includes nova-common' do + expect(chef_run).to include_recipe 'openstack-compute::nova-common' + end +end + +shared_examples 'expect_upgrades_python_keystoneclient' do + it 'upgrades python-keystoneclient' do + expect(chef_run).to upgrade_package 'python-keystoneclient' + end +end + +shared_examples 'expect_creates_nova_lock_dir' do + it 'creates the /var/lock/nova directory' do + expect(chef_run).to create_directory('/var/lock/nova').with( + user: 'nova', + group: 'nova', + mode: 0700 ) - ::Chef::Recipe.any_instance.stub(:secret). - with("secrets", "openstack_identity_bootstrap_token"). - and_return "bootstrap-token" - ::Chef::Recipe.any_instance.stub(:secret). - with("secrets", "quantum_metadata_secret"). - and_return "metadata-secret" - ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:user_password). - with("guest"). - and_return "rabbit-pass" - ::Chef::Recipe.any_instance.stub(:user_password). - with("admin-user"). - and_return "admin-pass" - ::Chef::Recipe.any_instance.stub(:service_password).with("openstack-compute"). - and_return "nova-pass" - ::Chef::Recipe.any_instance.stub(:service_password).with("openstack-network"). - and_return "quantum-pass" - ::Chef::Recipe.any_instance.stub(:memcached_servers).and_return [] - ::Chef::Recipe.any_instance.stub(:system). - with("grub2-set-default 'openSUSE GNU/Linux, with Xen hypervisor'"). - and_return true -end - -def expect_runs_nova_common_recipe - it "installs nova-common" do - expect(@chef_run).to include_recipe "openstack-compute::nova-common" end end -def expect_installs_python_keystone - it "installs python-keystone" do - expect(@chef_run).to upgrade_package "python-keystone" - end -end - -def expect_creates_nova_lock_dir - describe "/var/lock/nova" do - before do - @dir = @chef_run.directory "/var/lock/nova" +def expect_creates_api_paste(service, action = :restart) # rubocop:disable MethodLength + describe '/etc/nova/api-paste.ini' do + let(:file) { chef_run.template('/etc/nova/api-paste.ini') } + it 'creates api-paste.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'nova', + group: 'nova', + mode: 0644 + ) end - it "has proper owner" do - expect(@dir).to be_owned_by "nova", "nova" + describe 'keystone_authtoken' do + it 'has correct auth_token settings' do + [ + 'auth_uri = http://127.0.0.1:5000/v2.0', + 'auth_host = 127.0.0.1', + 'auth_port = 35357', + 'auth_protocol = http', + 'auth_version = v2.0', + 'admin_tenant_name = service', + 'admin_user = nova', + 'admin_password = nova-pass', + 'signing_dir = /var/cache/nova/api' + ].each do |line| + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote(line)}$/) + end + end end - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" - end - end -end - -def expect_creates_api_paste service, action=:restart - describe "api-paste.ini" do - before do - @file = @chef_run.template "/etc/nova/api-paste.ini" - end - - it "has proper owner" do - expect(@file).to be_owned_by "nova", "nova" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - - it "notifies nova-api-ec2 restart" do - expect(@file).to notify service, action + context 'template contents' do + context 'ec2 enabled' do + before do + node.set['openstack']['compute']['enabled_apis'] = %w(ec2) + end + + it 'sets the pipeline attribute' do + expect(chef_run).to render_file(file.name) + .with_content(/^pipeline = ec2faultwrap logrequest metaapp$/) + end + + it 'sets ec2 attributes' do + expect(chef_run).to render_file(file.name) + .with_content(/^\[composite:ec2\]$/) + end + end + + it 'sets the pipeline attribute when ec2 api is disabled' do + node.set['openstack']['compute']['enabled_apis'] = [] + expect(chef_run).to render_file(file.name) + .with_content(/^pipeline = faultwrap metaapp$/) + end + + it 'pastes the misc attributes' do + node.set['openstack']['compute']['misc_paste'] = %w(paste1 paste2) + expect(chef_run).to render_file(file.name) + .with_content(/^paste1$/).with_content(/^paste2$/) + end + end + + it 'notifies #{service} #{action}' do + expect(file).to notify(service).to(action) end end end diff --git a/chef/cookbooks/openstack-compute/spec/vncproxy-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/vncproxy-redhat_spec.rb index dcffe42..1bce3dd 100644 --- a/chef/cookbooks/openstack-compute/spec/vncproxy-redhat_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/vncproxy-redhat_spec.rb @@ -1,25 +1,37 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::vncproxy" do - before { compute_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-compute::vncproxy" +require_relative 'spec_helper' + +describe 'openstack-compute::vncproxy' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + + it 'upgrades nova vncproxy package' do + expect(chef_run).to upgrade_package('openstack-nova-novncproxy') end - it "starts nova vncproxy on boot" do - expected = "openstack-nova-novncproxy" - expect(@chef_run).to set_service_to_start_on_boot expected + it 'upgrades nova consoleauth package' do + expect(chef_run).to upgrade_package('openstack-nova-console') end - it "starts nova consoleauth" do - expect(@chef_run).to start_service "openstack-nova-console" + it 'starts nova vncproxy' do + expect(chef_run).to start_service('openstack-nova-novncproxy') end - it "starts nova consoleauth on boot" do - expected = "openstack-nova-console" - expect(@chef_run).to set_service_to_start_on_boot expected + it 'starts nova vncproxy on boot' do + expect(chef_run).to enable_service('openstack-nova-novncproxy') + end + + it 'starts nova consoleauth' do + expect(chef_run).to start_service('openstack-nova-consoleauth') + end + + it 'starts nova consoleauth on boot' do + expect(chef_run).to enable_service('openstack-nova-consoleauth') end end end diff --git a/chef/cookbooks/openstack-compute/spec/vncproxy_spec.rb b/chef/cookbooks/openstack-compute/spec/vncproxy_spec.rb index c800b46..d597384 100644 --- a/chef/cookbooks/openstack-compute/spec/vncproxy_spec.rb +++ b/chef/cookbooks/openstack-compute/spec/vncproxy_spec.rb @@ -1,35 +1,40 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe "openstack-compute::vncproxy" do - before { compute_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-compute::vncproxy" +require_relative 'spec_helper' + +describe 'openstack-compute::vncproxy' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'compute_stubs' + include_examples 'expect_runs_nova_common_recipe' + + it 'upgrades nova vncproxy packages' do + expect(chef_run).to upgrade_package('novnc') + expect(chef_run).to upgrade_package('websockify') + expect(chef_run).to upgrade_package('nova-novncproxy') end - expect_runs_nova_common_recipe - - it "installs vncproxy packages" do - expect(@chef_run).to upgrade_package "novnc" - expect(@chef_run).to upgrade_package "websockify" - expect(@chef_run).to upgrade_package "nova-novncproxy" + it 'upgrades nova consoleauth package' do + expect(chef_run).to upgrade_package('nova-consoleauth') end - it "installs consoleauth packages" do - expect(@chef_run).to upgrade_package "nova-consoleauth" + it 'starts nova vncproxy' do + expect(chef_run).to start_service('nova-novncproxy') end - it "starts nova vncproxy on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-novncproxy" + it 'starts nova vncproxy on boot' do + expect(chef_run).to enable_service('nova-novncproxy') end - it "starts nova consoleauth" do - expect(@chef_run).to start_service "nova-consoleauth" + it 'starts nova consoleauth' do + expect(chef_run).to start_service('nova-consoleauth') end - it "starts nova consoleauth on boot" do - expect(@chef_run).to set_service_to_start_on_boot "nova-consoleauth" + it 'starts nova consoleauth on boot' do + expect(chef_run).to enable_service('nova-consoleauth') end end end diff --git a/chef/cookbooks/openstack-compute/templates/default/api-paste.ini.erb b/chef/cookbooks/openstack-compute/templates/default/api-paste.ini.erb index 7056248..20c9f7f 100644 --- a/chef/cookbooks/openstack-compute/templates/default/api-paste.ini.erb +++ b/chef/cookbooks/openstack-compute/templates/default/api-paste.ini.erb @@ -8,11 +8,16 @@ use = egg:Paste#urlmap /: meta [pipeline:meta] +<% if node["openstack"]["compute"]["enabled_apis"].include?("ec2") %> pipeline = ec2faultwrap logrequest metaapp +<% else -%> +pipeline = faultwrap metaapp +<% end -%> [app:metaapp] paste.app_factory = nova.api.metadata.handler:MetadataRequestHandler.factory +<% if node["openstack"]["compute"]["enabled_apis"].include?("ec2") %> ####### # EC2 # ####### @@ -53,6 +58,7 @@ paste.filter_factory = nova.api.ec2:Validator.factory [app:ec2executor] paste.app_factory = nova.api.ec2:Executor.factory +<% end -%> ############# # Openstack # @@ -107,7 +113,17 @@ auth_host = <%= @identity_admin_endpoint.host %> auth_port = <%= @identity_admin_endpoint.port %> auth_protocol = <%= @identity_admin_endpoint.scheme %> auth_version = <%= node["openstack"]["compute"]["api"]["auth"]["version"] %> -admin_tenant_name = <%= @service_tenant_name %> -admin_user = <%= @service_user %> +admin_tenant_name = <%= node["openstack"]["compute"]["service_tenant_name"] %> +admin_user = <%= node["openstack"]["compute"]["service_user"] %> admin_password = <%= @service_pass %> signing_dir = <%= node["openstack"]["compute"]["api"]["auth"]["cache_dir"] %> + +<% if node["openstack"]["compute"]["misc_paste"] %> +######## +# Misc # +######## + +<% node["openstack"]["compute"]["misc_paste"].each do |m| %> +<%= m %> +<% end %> +<% end %> diff --git a/chef/cookbooks/openstack-compute/templates/default/libvirtd.conf.erb b/chef/cookbooks/openstack-compute/templates/default/libvirtd.conf.erb index ca82e4a..c49a40b 100644 --- a/chef/cookbooks/openstack-compute/templates/default/libvirtd.conf.erb +++ b/chef/cookbooks/openstack-compute/templates/default/libvirtd.conf.erb @@ -248,7 +248,7 @@ auth_tcp = "<%= @auth_tcp %>" # The maximum number of concurrent client connections to allow # over all sockets combined. -#max_clients = 20 +max_clients = <%= node['openstack']['compute']['libvirt']['max_clients'] %> # The minimum limit sets the number of workers to start up @@ -257,7 +257,7 @@ auth_tcp = "<%= @auth_tcp %>" # Typically you'd want max_workers to equal maximum number # of clients allowed #min_workers = 5 -#max_workers = 20 +max_workers = <%= node['openstack']['compute']['libvirt']['max_workers'] %> # The number of priority workers. If all workers from above @@ -273,13 +273,13 @@ auth_tcp = "<%= @auth_tcp %>" # # XXX this isn't actually enforced yet, only the per-client # limit is used so far -#max_requests = 20 +max_requests = <%= node['openstack']['compute']['libvirt']['max_requests'] %> # Limit on concurrent requests from a single client # connection. To avoid one client monopolizing the server # this should be a small fraction of the global max_requests # and max_workers parameter -#max_client_requests = 5 +max_client_requests = <%= node['openstack']['compute']['libvirt']['max_client_requests'] %> ################################################################# # @@ -367,7 +367,9 @@ auth_tcp = "<%= @auth_tcp %>" # NB This default all-zeros UUID will not work. Replace # it with the output of the 'uuidgen' command and then # uncomment this entry -#host_uuid = "00000000-0000-0000-0000-000000000000" +<% unless node['openstack']['compute']['libvirt']['host_uuid'].nil? %> +host_uuid = "<%= node['openstack']['compute']['libvirt']['host_uuid'] %>" +<% end %> ################################################################### # Keepalive protocol: diff --git a/chef/cookbooks/openstack-compute/templates/default/nova.conf.erb b/chef/cookbooks/openstack-compute/templates/default/nova.conf.erb index 6220d20..1827c4a 100644 --- a/chef/cookbooks/openstack-compute/templates/default/nova.conf.erb +++ b/chef/cookbooks/openstack-compute/templates/default/nova.conf.erb @@ -1,37 +1,78 @@ <%= node["openstack"]["compute"]["custom_template_banner"] %> [DEFAULT] + # LOGS/STATE debug=<%= node["openstack"]["compute"]["debug"] %> verbose=<%= node["openstack"]["compute"]["verbose"] %> auth_strategy=<%= node["openstack"]["compute"]["api"]["auth_strategy"] %> dhcpbridge_flagfile=/etc/nova/nova.conf dhcpbridge=/usr/bin/nova-dhcpbridge -logdir=/var/log/nova +log_dir=<%= node["openstack"]["compute"]["log_dir"] %> <% if node["openstack"]["compute"]["syslog"]["use"] %> log_config = /etc/openstack/logging.conf <% end %> -state_path=/var/lib/nova -lock_path=/var/lock/nova +state_path=<%= node["openstack"]["compute"]["state_path"] %> +instances_path=<%= node["openstack"]["compute"]["instances_path"] %> +instance_name_template=<%= node["openstack"]["compute"]["instance_name_template"] %> +network_allocate_retries=<%= node['openstack']['compute']['network_allocate_retries'] %> +lock_path=<%= node["openstack"]["compute"]["lock_path"] %> +# Command prefix to use for running commands as root (default: sudo) +rootwrap_config=/etc/nova/rootwrap.conf + +# Should unused base images be removed? (default: false) +remove_unused_base_images=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_base_images"] %> + +# Unused unresized base images younger than this will not be removed (default: 86400) +remove_unused_original_minimum_age_seconds=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_original_minimum_age_seconds"] %> + +# Options defined in nova.openstack.common.rpc +# Seconds to wait before a cast expires (TTL). Only supported +# # by impl_zmq. (integer value) +rpc_cast_timeout=<%= node['openstack']['compute']['rpc_cast_timeout'] %> +# # Seconds to wait for a response from a call. (integer value) +rpc_response_timeout=<%= node['openstack']['compute']['rpc_response_timeout'] %> +rpc_thread_pool_size=<%= node["openstack"]["compute"]["rpc_thread_pool_size"] %> +rpc_conn_pool_size=<%= node["openstack"]["compute"]["rpc_conn_pool_size"] %> +rpc_backend=<%= node["openstack"]["compute"]["rpc_backend"] %> +amqp_durable_queues=<%= node['openstack']['mq']['compute']['durable_queues'] %> +amqp_auto_delete=<%= node['openstack']['mq']['compute']['auto_delete'] %> + +<% if @mq_service_type == "rabbitmq" %> ##### RABBITMQ ##### -rabbit_userid=<%= node['openstack']['mq']['user'] %> -rabbit_password=<%= node['openstack']['mq']['password'] %> -# rabbit_virtual_host=<%= node["openstack"]["compute"]["rabbit"]["vhost"] %> -<% if node["openstack"]["compute"]["rabbit"]["ha"] -%> +rabbit_userid=<%= node["openstack"]["mq"]["compute"]["rabbit"]["userid"] %> +rabbit_password=<%= @mq_password %> +rabbit_virtual_host=<%= node["openstack"]["mq"]["compute"]["rabbit"]["vhost"] %> +<% if node["openstack"]["mq"]["compute"]["rabbit"]["ha"] -%> rabbit_hosts=<%= @rabbit_hosts %> rabbit_ha_queues=True <% else -%> -rabbit_host=<%= node['openstack']['mq']['bind_address'] %> -rabbit_port=<%= node['openstack']['mq']['port'] %> +rabbit_host=<%= node["openstack"]["mq"]["compute"]["rabbit"]["host"] %> +rabbit_port=<%= node["openstack"]["mq"]["compute"]["rabbit"]["port"] %> +rabbit_use_ssl=<%= node["openstack"]["mq"]["compute"]["rabbit"]["use_ssl"] %> <% end -%> -# Seconds to wait before a cast expires (TTL). Only supported -# by impl_zmq. (integer value) -rpc_cast_timeout=300 -# Seconds to wait for a response from a call. (integer value) -rpc_response_timeout=300 -# Size of RPC connection pool. (integer value) -rpc_conn_pool_size=100 +<% end -%> + +<% if @mq_service_type == "qpid" %> +##### QPID ##### +qpid_hostname=<%= node["openstack"]["mq"]["compute"]["qpid"]["host"] %> +qpid_port=<%= node["openstack"]["mq"]["compute"]["qpid"]["port"] %> +qpid_password=<%= @mq_password %> +qpid_username=<%= node["openstack"]["mq"]["compute"]["qpid"]["username"] %> +qpid_sasl_mechanisms=<%= node["openstack"]["mq"]["compute"]["qpid"]["sasl_mechanisms"] %> +qpid_reconnect_timeout=<%= node["openstack"]["mq"]["compute"]["qpid"]["reconnect_timeout"] %> +qpid_reconnect_limit=<%= node["openstack"]["mq"]["compute"]["qpid"]["reconnect_limit"] %> +qpid_reconnect_interval_min=<%= node["openstack"]["mq"]["compute"]["qpid"]["reconnect_interval_min"] %> +qpid_reconnect_interval_max=<%= node["openstack"]["mq"]["compute"]["qpid"]["reconnect_interval_max"] %> +qpid_reconnect_interval=<%= node["openstack"]["mq"]["compute"]["qpid"]["reconnect_interval"] %> +qpid_heartbeat=<%= node["openstack"]["mq"]["compute"]["qpid"]["heartbeat"] %> +# qpid protocol. default 'tcp'. set to 'ssl' to enable SSL +qpid_protocol=<%= node["openstack"]["mq"]["compute"]["qpid"]["protocol"] %> +qpid_tcp_nodelay=<%= node["openstack"]["mq"]["compute"]["qpid"]["tcp_nodelay"] %> +qpid_topology_version=<%= node['openstack']['mq']['compute']['qpid']['topology_version'] %> +<% end %> + ##### SCHEDULER ##### # scheduler_manager=nova.scheduler.manager.SchedulerManager @@ -42,95 +83,121 @@ scheduler_default_filters=<%= @scheduler_default_filters %> default_availability_zone=<%= node["openstack"]["compute"]["config"]["availability_zone"] %> default_schedule_zone=<%= node["openstack"]["compute"]["config"]["default_schedule_zone"] %> storage_availability_zone=<%= node["openstack"]["compute"]["config"]["storage_availability_zone"] %> +sql_connection=<%= @sql_connection %> +sql_max_pool_size=100 +sql_max_overflow=100 +# New instances will be scheduled on a host chosen randomly +# from a subset of the N best hosts. This property defines the +# subset size that a host is chosen from. A value of 1 chooses +# the first host returned by the weighing functions. This +# value must be at least 1. Any value less than 1 will be +# ignored, and 1 will be used instead (integer value) +scheduler_host_subset_size=<%= node['openstack']['compute']['scheduler']['host_subset_size'] %> ##### NETWORK ##### <% case node["openstack"]["compute"]["network"]["service_type"] -when "quantum" -%> +when "neutron" -%> + # N.B. due to https://bugs.launchpad.net/nova/+bug/1206330 # we override the endpoint scheme below, ignore the port # and essentially force http <% if @network_endpoint.port == 443 -%> -quantum_url=http://<%= @network_endpoint.host %>:80 +neutron_url=http://<%= @network_endpoint.host %>:80 <% else -%> -quantum_url=http://<%= @network_endpoint.host %>:<%= @network_endpoint.port %> +neutron_url=http://<%= @network_endpoint.host %>:<%= @network_endpoint.port %> <% end -%> -network_api_class=<%= node["openstack"]["compute"]["network"]["quantum"]["network_api_class"] %> -quantum_auth_strategy=<%= node["openstack"]["compute"]["network"]["quantum"]["auth_strategy"] %> -quantum_admin_tenant_name=<%= node['openstack']['identity']['network']['tenant'] %> -quantum_admin_username=<%= node['openstack']['identity']['network']['username'] %> -quantum_admin_password=<%= node['openstack']['identity']['network']['password'] %> -quantum_admin_auth_url=<%= @identity_endpoint.to_s %> -libvirt_vif_driver=<%= node["openstack"]["compute"]["network"]["quantum"]["libvirt_vif_driver"] %> -linuxnet_interface_driver=<%= node["openstack"]["compute"]["network"]["quantum"]["linuxnet_interface_driver"] %> +neutron_url_timeout=<%= node["openstack"]["compute"]["network"]["neutron"]["url_timeout"] %> +network_api_class=<%= node["openstack"]["compute"]["network"]["neutron"]["network_api_class"] %> +neutron_auth_strategy=<%= node["openstack"]["compute"]["network"]["neutron"]["auth_strategy"] %> +neutron_admin_tenant_name=<%= node["openstack"]["compute"]["network"]["neutron"]["admin_tenant_name"] %> +neutron_admin_username=<%= node["openstack"]["compute"]["network"]["neutron"]["admin_username"] %> +neutron_admin_password=<%= @neutron_admin_password %> +neutron_admin_auth_url=<%= @identity_endpoint.to_s %> +linuxnet_interface_driver=<%= node["openstack"]["compute"]["network"]["neutron"]["linuxnet_interface_driver"] %> firewall_driver = nova.virt.firewall.NoopFirewallDriver -security_group_api=<%= node["openstack"]["compute"]["network"]["quantum"]["security_group_api"] %> -service_quantum_metadata_proxy=<%= node["openstack"]["compute"]["network"]["quantum"]["service_quantum_metadata_proxy"] %> -quantum_metadata_proxy_shared_secret=<%= @quantum_metadata_proxy_shared_secret %> -default_floating_pool=<%= node["openstack"]["compute"]["network"]["quantum"]["public_network_name"] %> -dns_server=<%= node["openstack"]["compute"]["network"]["quantum"]["dns_server"] %> +security_group_api=<%= node["openstack"]["compute"]["network"]["neutron"]["security_group_api"] %> +service_neutron_metadata_proxy=<%= node["openstack"]["compute"]["network"]["neutron"]["service_neutron_metadata_proxy"] %> +neutron_metadata_proxy_shared_secret=<%= @neutron_metadata_proxy_shared_secret %> +default_floating_pool=<%= node["openstack"]["compute"]["network"]["neutron"]["public_network_name"] %> +dns_server=<%= node["openstack"]["compute"]["network"]["neutron"]["dns_server"] %> <% when "nova" -%> multi_host=<%= node["openstack"]["compute"]["network"]["multi_host"] %> network_manager=<%= node["openstack"]["compute"]["network"]["network_manager"] %> public_interface=<%= node["openstack"]["compute"]["network"]["public_interface"] %> -fixed_range=<%= node["openstack"]["compute"]["network"]["fixed_range"] %> dmz_cidr=<%= node["openstack"]["compute"]["network"]["dmz_cidr"] %> -<% if %w(fedora redhat centos).include? node.platform -%> -# https://bugzilla.redhat.com/show_bug.cgi?id=788485 - not released in epel yet -force_dhcp_release=false -<% else -%> -force_dhcp_release=true -<% end -%> +force_dhcp_release=<%= node["openstack"]["compute"]["network"]["force_dhcp_release"] %> <% if node["openstack"]["compute"]["dhcp_domain"] -%> dhcp_domain=<%= node["openstack"]["compute"]["dhcp_domain"] %> <% end %> send_arp_for_ha=true use_single_default_gateway=<%= node["openstack"]["compute"]["network"]["use_single_default_gateway"] %> -<% if node["openstack"]["compute"]["libvirt"]["virt_type"] == "qemu" -%> -libvirt_use_virtio_for_bridges=false -<% else -%> -libvirt_use_virtio_for_bridges=true -<% end -%> vlan_interface=<%= node["openstack"]["compute"]["network"]["vlan_interface"] %> - +auto_assign_floating_ip=<%= node["openstack"]["compute"]["network"]["auto_assign_floating_ip"] %> <% end -%> ##### GLANCE ##### image_service=nova.image.glance.GlanceImageService glance_api_servers=<%= @glance_api_ipaddress %>:<%= @glance_api_port %> +glance_num_retries= <%= node['openstack']['compute']['glance_num_retries'] %> ##### COMPUTE ##### compute_driver=<%= node["openstack"]["compute"]["driver"] %> +<% if node["openstack"]["compute"]["default_ephemeral_format"] %> +default_ephemeral_format=<%= node["openstack"]["compute"]["default_ephemeral_format"] %> +<% end %> +preallocate_images=<%= node["openstack"]["compute"]["preallocate_images"] %> +use_cow_images=<%= node["openstack"]["compute"]["use_cow_images"] %> +vif_plugging_is_fatal=<%= node["openstack"]["compute"]["vif_plugging_is_fatal"] %> +vif_plugging_timeout=<%= node["openstack"]["compute"]["vif_plugging_timeout"] %> compute_manager=nova.compute.manager.ComputeManager -sql_connection=<%= @sql_connection %> -sql_max_pool_size=100 -sql_max_overflow=100 connection_type=libvirt -libvirt_inject_password=True -libvirt_type=<%= node["openstack"]["compute"]["libvirt"]["virt_type"] %> -# Command prefix to use for running commands as root (default: sudo) -rootwrap_config=/etc/nova/rootwrap.conf -# Should unused base images be removed? (default: false) -remove_unused_base_images=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_base_images"] %> -# Unused resized base images younger than this will not be removed (default: 3600) -remove_unused_resized_minimum_age_seconds=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_resized_minimum_age_seconds"] %> -# Unused unresized base images younger than this will not be removed (default: 86400) -remove_unused_original_minimum_age_seconds=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_original_minimum_age_seconds"] %> -# Write a checksum for files in _base to disk (default: false) -checksum_base_images=<%= node["openstack"]["compute"]["libvirt"]["checksum_base_images"] %> +live_migration_retry_count=30 +force_config_drive=true + +##### NOTIFICATIONS ##### +<% if node['openstack']['compute']['config']['notification_drivers'] %> +# Driver or drivers to handle sending notifications (multi valued) +<% node['openstack']['compute']['config']['notification_drivers'].each do |d| %> +notification_driver=<%= d %> +<% end %> +<% end %> + +# Generate periodic compute.instance.exists notifications +instance_usage_audit=<%= node['openstack']['compute']['config']['instance_usage_audit'] %> + +# Time period to generate instance usages for. Time period +# must be hour, day, month or year (string value) +instance_usage_audit_period=<%= node['openstack']['compute']['config']['instance_usage_audit_period'] %> + +<% unless node['openstack']['compute']['config']['notify_on_state_change'].empty? -%> +# If set, send compute.instance.update notifications on +# instance state changes. Valid values are None for no +# notifications, "vm_state" for notifications on VM state +# changes, or "vm_and_task_state" for notifications on VM and +# task state changes. (string value) +notify_on_state_change=<%= node['openstack']['compute']['config']['notify_on_state_change'] %> +<% end %> + +<% if node["openstack"]["compute"]["enabled_apis"].include?("osapi_compute") -%> +# The IP address on which the OpenStack API will listen. (string value) +osapi_compute_listen=<%= @compute_api_bind_ip %> +# The port on which the OpenStack API will listen. (integer value) +osapi_compute_listen_port=<%= @compute_api_bind_port %> +<% end -%> ##### VNCPROXY ##### novncproxy_base_url=<%= @novncproxy_base_url %> -#xvpvncproxy_base_url=<%= @xvpvncproxy_base_url %> +xvpvncproxy_base_url=<%= @xvpvncproxy_base_url %> # This is only required on the server running xvpvncproxy -# xvpvncproxy_host=<%= node['openstack']['endpoints']['compute-xvpvnc']['host'] %> -# xvpvncproxy_port=<%= node['openstack']['endpoints']['compute-xvpvnc']['port'] %> +xvpvncproxy_host=<%= @xvpvncproxy_bind_host %> +xvpvncproxy_port=<%= @xvpvncproxy_bind_port %> # This is only required on the server running novncproxy -#novncproxy_host=<%= node['openstack']['endpoints']['compute-novnc']['host'] %> -novncproxy_port=<%= node['openstack']['endpoints']['compute-novnc']['port'] %> +novncproxy_host=<%= @novncproxy_bind_host %> +novncproxy_port=<%= @novncproxy_bind_port %> vncserver_listen=<%= @vncserver_listen %> vncserver_proxyclient_address=<%= @vncserver_proxyclient_address %> @@ -151,9 +218,9 @@ osapi_max_limit=<%= node["openstack"]["compute"]["config"]["osapi_max_limit"] %> # URIs returned in the various links collections to contain the proper # HTTPS endpoint. osapi_compute_link_prefix = <%= @osapi_compute_link_prefix %> -snapshot_image_format=<%= node["openstack"]["compute"]["config"]["snapshot_image_format"] %> start_guests_on_host_boot=<%= node["openstack"]["compute"]["config"]["start_guests_on_host_boot"] %> resume_guests_state_on_host_boot=<%= node["openstack"]["compute"]["config"]["resume_guests_state_on_host_boot"] %> +allow_resize_to_same_host=<%= node["openstack"]["compute"]["config"]["allow_resize_to_same_host"] %> ##### QUOTAS ##### # (StrOpt) default driver to use for quota checks (default: nova.quota.DbQuotaDriver) @@ -195,33 +262,60 @@ cpu_allocation_ratio=<%= node["openstack"]["compute"]["config"]["cpu_allocation_ # virtual ram to physical ram allocation ratio (default: 1.5) ram_allocation_ratio=<%= node["openstack"]["compute"]["config"]["ram_allocation_ratio"] %> <%- end %> +<%- if /DiskFilter/.match(@scheduler_default_filters) %> +# virtual disk to physical disk allocation ratio (default: 1.0) +disk_allocation_ratio=<%= node["openstack"]["compute"]["config"]["disk_allocation_ratio"] %> +<%- end %> <%- elsif /SimpleScheduler/.match(node["openstack"]["compute"]["scheduler"]["scheduler_driver"]) %> # SimpleScheduler Only Options # maximum number of instance cores to allow per host max_cores=<%= node["openstack"]["compute"]["config"]["cpu_allocation_ratio"].to_i * node["cpu"]["total"].to_i %> <%- end %> -# If true, force creation of config drive regardless of if --config-drive was specified in the API call +<% if node["openstack"]["compute"]["config"]["force_config_drive"] %> force_config_drive=<%= node["openstack"]["compute"]["config"]["force_config_drive"] %> +<% end %> +mkisofs_cmd=<%= node["openstack"]["compute"]["config"]["mkisofs_cmd"] %> +injected_network_template=<%= node["openstack"]["compute"]["config"]["injected_network_template"] %> -<% if %w(fedora redhat centos).include? node.platform -%> +<% if node["openstack"]["compute"]["enabled_apis"].include?("ec2") %> +# The IP address on which the EC2 API will listen. (string value) +ec2_listen=<%= @ec2_api_bind_ip %> +# The port on which the EC2 API will listen. (integer value) +ec2_listen_port=<%= @ec2_api_bind_port %> + +<% if %w(fedora rhel).include? node.platform_family -%> # Adding support for non-modded euca2ools to display ip address info # https://bugs.launchpad.net/nova/+bug/901594 ec2_private_dns_show_ip=True <% end -%> ##### WORKERS ###### -ec2_workers=<%= node["cpu"]["total"] %> -osapi_compute_workers=<%= node["cpu"]["total"] %> -metadata_workers=<%= node["cpu"]["total"] %> +<% if node["openstack"]["compute"]["ec2_workers"] %> +ec2_workers=<%= node["openstack"]["compute"]["ec2_workers"] %> +<% end %> ##### KEYSTONE ##### keystone_ec2_url=<%= @identity_endpoint.scheme %>://<%= @identity_endpoint.host %>:<%= @identity_endpoint.port %>/v2.0/ec2tokens +<% end -%> + +# a list of APIs to enable by default (list value) +enabled_apis=<%= node["openstack"]["compute"]["enabled_apis"] %> + +##### WORKERS ###### +<% if node["openstack"]["compute"]["osapi_compute_workers"] %> +osapi_compute_workers=<%= node["openstack"]["compute"]["osapi_compute_workers"] %> +<% end %> +<% if node["openstack"]["compute"]["metadata_workers"] %> +metadata_workers=<%= node["openstack"]["compute"]["metadata_workers"] %> +<% end %> ##### VOLUMES ##### # iscsi target user-land tool to use iscsi_helper=<%= @iscsi_helper %> volume_api_class=<%= node["openstack"]["compute"]["config"]["volume_api_class"] %> +# Region name of this node (string value) +os_region_name=<%= node['openstack']['region'] %> ##### THIRD PARTY ADDITIONS ##### <% if node["openstack"]["compute"]["plugins"] %> @@ -230,6 +324,273 @@ osapi_compute_extension=<%= p %> <% end %> <% end %> +<% if node["openstack"]["compute"]["misc_nova"] %> +<% node["openstack"]["compute"]["misc_nova"].each do |m| %> +<%= m %> +<% end %> +<% end %> + [conductor] use_local=<%= node["openstack"]["compute"]["conductor"]["use_local"] %> +<% if node["openstack"]["compute"]["conductor"]["workers"] %> +workers=<%= node["openstack"]["compute"]["conductor"]["workers"] %> +<% end %> + +<% if %w(vmwareapi.VMwareESXDriver vmwareapi.VMwareVCDriver).include?(node['openstack']['compute']['driver']) %> +[vmware] + +# +# Options defined in nova.virt.vmwareapi.driver +# + +# URL for connection to VMware ESX/VC host. (string value) +host_ip = <%= node['openstack']['compute']['vmware']['host_ip'] %> + +# Username for connection to VMware ESX/VC host. (string +# value) +host_username = <%= node['openstack']['compute']['vmware']['host_username'] %> + +# Password for connection to VMware ESX/VC host. (string +# value) +host_password = <%= @vmware_host_pass %> + +<% if node['openstack']['compute']['vmware']['cluster_name'] %> +# Name of a VMware Cluster ComputeResource. Used only if +# compute_driver is vmwareapi.VMwareVCDriver. (multi valued) + <% node['openstack']['compute']['vmware']['cluster_name'].each do |cluster| %> +cluster_name = <%= cluster %> + <% end %> +<% end %> + +<% if node['openstack']['compute']['vmware']['datastore_regex'] %> +# Regex to match the name of a datastore. (string value) +datastore_regex = <%= node['openstack']['compute']['vmware']['datastore_regex'] %> +<% end %> + +# The interval used for polling of remote tasks. (floating +# point value) +task_poll_interval = <%= node['openstack']['compute']['vmware']['task_poll_interval'] %> + +# The number of times we retry on failures, e.g., socket +# error, etc. (integer value) +api_retry_count = <%= node['openstack']['compute']['vmware']['api_retry_count'] %> + +# VNC starting port (integer value) +vnc_port = <%= node['openstack']['compute']['vmware']['vnc_port'] %> + +# Total number of VNC ports (integer value) +vnc_port_total = <%= node['openstack']['compute']['vmware']['vnc_port_total'] %> + +# Whether to use linked clone (boolean value) +use_linked_clone = <%= node['openstack']['compute']['vmware']['use_linked_clone'] %> + + +# +# Options defined in nova.virt.vmwareapi.vif +# + +# Physical ethernet adapter name for vlan networking (string +# value) +vlan_interface = <%= node['openstack']['compute']['vmware']['vlan_interface'] %> + + +<% if node['openstack']['compute']['vmware']['wsdl_location'] %> +# +# Options defined in nova.virt.vmwareapi.vim +# +# Optional VIM Service WSDL Location e.g +# http:///vimService.wsdl. Optional over-ride to +# default location for bug work-arounds (string value) +wsdl_location = <%= node['openstack']['compute']['vmware']['wsdl_location'] %> +<% end %> + +# +# Options defined in nova.virt.vmwareapi.vim_util +# + +# The maximum number of ObjectContent data objects that should +# be returned in a single result. A positive value will cause +# the operation to suspend the retrieval when the count of +# objects reaches the specified maximum. The server may still +# limit the count to something less than the configured value. +# Any remaining objects may be retrieved with additional +# requests. (integer value) +maximum_objects = <%= node['openstack']['compute']['vmware']['maximum_objects'] %> + + +# +# Options defined in nova.virt.vmwareapi.vmops +# + +# Name of Integration Bridge (string value) +integration_bridge = <%= node['openstack']['compute']['vmware']['integration_bridge'] %> +<% end %> + +[libvirt] + +# +# Options defined in nova.virt.libvirt.driver +# + +# Rescue ami image (string value) +#rescue_image_id= + +# Rescue aki image (string value) +#rescue_kernel_id= + +# Rescue ari image (string value) +#rescue_ramdisk_id= + +# Libvirt domain type (valid options are: kvm, lxc, qemu, uml, +# xen) (string value) +# Deprecated group/name - [DEFAULT]/libvirt_type +virt_type=<%= node["openstack"]["compute"]["libvirt"]["virt_type"] %> + +# Override the default libvirt URI (which is dependent on +# virt_type) (string value) +# Deprecated group/name - [DEFAULT]/libvirt_uri +#connection_uri= + +# Inject the admin password at boot time, without an agent. +# (boolean value) +# Deprecated group/name - [DEFAULT]/libvirt_inject_password +inject_password=<%= node["openstack"]["compute"]["libvirt"]["libvirt_inject_password"] %> + +# Inject the ssh public key at boot time (boolean value) +# Deprecated group/name - [DEFAULT]/libvirt_inject_key +inject_key=<%= node["openstack"]["compute"]["libvirt"]["libvirt_inject_key"] %> + +# The partition to inject to : -2 => disable, -1 => inspect +# (libguestfs only), 0 => not partitioned, >0 => partition +# number (integer value) +# Deprecated group/name - [DEFAULT]/libvirt_inject_partition +#inject_partition=-2 + +# Sync virtual and real mouse cursors in Windows VMs (boolean +# value) +#use_usb_tablet=true + +# Migration target URI (any included "%s" is replaced with the +# migration target hostname) (string value) +live_migration_uri=qemu+tcp://%s/system + +# Migration flags to be set for live migration (string value) +live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_LIVE, VIR_MIGRATE_PERSIST_DEST + +# Migration flags to be set for block migration (string value) +#block_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_NON_SHARED_INC + +# Maximum bandwidth to be used during migration, in Mbps +# (integer value) +live_migration_bandwidth=0 + +# Snapshot image format (valid options are : raw, qcow2, vmdk, +# vdi). Defaults to same as source image (string value) +snapshot_image_format=<%= node["openstack"]["compute"]["config"]["snapshot_image_format"] %> + +# The libvirt VIF driver to configure the VIFs. (string value) +# Deprecated group/name - [DEFAULT]/libvirt_vif_driver +vif_driver=<%= node["openstack"]["compute"]["network"]["neutron"]["libvirt_vif_driver"] %> + +# Libvirt handlers for remote volumes. (list value) +# Deprecated group/name - [DEFAULT]/libvirt_volume_drivers +#volume_drivers=iscsi=nova.virt.libvirt.volume.LibvirtISCSIVolumeDriver,iser=nova.virt.libvirt.volume.LibvirtISERVolumeDriver,local=nova.virt.libvirt.volume.LibvirtVolumeDriver,fake=nova.virt.libvirt.volume.LibvirtFakeVolumeDriver,rbd=nova.virt.libvirt.volume.LibvirtNetVolumeDriver,sheepdog=nova.virt.libvirt.volume.LibvirtNetVolumeDriver,nfs=nova.virt.libvirt.volume.LibvirtNFSVolumeDriver,aoe=nova.virt.libvirt.volume.LibvirtAOEVolumeDriver,glusterfs=nova.virt.libvirt.volume.LibvirtGlusterfsVolumeDriver,fibre_channel=nova.virt.libvirt.volume.LibvirtFibreChannelVolumeDriver,scality=nova.virt.libvirt.volume.LibvirtScalityVolumeDriver + +# Override the default disk prefix for the devices attached to +# a server, which is dependent on virt_type. (valid options +# are: sd, xvd, uvd, vd) (string value) +# Deprecated group/name - [DEFAULT]/libvirt_disk_prefix +#disk_prefix= + +# Number of seconds to wait for instance to shut down after +# soft reboot request is made. We fall back to hard reboot if +# instance does not shutdown within this window. (integer +# value) +# Deprecated group/name - [DEFAULT]/libvirt_wait_soft_reboot_seconds +#wait_soft_reboot_seconds=120 + +# Set to "host-model" to clone the host CPU feature flags; to +# "host-passthrough" to use the host CPU model exactly; to +# "custom" to use a named CPU model; to "none" to not set any +# CPU model. If virt_type="kvm|qemu", it will default to +# "host-model", otherwise it will default to "none" (string +# value) +# Deprecated group/name - [DEFAULT]/libvirt_cpu_mode +<% if node["openstack"]["compute"]["libvirt"]["virt_type"] == "qemu" -%> +cpu_mode=none +<% end -%> + +# Set to a named libvirt CPU model (see names listed in +# /usr/share/libvirt/cpu_map.xml). Only has effect if +# cpu_mode="custom" and virt_type="kvm|qemu" (string value) +# Deprecated group/name - [DEFAULT]/libvirt_cpu_model +#cpu_model= + +# Location where libvirt driver will store snapshots before +# uploading them to image service (string value) +# Deprecated group/name - [DEFAULT]/libvirt_snapshots_directory +#snapshots_directory=$instances_path/snapshots + +# Location where the Xen hvmloader is kept (string value) +#xen_hvmloader_path=/usr/lib/xen/boot/hvmloader + +# Specific cachemodes to use for different disk types e.g: +# file=directsync,block=none (list value) +<% if node["openstack"]["compute"]["config"]["disk_cachemodes"] %> +disk_cachemodes=<%= node["openstack"]["compute"]["config"]["disk_cachemodes"] %> +<% end %> + +# A path to a device that will be used as source of entropy on +# the host. Permitted options are: /dev/random or /dev/hwrng +# (string value) +#rng_dev_path= + +# +# Options defined in nova.virt.libvirt.imagecache +# + +# Unused resized base images younger than this will not be removed (default: 3600) +remove_unused_resized_minimum_age_seconds=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_resized_minimum_age_seconds"] %> + +# Write a checksum for files in _base to disk (default: false) +checksum_base_images=<%= node["openstack"]["compute"]["libvirt"]["checksum_base_images"] %> + +# +# Options defined in nova.virt.libvirt.vif +# + +<% if node["openstack"]["compute"]["libvirt"]["virt_type"] == "qemu" -%> +use_virtio_for_bridges=false +<% else -%> +use_virtio_for_bridges=true +<% end -%> + +# +# Options defined in nova.virt.libvirt.imagebackend +# + +# VM Images format. Acceptable values are: raw, qcow2, lvm, rbd, default. If default is specified, +# then use_cow_images flag is used instead of this one. +images_type=<%= node['openstack']['compute']['libvirt']['images_type'] %> + +<% if node['openstack']['compute']['libvirt']['images_type'] == 'lvm' -%> +images_volume_group=<%= node['openstack']['compute']['libvirt']['volume_group'] %> +sparse_logical_volumes=<%= node['openstack']['compute']['libvirt']['sparse_logical_volumes'] %> + +<% elsif node['openstack']['compute']['libvirt']['images_type'] == 'rbd' -%> +images_rbd_pool=<%= node['openstack']['compute']['libvirt']['images_rbd_pool'] %> +images_rbd_ceph_conf=<%= node['openstack']['compute']['libvirt']['images_rbd_ceph_conf'] %> + +# +# Options defined in nova.virt.libvirt.volume +# + +# The RADOS client name for accessing rbd volumes (string value) +# NOTE that if these two options are set here, it will override the rbd_user that cinder provides +# for nova in netdisk_properties: +# https://github.com/openstack/nova/blob/c15dff2e9978fe851c73e92ab7f9b46e27de81ba/nova/virt/libvirt/volume.py#L217-L229 +rbd_user=<%= node['openstack']['compute']['libvirt']['rbd']['rbd_user'] %> +# The libvirt UUID of the secret for the rbd images (string value) +rbd_secret_uuid=<%= node['openstack']['block-storage']['rbd_secret_uuid'] %> +<% end -%> diff --git a/chef/cookbooks/openstack-compute/templates/default/openrc.erb b/chef/cookbooks/openstack-compute/templates/default/openrc.erb deleted file mode 100644 index 1f59170..0000000 --- a/chef/cookbooks/openstack-compute/templates/default/openrc.erb +++ /dev/null @@ -1,23 +0,0 @@ -<%= node["openstack"]["compute"]["custom_template_banner"] %> - -# COMMON OPENSTACK ENVS -export OS_USERNAME=<%= @user %> -export OS_PASSWORD=<%= @password %> -export OS_TENANT_NAME=<%= @tenant %> -export OS_AUTH_URL=<%= @identity_endpoint.to_s %> -export OS_AUTH_STRATEGY=<%= @auth_strategy %> -export OS_REGION_NAME=<%= node["openstack"]["compute"]["region"] %> - -# LEGACY NOVA ENVS -export NOVA_USERNAME=${OS_USERNAME} -export NOVA_PROJECT_ID=${OS_TENANT_NAME} -export NOVA_PASSWORD=${OS_PASSWORD} -export NOVA_API_KEY=${OS_PASSWORD} -export NOVA_URL=${OS_AUTH_URL} -export NOVA_VERSION=<%= @nova_api_version %> -export NOVA_REGION_NAME=<%= node["openstack"]["compute"]["region"] %> - -# EUCA2OOLs ENV VARIABLES -export EC2_ACCESS_KEY=<%= node["credentials"]["EC2"]["admin"]["access"] %> -export EC2_SECRET_KEY=<%= node["credentials"]["EC2"]["admin"]["secret"] %> -export EC2_URL=<%= @ec2_url %> diff --git a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/api-metadata.filters.erb b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/api-metadata.filters.erb deleted file mode 100644 index cc623d2..0000000 --- a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/api-metadata.filters.erb +++ /dev/null @@ -1,15 +0,0 @@ -<%= node["openstack"]["compute"]["custom_template_banner"] %> - -# nova-rootwrap command filters for api-metadata nodes -# This is needed on nova-api hosts running with "metadata" in enabled_apis -# or when running nova-api-metadata -# This file should be owned by (and only-writeable by) the root user - -[Filters] -# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ... -iptables-save: CommandFilter, iptables-save, root -ip6tables-save: CommandFilter, ip6tables-save, root - -# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,) -iptables-restore: CommandFilter, iptables-restore, root -ip6tables-restore: CommandFilter, ip6tables-restore, root diff --git a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/compute.filters.erb b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/compute.filters.erb deleted file mode 100644 index e9009bc..0000000 --- a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/compute.filters.erb +++ /dev/null @@ -1,203 +0,0 @@ -<%= node["openstack"]["compute"]["custom_template_banner"] %> - -# nova-rootwrap command filters for compute nodes -# This file should be owned by (and only-writeable by) the root user - -[Filters] -# nova/virt/disk/mount/api.py: 'kpartx', '-a', device -# nova/virt/disk/mount/api.py: 'kpartx', '-d', device -kpartx: CommandFilter, /sbin/kpartx, root - -# nova/virt/xenapi/vm_utils.py: tune2fs, -O ^has_journal, part_path -# nova/virt/xenapi/vm_utils.py: tune2fs, -j, partition_path -tune2fs: CommandFilter, /sbin/tune2fs, root - -# nova/virt/disk/mount/api.py: 'mount', mapped_device -# nova/virt/disk/api.py: 'mount', '-o', 'bind', src, target -# nova/virt/xenapi/vm_utils.py: 'mount', '-t', 'ext2,ext3,ext4,reiserfs'.. -# nova/virt/configdrive.py: 'mount', device, mountdir -# nova/virt/libvirt/volume.py: 'mount', '-t', 'sofs' ... -mount: CommandFilter, /bin/mount, root - -# nova/virt/disk/mount/api.py: 'umount', mapped_device -# nova/virt/disk/api.py: 'umount' target -# nova/virt/xenapi/vm_utils.py: 'umount', dev_path -# nova/virt/configdrive.py: 'umount', mountdir -umount: CommandFilter, /bin/umount, root - -# nova/virt/disk/mount/nbd.py: 'qemu-nbd', '-c', device, image -# nova/virt/disk/mount/nbd.py: 'qemu-nbd', '-d', device -qemu-nbd: CommandFilter, /usr/bin/qemu-nbd, root - -# nova/virt/disk/mount/loop.py: 'losetup', '--find', '--show', image -# nova/virt/disk/mount/loop.py: 'losetup', '--detach', device -losetup: CommandFilter, /sbin/losetup, root - -# nova/virt/disk/vfs/localfs.py: 'tee', canonpath -tee: CommandFilter, /usr/bin/tee, root - -# nova/virt/disk/vfs/localfs.py: 'mkdir', canonpath -mkdir: CommandFilter, /bin/mkdir, root - -# nova/virt/disk/vfs/localfs.py: 'chown' -# nova/virt/libvirt/connection.py: 'chown', os.getuid( console_log -# nova/virt/libvirt/connection.py: 'chown', os.getuid( console_log -# nova/virt/libvirt/connection.py: 'chown', 'root', basepath('disk') -# nova/utils.py: 'chown', owner_uid, path -chown: CommandFilter, /bin/chown, root - -# nova/virt/disk/vfs/localfs.py: 'chmod' -chmod: CommandFilter, /bin/chmod, root - -# nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap' -# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up' -# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev -# nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i.. -# nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'.. -# nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',.. -# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',.. -# nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev) -# nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1] -# nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge -# nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', .. -# nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',.. -# nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ... -# nova/network/linux_net.py: 'ip', 'link', 'set', interface, address,.. -# nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up' -# nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up' -# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, .. -# nova/network/linux_net.py: 'ip', 'link', 'set', dev, address, .. -# nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up' -# nova/network/linux_net.py: 'ip', 'route', 'add', .. -# nova/network/linux_net.py: 'ip', 'route', 'del', . -# nova/network/linux_net.py: 'ip', 'route', 'show', 'dev', dev -ip: CommandFilter, /sbin/ip, root - -# nova/virt/libvirt/vif.py: 'tunctl', '-b', '-t', dev -# nova/network/linux_net.py: 'tunctl', '-b', '-t', dev -tunctl: CommandFilter, tunctl, root - -# nova/virt/libvirt/vif.py: 'ovs-vsctl', ... -# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ... -# nova/network/linux_net.py: 'ovs-vsctl', .... -ovs-vsctl: CommandFilter, /usr/bin/ovs-vsctl, root - -# nova/network/linux_net.py: 'ovs-ofctl', .... -ovs-ofctl: CommandFilter, /usr/bin/ovs-ofctl, root - -# nova/virt/libvirt/connection.py: 'dd', if=%s % virsh_output, ... -dd: CommandFilter, /bin/dd, root - -# nova/virt/xenapi/volume_utils.py: 'iscsiadm', '-m', ... -iscsiadm: CommandFilter, iscsiadm, root - -# nova/virt/libvirt/volume.py: 'aoe-revalidate', aoedev -# nova/virt/libvirt/volume.py: 'aoe-discover' -aoe-revalidate: CommandFilter, /usr/sbin/aoe-revalidate, root -aoe-discover: CommandFilter, /usr/sbin/aoe-discover, root - -# nova/virt/xenapi/vm_utils.py: parted, --script, ... -# nova/virt/xenapi/vm_utils.py: 'parted', '--script', dev_path, ..*. -parted: CommandFilter, parted, root - -# nova/virt/xenapi/vm_utils.py: 'pygrub', '-qn', dev_path -pygrub: CommandFilter, /usr/bin/pygrub, root - -# nova/virt/xenapi/vm_utils.py: fdisk %(dev_path)s -fdisk: CommandFilter, /sbin/fdisk, root - -# nova/virt/xenapi/vm_utils.py: e2fsck, -f, -p, partition_path -# nova/virt/disk/api.py: e2fsck, -f, -p, image -e2fsck: CommandFilter, /sbin/e2fsck, root - -# nova/virt/xenapi/vm_utils.py: resize2fs, partition_path -# nova/virt/disk/api.py: resize2fs, image -resize2fs: CommandFilter, /sbin/resize2fs, root - -# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ... -iptables-save: CommandFilter, iptables-save, root -ip6tables-save: CommandFilter, ip6tables-save, root - -# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,) -iptables-restore: CommandFilter, iptables-restore, root -ip6tables-restore: CommandFilter, ip6tables-restore, root - -# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ... -# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],.. -arping: CommandFilter, arping, root - -# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address -dhcp_release: CommandFilter, /usr/bin/dhcp_release, root - -# nova/network/linux_net.py: 'kill', '-9', pid -# nova/network/linux_net.py: 'kill', '-HUP', pid -kill_dnsmasq: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP - -# nova/network/linux_net.py: 'kill', pid -kill_radvd: KillFilter, root, /usr/sbin/radvd - -# nova/network/linux_net.py: dnsmasq call -dnsmasq: DnsmasqFilter, /usr/sbin/dnsmasq, root -dnsmasq_deprecated: DeprecatedDnsmasqFilter, /usr/sbin/dnsmasq, root - -# nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'.. -radvd: CommandFilter, /usr/sbin/radvd, root - -# nova/network/linux_net.py: 'brctl', 'addbr', bridge -# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0 -# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off' -# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface -brctl: CommandFilter, brctl, root - -# nova/virt/libvirt/utils.py: 'mkswap' -# nova/virt/xenapi/vm_utils.py: 'mkswap' -mkswap: CommandFilter, /sbin/mkswap, root - -# nova/virt/xenapi/vm_utils.py: 'mkfs' -mkfs: CommandFilter, /sbin/mkfs, root - -# nova/virt/libvirt/utils.py: 'qemu-img' -qemu-img: CommandFilter, /usr/bin/qemu-img, root - -# nova/virt/disk/vfs/localfs.py: 'readlink', '-e' -readlink: CommandFilter, readlink, root - -# nova/virt/disk/api.py: 'touch', target -touch: CommandFilter, /usr/bin/touch, root - -# nova/virt/disk/api.py: -mkfs.ext3: CommandFilter, /sbin/mkfs.ext3, root -mkfs.ntfs: CommandFilter, /sbin/mkfs.ntfs, root - -# nova/virt/libvirt/connection.py: -read_initiator: ReadFileFilter, /etc/iscsi/initiatorname.iscsi - -# nova/virt/libvirt/connection.py: -lvremove: CommandFilter, /sbin/lvremove, root - -# nova/virt/libvirt/utils.py: -lvcreate: CommandFilter, /sbin/lvcreate, root - -# nova/virt/libvirt/utils.py: -lvs: CommandFilter, /sbin/lvs, root - -# nova/virt/libvirt/utils.py: -vgs: CommandFilter, /sbin/vgs, root - -# nova/virt/baremetal/volume_driver.py: 'tgtadm', '--lld', 'iscsi', ... -tgtadm: CommandFilter, /usr/sbin/tgtadm, root - -# nova/utils.py:read_file_as_root: 'cat', file_path -# (called from nova/virt/disk/vfs/localfs.py:VFSLocalFS.read_file) -read_passwd: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/passwd -read_shadow: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/shadow - -# nova/virt/libvirt/volume.py: 'multipath' '-R' -multipath: CommandFilter, /sbin/multipath, root - -# nova/virt/libvirt/utils.py: -systool: CommandFilter, /usr/bin/systool, root - -# nova/virt/libvirt/volume.py: -sginfo: CommandFilter, /usr/bin/sginfo, root -sg_scan: CommandFilter, /usr/bin/sg_scan, root diff --git a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/network.filters.erb b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/network.filters.erb deleted file mode 100644 index bb74ea2..0000000 --- a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/network.filters.erb +++ /dev/null @@ -1,77 +0,0 @@ -<%= node["openstack"]["compute"]["custom_template_banner"] %> - -[Filters] -# nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap' -# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up' -# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev -# nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i.. -# nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'.. -# nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',.. -# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',.. -# nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev) -# nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1] -# nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge -# nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', .. -# nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',.. -# nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ... -# nova/network/linux_net.py: 'ip', 'link', 'set', interface, address,.. -# nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up' -# nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up' -# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, .. -# nova/network/linux_net.py: 'ip', 'link', 'set', dev, address, .. -# nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up' -# nova/network/linux_net.py: 'ip', 'route', 'add', .. -# nova/network/linux_net.py: 'ip', 'route', 'del', . -# nova/network/linux_net.py: 'ip', 'route', 'show', 'dev', dev -ip: CommandFilter, /sbin/ip, root - -# nova/virt/libvirt/vif.py: 'ovs-vsctl', ... -# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ... -# nova/network/linux_net.py: 'ovs-vsctl', .... -ovs-vsctl: CommandFilter, /usr/bin/ovs-vsctl, root - -# nova/network/linux_net.py: 'ovs-ofctl', .... -ovs-ofctl: CommandFilter, /usr/bin/ovs-ofctl, root - -# nova/network/linux_net.py: 'ebtables', '-D' ... -# nova/network/linux_net.py: 'ebtables', '-I' ... -ebtables: CommandFilter, /sbin/ebtables, root -ebtables_usr: CommandFilter, /usr/sbin/ebtables, root - -# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ... -iptables-save: CommandFilter, iptables-save, root -ip6tables-save: CommandFilter, ip6tables-save, root - -# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,) -iptables-restore: CommandFilter, iptables-restore, root -ip6tables-restore: CommandFilter, ip6tables-restore, root - -# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ... -# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],.. -arping: CommandFilter, arping, root - -# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address -dhcp_release: CommandFilter, /usr/bin/dhcp_release, root - -# nova/network/linux_net.py: 'kill', '-9', pid -# nova/network/linux_net.py: 'kill', '-HUP', pid -kill_dnsmasq: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP - -# nova/network/linux_net.py: 'kill', pid -kill_radvd: KillFilter, root, /usr/sbin/radvd - -# nova/network/linux_net.py: dnsmasq call -dnsmasq: DnsmasqFilter, /usr/sbin/dnsmasq, root -dnsmasq_deprecated: DeprecatedDnsmasqFilter, /usr/sbin/dnsmasq, root - -# nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'.. -radvd: CommandFilter, /usr/sbin/radvd, root - -# nova/network/linux_net.py: 'brctl', 'addbr', bridge -# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0 -# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off' -# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface -brctl: CommandFilter, brctl, root - -# nova/network/linux_net.py: 'sysctl', .... -sysctl: CommandFilter, /sbin/sysctl, root diff --git a/chef/cookbooks/openstack-compute/templates/default/secret.xml.erb b/chef/cookbooks/openstack-compute/templates/default/secret.xml.erb new file mode 100644 index 0000000..adc099d --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/secret.xml.erb @@ -0,0 +1,6 @@ + + <%= @uuid -%> + + client.<%= @client_name -%> secret + + diff --git a/chef/cookbooks/openstack-dashboard/.rubocop.yml b/chef/cookbooks/openstack-dashboard/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-dashboard/.tailor b/chef/cookbooks/openstack-dashboard/.tailor deleted file mode 100644 index 99f0dcf..0000000 --- a/chef/cookbooks/openstack-dashboard/.tailor +++ /dev/null @@ -1,25 +0,0 @@ -Tailor.config do |config| - config.formatters "text" - config.file_set '**/*.rb' do |style| - style.max_line_length 80, level: :off - style.allow_camel_case_methods false, level: :error - style.allow_hard_tabs false, level: :error - style.allow_screaming_snake_case_classes false, level: :error - style.allow_trailing_line_spaces false, level: :error - style.allow_invalid_ruby false, level: :warn - style.indentation_spaces 2, level: :error - style.max_code_lines_in_class 300, level: :error - style.max_code_lines_in_method 30, level: :error - style.spaces_after_comma 1, level: :error - style.spaces_after_lbrace 1, level: :error - style.spaces_after_lbracket 0, level: :error - style.spaces_after_lparen 0, level: :error - style.spaces_before_comma 0, level: :error - style.spaces_before_lbrace 1, level: :error - style.spaces_before_rbrace 1, level: :error - style.spaces_before_rbracket 0, level: :error - style.spaces_before_rparen 0, level: :error - style.spaces_in_empty_braces 0, level: :error - style.trailing_newlines 1, level: :error - end -end diff --git a/chef/cookbooks/openstack-dashboard/CHANGELOG.md b/chef/cookbooks/openstack-dashboard/CHANGELOG.md new file mode 100644 index 0000000..da7716a --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/CHANGELOG.md @@ -0,0 +1,28 @@ +openstack-dashboard Cookbook CHANGELOG +============================== +This file is used to list changes made in each version of the openstack-dashboard cookbook. +## 9.0.3 +* Fix LOGIN_REDIRECT_URL to be configurable on rhel + +## 9.0.2 +* Add support for configuring OPENSTACK_KEYSTONE_BACKEND + +## 9.0.1 +### Bug +* Fix openstack_keystone_default_role default +* Fix the depends cookbook version issue in metadata.rb + +## 9.0.0 +* Upgrade to Icehouse + +## 8.1.1 +### Bug +* Fix the DB2 ODBC driver issue + +## 8.1.0 +### Blue print +* Use the library method auth_uri_transform + +## 8.0.0 +### New version +* Upgrade to upstream Havana release diff --git a/chef/cookbooks/openstack-dashboard/Gemfile b/chef/cookbooks/openstack-dashboard/Gemfile index ffbff4a..ccfa456 100644 --- a/chef/cookbooks/openstack-dashboard/Gemfile +++ b/chef/cookbooks/openstack-dashboard/Gemfile @@ -1,9 +1,9 @@ -source "https://rubygems.org" +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 1.4.5" -gem "chefspec", "~> 1.3.0" -gem "foodcritic" -gem "strainer" -gem "tailor" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' diff --git a/chef/cookbooks/openstack-dashboard/Gemfile.lock b/chef/cookbooks/openstack-dashboard/Gemfile.lock index fb9593e..2954927 100644 --- a/chef/cookbooks/openstack-dashboard/Gemfile.lock +++ b/chef/cookbooks/openstack-dashboard/Gemfile.lock @@ -1,159 +1,192 @@ GEM remote: https://rubygems.org/ specs: - activesupport (3.2.13) - i18n (= 0.6.1) + activesupport (3.2.17) + i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.3.4) - akami (1.2.0) + addressable (2.3.6) + akami (1.2.1) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - berkshelf (1.4.5) - activesupport (>= 3.2.0) - addressable - celluloid (>= 0.14.0) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) - json (>= 1.5.0) - minitar - mixlib-config (~> 1.1) - mixlib-shellout (~> 1.1) - multi_json (~> 1.5) - retryable - ridley (~> 0.12.4) - solve (>= 0.4.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.5.0) + solve (>= 0.5.0) thor (~> 0.18.0) - yajl-ruby + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) + buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.1) + buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) timers (>= 1.0.0) - chef (11.4.4) - erubis - highline (>= 1.6.9) - json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) net-ssh (~> 2.6) - net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (1.3.0) - chef (>= 10.0) - erubis - fauxhai (>= 0.1.1, < 2.0) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.8.4) - builder (>= 2.1.2) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.7) - multipart-post (~> 1.1) - fauxhai (1.1.1) - httparty + faraday (0.8.9) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) net-ssh ohai - ffi (1.8.1) - foodcritic (2.1.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) - rak (~> 1.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.0.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.1) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - mime-types (1.23) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.4) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.1.0) - multi_json (1.7.6) - multi_xml (0.5.4) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) multipart-post (1.2.0) - net-http-persistent (2.8) - net-ssh (2.6.7) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - net-ssh-multi (1.1) - net-ssh (>= 2.1.4) - net-ssh-gateway (>= 0.99.0) - nokogiri (1.5.10) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.16.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) - rak (1.4) + rainbow (2.0.0) + rake (10.2.2) + rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (0.12.4) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) + buff-extensions (~> 0.3) + buff-ignore (~> 1.1) + buff-shell_out (~> 0.1) celluloid (~> 0.14.0) - chozo (>= 0.6.0) + celluloid-io (~> 0.14.0) erubis faraday (>= 0.8.4) hashie (>= 2.0.2) + json (>= 1.7.7) mixlib-authentication (>= 1.3.0) - mixlib-config (>= 1.1.0) - mixlib-log (>= 1.3.0) - mixlib-shellout (>= 1.1.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) + varia_model (~> 0.1) winrm (~> 1.1.0) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.1) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) savon (0.9.5) akami (~> 1.0) @@ -163,32 +196,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.4.4) - json - strainer (2.1.0) - berkshelf (~> 1.3) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) + berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - text-table (1.2.3) thor (0.18.1) - timers (1.1.0) - tins (0.8.0) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) uuidtools (2.1.4) + varia_model (0.3.2) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -198,10 +228,10 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 1.4.5) - chef (~> 11.4.4) - chefspec (~> 1.3.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - tailor diff --git a/chef/cookbooks/openstack-dashboard/README.md b/chef/cookbooks/openstack-dashboard/README.md index 5144dd5..050047f 100644 --- a/chef/cookbooks/openstack-dashboard/README.md +++ b/chef/cookbooks/openstack-dashboard/README.md @@ -35,29 +35,62 @@ Sets up the Horizon dashboard within an Apache `mod_wsgi` container. Attributes ========== -* `openstack["dashboard"]["db"]["username"]` - username for horizon database access -* `openstack["dashboard"]["server_hostname"]` - sets the ServerName in the Apache config. -* `openstack["dashboard"]["use_ssl"]` - toggle for using ssl with dashboard (default true) -* `openstack["dashboard"]["ssl"]["dir"]` - directory where ssl certs are stored on this system -* `openstack["dashboard"]["ssl"]["cert"]` - name to use when creating the ssl certificate -* `openstack["dashboard"]["ssl"]["key"]` - name to use when creating the ssl key -* `openstack["dashboard"]["dash_path"]` - base path for dashboard files (document root) -* `openstack["dashboard"]["wsgi_path"]` - path for wsgi dir -* `openstack["dashboard"]["ssl_offload"]` - Set SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') flag for offloading SSL -* `openstack["dashboard"]["plugins"]` - Array of plugins to include via INSTALED\_APPS +* `openstack['dashboard']['db']['username']` - Username for horizon database access +* `openstack['dashboard']['server_hostname']` - Sets the ServerName in the Apache config +* `openstack['dashboard']['allowed_hosts']` - List of host/domain names we can service (default: '\[\*\]') +* `openstack['dashboard']['dash_path']` - Base path for dashboard files (document root) +* `openstack['dashboard']['wsgi_path']` - Path for wsgi dir +* `openstack['dashboard']['wsgi_socket_prefix']` - Location that will override the standard Apache runtime directory +* `openstack['dashboard']['ssl_offload']` - Set SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') flag for offloading SSL +* `openstack['dashboard']['plugins']` - Array of plugins to include via INSTALED\_APPS +* `openstack['dashboard']['simple_ip_management']` - Boolean to enable or disable simplified floating IP address management +TODO: Add DB2 support on other platforms +* `openstack['dashboard']['platform']['db2_python_packages']` - Array of DB2 python packages, only available on redhat platform +* `openstack['dashboard']['http_port']` - Port that httpd should listen on (default: 80) +* `openstack['dashboard']['https_port']` - Port that httpd should listen on for using ssl (default: 443) +* `openstack['dashboard']['password_autocomplete']` - Toggle browser autocompletion for login form ('on' or 'off', default: 'on') + +Identity +-------- +* `openstack['dashboard']['identity_api_version']` - Force a specific Identity API version ('2.0' or '3', default: '2.0') +* `openstack['dashboard']['keystone_multidomain_support']` - Boolean to enable multi-Domain support +* `openstack['dashboard']['keystone_default_domain']` - Default Domain if using API v3 and on a single-domain model (default: 'Default') +* `openstack['dashboard']['keystone_default_role']` - Default Keystone role assigned to project members (default: '_member_') +* `openstack['dashboard']['keystone_backend']['name']` - Keystone backend in use ('native' or 'ldap', default: 'native') +* `openstack['dashboard']['keystone_backend']['can_edit_user']` - Boolean to allow some user-related identity operations (default: true) +* `openstack['dashboard']['keystone_backend']['can_edit_group']` - Boolean to allow some group-related identity operations (default: true) +* `openstack['dashboard']['keystone_backend']['can_edit_project']` - Boolean to allow some project-related identity operations (default: true) +* `openstack['dashboard']['keystone_backend']['can_edit_domain']` - Boolean to allow some domain-related identity operations (default: true) +* `openstack['dashboard']['keystone_backend']['can_edit_role']` - Boolean to allow some role-related identity operations (default: true) + +Certificate +----------- +* `openstack['dashboard']['use_ssl']` - Toggle for using ssl with dashboard (default: true) +* `openstack['dashboard']['ssl']['dir']` - Directory where ssl certs are stored on this system (default: platform dependent) +* `openstack['dashboard']['ssl']['cert']` - Name to use when creating the ssl certificate +* `openstack['dashboard']['ssl']['cert_url']` - If using an existing certificate, this is the URL to its location +* `openstack['dashboard']['ssl']['key']` - Name to use when creating the ssl key +* `openstack['dashboard']['ssl']['key_url']` - If using an existing certificate key, this is the URL to its location + +By default the openstack-dashboard cookbook ships with a self-signed certificate from a fake organization. +It is possible to use a real production certificate from your organization by putting that certificate +somewhere where the cookbook can download it from then simply passing in the URL of the certificate, and its +corresponding key, using the 'cert_url' and 'key_url' attributes. Testing ===== -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. -Tests are defined in Strainerfile. +Berkshelf +===== -To run tests: - - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -75,10 +108,16 @@ License and Author | **Author** | John Dewey () | | **Author** | Matt Ray () | | **Author** | Sean Gallagher () | +| **Author** | Chen Zhiwei () | +| **Author** | Jian Hua Geng () | +| **Author** | Ionut Artarisi () | +| **Author** | Eric Zhou () | | | | | **Copyright** | Copyright (c) 2012, Rackspace US, Inc. | | **Copyright** | Copyright (c) 2012-2013, AT&T Services, Inc. | | **Copyright** | Copyright (c) 2013, Opscode, Inc. | +| **Copyright** | Copyright (c) 2013-2014, IBM, Corp. | +| **Copyright** | Copyright (c) 2013-2014, SUSE Linux GmbH. | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/openstack-dashboard/Strainerfile b/chef/cookbooks/openstack-dashboard/Strainerfile index 7e292b4..44e3e14 100644 --- a/chef/cookbooks/openstack-dashboard/Strainerfile +++ b/chef/cookbooks/openstack-dashboard/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-dashboard/TESTING.md b/chef/cookbooks/openstack-dashboard/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-dashboard/attributes/default.rb b/chef/cookbooks/openstack-dashboard/attributes/default.rb index 9aa901e..3708782 100644 --- a/chef/cookbooks/openstack-dashboard/attributes/default.rb +++ b/chef/cookbooks/openstack-dashboard/attributes/default.rb @@ -1,8 +1,10 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-dashboard # Attributes:: default # # Copyright 2012, AT&T, Inc. +# Copyright 2013-2014, IBM, Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,86 +21,164 @@ # Set to some text value if you want templated config files # to contain a custom banner at the top of the written file -default["openstack"]["dashboard"]["custom_template_banner"] = " +default['openstack']['dashboard']['custom_template_banner'] = ' # This file autogenerated by Chef # Do not edit, changes will be overwritten -" +' -default["openstack"]["dashboard"]["debug"] = false - -# This user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# db_password routine. -default["openstack"]["dashboard"]["db"]["username"] = "dash" +default['openstack']['dashboard']['debug'] = false # The Keystone role used by default for users logging into the dashboard -default["openstack"]["dashboard"]["keystone_default_role"] = "Member" +default['openstack']['dashboard']['keystone_default_role'] = '_member_' # This is the name of the Chef role that will install the Keystone Service API -default["openstack"]["dashboard"]["keystone_service_chef_role"] = "keystone" +default['openstack']['dashboard']['keystone_service_chef_role'] = 'keystone' -default["openstack"]["dashboard"]["server_hostname"] = nil -default["openstack"]["dashboard"]["use_ssl"] = false -default["openstack"]["dashboard"]["ssl"]["cert"] = "horizon.pem" -default["openstack"]["dashboard"]["ssl"]["key"] = "horizon.key" +default['openstack']['dashboard']['server_hostname'] = nil +default['openstack']['dashboard']['use_ssl'] = false +default['openstack']['dashboard']['ssl']['cert_url'] = nil +default['openstack']['dashboard']['ssl']['key_url'] = nil +# When using a remote certificate and key, the names of the actual installed certificate +# and key in the file system are determined by the following two attributes. +# If you want the name of the installed files to match the name of the files from the URL, +# they need to be manually set below, if not the conventional horizon.* names will be used. +default['openstack']['dashboard']['ssl']['cert'] = 'horizon.pem' +default['openstack']['dashboard']['ssl']['key'] = 'horizon.key' -default["openstack"]["dashboard"]["swift"]["enabled"] = "False" +# List of hosts/domains the dashboard can serve. This should be changed, a '*' +# allows everything +default['openstack']['dashboard']['allowed_hosts'] = ['*'] -default["openstack"]["dashboard"]["theme"] = "default" +default['openstack']['dashboard']['swift']['enabled'] = 'False' -default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/openstack-dashboard" +default['openstack']['dashboard']['theme'] = 'default' -case node["platform"] -when "fedora", "centos", "redhat" - default["openstack"]["dashboard"]["ssl"]["dir"] = "/etc/pki/tls" - default["openstack"]["dashboard"]["local_settings_path"] = "/etc/openstack-dashboard/local_settings" - default["openstack"]["dashboard"]["static_path"] = "/usr/share/openstack-dashboard/static" +default['openstack']['dashboard']['apache']['sites-path'] = "#{node['apache']['dir']}/openstack-dashboard" + +default['openstack']['dashboard']['http_port'] = 80 +default['openstack']['dashboard']['https_port'] = 443 + +default['openstack']['dashboard']['secret_key_content'] = nil + +default['openstack']['dashboard']['webroot'] = '/' + +case node['platform_family'] +when 'fedora', 'rhel' + default['openstack']['dashboard']['horizon_user'] = 'apache' + default['openstack']['dashboard']['horizon_group'] = 'apache' + default['openstack']['dashboard']['secret_key_path'] = '/usr/share/openstack-dashboard/openstack_dashboard/local/.secret_key_store' + default['openstack']['dashboard']['ssl']['dir'] = '/etc/pki/tls' + default['openstack']['dashboard']['local_settings_path'] = '/etc/openstack-dashboard/local_settings' + default['openstack']['dashboard']['django_path'] = '/usr/share/openstack-dashboard' + default['openstack']['dashboard']['login_url'] = "#{node['openstack']['dashboard']['webroot']}auth/login/" + default['openstack']['dashboard']['logout_url'] = "#{node['openstack']['dashboard']['webroot']}auth/logout/" + default['openstack']['dashboard']['login_redirect_url'] = node['openstack']['dashboard']['webroot'] # TODO(shep) - Fedora does not generate self signed certs by default - default["openstack"]["dashboard"]["platform"] = { - "mysql_python_packages" => ["MySQL-python"], - "postgresql_python_packages" => ["python-psycopg2"], - "horizon_packages" => ["openstack-dashboard"], - "memcache_python_packages" => ["python-memcached"], - "package_overrides" => "" + default['openstack']['dashboard']['platform'] = { + 'mysql_python_packages' => ['MySQL-python'], + 'db2_python_packages' => %w{python-ibm-db python-ibm-db-django python-ibm-db-sa}, + 'postgresql_python_packages' => ['python-psycopg2'], + 'sqlite_python_packages' => [], + 'horizon_packages' => ['openstack-dashboard'], + 'memcache_python_packages' => ['python-memcached'], + 'package_overrides' => '' } - if node["platform"] == "fedora" - default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" + if node['platform_family'] == 'fedora' + default['openstack']['dashboard']['apache']['sites-path'] = "#{node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" else - default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/conf.d/openstack-dashboard" + default['openstack']['dashboard']['apache']['sites-path'] = "#{node["apache"]["dir"]}/sites-available/openstack-dashboard" end -when "suse" - default["openstack"]["dashboard"]["ssl"]["dir"] = "/etc/ssl" - default["openstack"]["dashboard"]["local_settings_path"] = "/usr/share/openstack-dashboard/openstack_dashboard/local/local_settings.py" - default["openstack"]["dashboard"]["static_path"] = "/usr/share/openstack-dashboard/static" - default["openstack"]["dashboard"]["platform"] = { - "mysql_python_packages" => ["python-mysql"], - "postgresql_python_packages" => ["python-psycopg2"], - "horizon_packages" => ["openstack-dashboard"], - "memcache_python_packages" => ["python-python-memcached"], - "package_overrides" => "" +when 'suse' + default['openstack']['dashboard']['horizon_user'] = 'wwwrun' + default['openstack']['dashboard']['horizon_group'] = 'www' + default['openstack']['dashboard']['secret_key_path'] = '/srv/www/openstack-dashboard/openstack_dashboard/local/.secret_key_store' + default['openstack']['dashboard']['ssl']['dir'] = '/etc/ssl' + default['openstack']['dashboard']['local_settings_path'] = '/srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py' + default['openstack']['dashboard']['django_path'] = '/srv/www/openstack-dashboard' + default['openstack']['dashboard']['login_url'] = nil + default['openstack']['dashboard']['logout_url'] = nil + default['openstack']['dashboard']['login_redirect_url'] = nil + default['openstack']['dashboard']['platform'] = { + 'mysql_python_packages' => ['python-mysql'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'sqlite_python_packages' => [], + 'horizon_packages' => ['openstack-dashboard'], + 'memcache_python_packages' => ['python-python-memcached'], + 'package_overrides' => '' } - default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" -when "ubuntu" - default["openstack"]["dashboard"]["ssl"]["dir"] = "/etc/ssl" - default["openstack"]["dashboard"]["local_settings_path"] = "/etc/openstack-dashboard/local_settings.py" - default["openstack"]["dashboard"]["static_path"] = "/usr/share/openstack-dashboard/openstack_dashboard/static" - default["openstack"]["dashboard"]["platform"] = { - "horizon_packages" => ["node-less", "openstack-dashboard"], - "mysql_python_packages" => ["python-mysqldb"], - "postgresql_python_packages" => ["python-psycopg2"], - "memcache_python_packages" => ["python-memcache"], - "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + default['openstack']['dashboard']['apache']['sites-path'] = "#{node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" +when 'debian' + default['openstack']['dashboard']['horizon_user'] = 'horizon' + default['openstack']['dashboard']['horizon_group'] = 'horizon' + default['openstack']['dashboard']['secret_key_path'] = '/var/lib/openstack-dashboard/secret_key' + default['openstack']['dashboard']['ssl']['dir'] = '/etc/ssl' + default['openstack']['dashboard']['local_settings_path'] = '/etc/openstack-dashboard/local_settings.py' + default['openstack']['dashboard']['django_path'] = '/usr/share/openstack-dashboard' + default['openstack']['dashboard']['login_url'] = nil + default['openstack']['dashboard']['logout_url'] = nil + default['openstack']['dashboard']['login_redirect_url'] = nil + default['openstack']['dashboard']['platform'] = { + 'mysql_python_packages' => ['python-mysqldb'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'memcache_python_packages' => ['python-memcache'], + 'sqlite_python_packages' => [], + 'package_overrides' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" } - default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/sites-available/openstack-dashboard" + # lessc became node-less in 12.10 + if node['lsb']['release'] > '12.04' + default['openstack']['dashboard']['platform']['horizon_packages'] = ['node-less', 'openstack-dashboard'] + else + default['openstack']['dashboard']['platform']['horizon_packages'] = ['lessc', 'openstack-dashboard'] + end + default['openstack']['dashboard']['apache']['sites-path'] = "#{node["apache"]["dir"]}/sites-available/openstack-dashboard" end -default["openstack"]["dashboard"]["dash_path"] = "/usr/share/openstack-dashboard/openstack_dashboard" -default["openstack"]["dashboard"]["stylesheet_path"] = "/usr/share/openstack-dashboard/openstack_dashboard/templates/_stylesheets.html" -default["openstack"]["dashboard"]["wsgi_path"] = node["openstack"]["dashboard"]["dash_path"] + "/wsgi/django.wsgi" -default["openstack"]["dashboard"]["session_backend"] = "memcached" +default['openstack']['dashboard']['dash_path'] = "#{node['openstack']['dashboard']['django_path']}/openstack_dashboard" +default['openstack']['dashboard']['static_path'] = "#{node['openstack']['dashboard']['django_path']}/static" +default['openstack']['dashboard']['stylesheet_path'] = '/usr/share/openstack-dashboard/openstack_dashboard/templates/_stylesheets.html' +default['openstack']['dashboard']['wsgi_path'] = node['openstack']['dashboard']['dash_path'] + '/wsgi/django.wsgi' +default['openstack']['dashboard']['wsgi_socket_prefix'] = nil +default['openstack']['dashboard']['session_backend'] = 'signed_cookies' -default["openstack"]["dashboard"]["ssl_offload"] = false -default["openstack"]["dashboard"]["plugins"] = nil +default['openstack']['dashboard']['ssl_offload'] = false +default['openstack']['dashboard']['plugins'] = nil -default["openstack"]["dashboard"]["error_log"] = "openstack-dashboard-error.log" -default["openstack"]["dashboard"]["access_log"] = "openstack-dashboard-access.log" +default['openstack']['dashboard']['error_log'] = 'openstack-dashboard-error.log' +default['openstack']['dashboard']['access_log'] = 'openstack-dashboard-access.log' + +default['openstack']['dashboard']['help_url'] = 'http://docs.openstack.org' + +default['openstack']['dashboard']['csrf_cookie_secure'] = true +default['openstack']['dashboard']['session_cookie_secure'] = true + +default['openstack']['dashboard']['keystone_multidomain_support'] = false +default['openstack']['dashboard']['identity_api_version'] = 2.0 +default['openstack']['dashboard']['keystone_default_domain'] = 'Default' +default['openstack']['dashboard']['console_type'] = 'AUTO' + +default['openstack']['dashboard']['keystone_backend']['name'] = 'native' +default['openstack']['dashboard']['keystone_backend']['can_edit_user'] = true +default['openstack']['dashboard']['keystone_backend']['can_edit_group'] = true +default['openstack']['dashboard']['keystone_backend']['can_edit_project'] = true +default['openstack']['dashboard']['keystone_backend']['can_edit_domain'] = true +default['openstack']['dashboard']['keystone_backend']['can_edit_role'] = true + +default['openstack']['dashboard']['log_level']['horizon'] = 'INFO' +default['openstack']['dashboard']['log_level']['openstack_dashboard'] = 'INFO' +default['openstack']['dashboard']['log_level']['novaclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['cinderclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['keystoneclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['glanceclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['neutronclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['heatclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['ceilometerclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['troveclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['swiftclient'] = 'INFO' +default['openstack']['dashboard']['log_level']['openstack_auth'] = 'INFO' +default['openstack']['dashboard']['log_level']['nose.plugins.manager'] = 'INFO' +default['openstack']['dashboard']['log_level']['django'] = 'INFO' + +default['openstack']['dashboard']['password_autocomplete'] = 'on' +default['openstack']['dashboard']['simple_ip_management'] = false +default['openstack']['dashboard']['neutron']['enable_lb'] = false +default['openstack']['dashboard']['neutron']['enable_quotas'] = true diff --git a/chef/cookbooks/openstack-dashboard/metadata.rb b/chef/cookbooks/openstack-dashboard/metadata.rb index 2965ea1..0150293 100644 --- a/chef/cookbooks/openstack-dashboard/metadata.rb +++ b/chef/cookbooks/openstack-dashboard/metadata.rb @@ -1,16 +1,16 @@ -name "openstack-dashboard" -maintainer "AT&T Services, Inc." -maintainer_email "cookbooks@lists.tfoundry.com" -license "Apache 2.0" -description "Installs/Configures the OpenStack Dasboard (Horizon)" +name 'openstack-dashboard' +maintainer 'AT&T Services, Inc.' +maintainer_email 'cookbooks@lists.tfoundry.com' +license 'Apache 2.0' +description 'Installs/Configures the OpenStack Dasboard (Horizon)' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "7.0.0" +version '9.0.3' -recipe "openstack-dashboard::server", "Sets up the Horizon dashboard within an Apache `mod_wsgi` container." +recipe 'openstack-dashboard::server', 'Sets up the Horizon dashboard within an Apache `mod_wsgi` container.' %w{ ubuntu fedora redhat centos suse }.each do |os| supports os end -depends "apache2" -depends "openstack-common", "~> 0.4.0" +depends 'apache2', '>= 1.9.6' +depends 'openstack-common', '~> 9.0' diff --git a/chef/cookbooks/openstack-dashboard/recipes/default.rb b/chef/cookbooks/openstack-dashboard/recipes/default.rb index 2b3bb4a..e2438eb 100644 --- a/chef/cookbooks/openstack-dashboard/recipes/default.rb +++ b/chef/cookbooks/openstack-dashboard/recipes/default.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-dashboard # Recipe:: default diff --git a/chef/cookbooks/openstack-dashboard/recipes/server.rb b/chef/cookbooks/openstack-dashboard/recipes/server.rb index 65af496..1d1479e 100644 --- a/chef/cookbooks/openstack-dashboard/recipes/server.rb +++ b/chef/cookbooks/openstack-dashboard/recipes/server.rb @@ -1,9 +1,11 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-dashboard # Recipe:: server # # Copyright 2012, Rackspace US, Inc. # Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013-2014, IBM, Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,9 +20,9 @@ # limitations under the License. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end @@ -30,172 +32,238 @@ end # and it should simply be a restorecon on the configuration file(s) and not # change the selinux mode # -execute "set-selinux-permissive" do - command "/sbin/setenforce Permissive" +execute 'set-selinux-permissive' do + command '/sbin/setenforce Permissive' action :run only_if "[ ! -e /etc/httpd/conf/httpd.conf ] && [ -e /etc/redhat-release ] && [ $(/sbin/sestatus | grep -c '^Current mode:.*enforcing') -eq 1 ]" end -platform_options = node["openstack"]["dashboard"]["platform"] +platform_options = node['openstack']['dashboard']['platform'] -include_recipe "apache2" -include_recipe "apache2::mod_wsgi" -include_recipe "apache2::mod_rewrite" -include_recipe "apache2::mod_ssl" +include_recipe 'apache2' +include_recipe 'apache2::mod_wsgi' +include_recipe 'apache2::mod_rewrite' +include_recipe 'apache2::mod_ssl' # # Workaround to re-enable selinux after installing apache on a fedora machine that has # selinux enabled and is currently permissive and the configuration set to enforcing. # TODO(breu): get the other one working and this won't be necessary # -execute "set-selinux-enforcing" do - command "/sbin/setenforce Enforcing ; restorecon -R /etc/httpd" +execute 'set-selinux-enforcing' do + command '/sbin/setenforce Enforcing ; restorecon -R /etc/httpd' action :run only_if "[ -e /etc/httpd/conf/httpd.conf ] && [ -e /etc/redhat-release ] && [ $(/sbin/sestatus | grep -c '^Current mode:.*permissive') -eq 1 ] && [ $(/sbin/sestatus | grep -c '^Mode from config file:.*enforcing') -eq 1 ]" end -identity_admin_endpoint = endpoint "identity-admin" +identity_admin_endpoint = endpoint 'identity-admin' auth_admin_uri = ::URI.decode identity_admin_endpoint.to_s -identity_endpoint = endpoint "identity-api" +identity_endpoint = endpoint 'identity-api' auth_uri = ::URI.decode identity_endpoint.to_s -db_pass = db_password "horizon" -db_info = db "dashboard" +case node['openstack']['dashboard']['identity_api_version'] +when 2.0 + auth_version = 'v2.0' +when 3 + auth_version = 'v3.0' +end -python_packages = platform_options["#{db_info['db_type']}_python_packages"] -(platform_options["horizon_packages"] + python_packages).each do |pkg| +auth_admin_uri = auth_uri_transform auth_admin_uri, auth_version +auth_uri = auth_uri_transform auth_uri, auth_version + +db_pass = get_password 'db', 'horizon' +db_info = db 'dashboard' + +python_packages = platform_options["#{db_info['service_type']}_python_packages"] +(platform_options['horizon_packages'] + python_packages).each do |pkg| package pkg do action :upgrade - options platform_options["package_overrides"] + options platform_options['package_overrides'] end end -if node["openstack"]["dashboard"]["session_backend"] == "memcached" - platform_options["memcache_python_packages"].each do |pkg| +if node['openstack']['dashboard']['session_backend'] == 'memcached' + platform_options['memcache_python_packages'].each do |pkg| package pkg end end memcached = memcached_servers -template node["openstack"]["dashboard"]["local_settings_path"] do - source "local_settings.py.erb" - owner "root" - group "root" - mode 00644 - - variables( - :db_pass => db_pass, - :db_info => db_info, - :auth_uri => auth_uri, - :auth_admin_uri => auth_admin_uri, - :memcached_servers => memcached - ) - - notifies :restart, "service[apache2]" -end - -# FIXME: this shouldn't run every chef run -# dashboard should not need db, so comment the following code block. -if "False" == "True" -execute "openstack-dashboard syncdb" do - cwd "/usr/share/openstack-dashboard" - environment ({'PYTHONPATH' => '/etc/openstack-dashboard:/usr/share/openstack-dashboard:$PYTHONPATH'}) - command "python manage.py syncdb --noinput" - action :run - # not_if "/usr/bin/mysql -u root -e 'describe #{node["dash"]["db"]}.django_content_type'" -end -end - -cookbook_file "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/certs/#{node["openstack"]["dashboard"]["ssl"]["cert"]}" do - source "horizon.pem" - mode 00644 - owner "root" - group "root" - - notifies :run, "execute[restore-selinux-context]", :immediately -end - -case node["platform"] -when "ubuntu","debian" - grp = "ssl-cert" -else - grp = "root" -end - -cookbook_file "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/private/#{node["openstack"]["dashboard"]["ssl"]["key"]}" do - source "horizon.key" - mode 00640 - owner "root" - group grp # Don't know about fedora - - notifies :run, "execute[restore-selinux-context]", :immediately -end - -# stop apache bitching -directory "#{node["openstack"]["dashboard"]["dash_path"]}/.blackhole" do - owner "root" - action :create -end - -template node["openstack"]["dashboard"]["apache"]["sites-path"] do - source "dash-site.erb" - owner "root" - group "root" - mode 00644 - - variables( - :ssl_cert_file => "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/certs/#{node["openstack"]["dashboard"]["ssl"]["cert"]}", - :ssl_key_file => "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/private/#{node["openstack"]["dashboard"]["ssl"]["key"]}" - ) - - notifies :run, "execute[restore-selinux-context]", :immediately -end - +# delete the openstack-dashboard.conf before reload apache2 service on fedora, redhat and centos +# since this file is not valid on those platforms for the apache2 service. file "#{node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" do action :delete backup false - only_if { platform?("fedora", "redhat", "centos") } # :pragma-foodcritic: ~FC024 - won't fix this + only_if { platform_family?('fedora', 'rhel') } # :pragma-foodcritic: ~FC024 - won't fix this +end + +template node['openstack']['dashboard']['local_settings_path'] do + source 'local_settings.py.erb' + owner 'root' + group 'root' + mode 00644 + + variables( + db_pass: db_pass, + db_info: db_info, + auth_uri: auth_uri, + auth_admin_uri: auth_admin_uri, + memcached_servers: memcached + ) + + notifies :restart, 'service[apache2]', :immediately +end + +execute 'openstack-dashboard syncdb' do + cwd node['openstack']['dashboard']['django_path'] + environment 'PYTHONPATH' => "/etc/openstack-dashboard:#{node['openstack']['dashboard']['django_path']}:$PYTHONPATH" + command 'python manage.py syncdb --noinput' + action :run + only_if do + node['openstack']['dashboard']['session_backend'] == 'sql' && + node['openstack']['db']['dashboard']['migrate'] || + db_info['service_type'] == 'sqlite' + end +end + +cert_file = "#{node['openstack']['dashboard']['ssl']['dir']}/certs/#{node['openstack']['dashboard']['ssl']['cert']}" +cert_mode = 00644 +cert_owner = 'root' +cert_group = 'root' +if node['openstack']['dashboard']['ssl']['cert_url'] + remote_file cert_file do + source node['openstack']['dashboard']['ssl']['cert_url'] + mode cert_mode + owner cert_owner + group cert_group + + notifies :run, 'execute[restore-selinux-context]', :immediately + end +else + cookbook_file cert_file do + source 'horizon.pem' + mode cert_mode + owner cert_owner + group cert_group + + notifies :run, 'execute[restore-selinux-context]', :immediately + end +end + +key_file = "#{node['openstack']['dashboard']['ssl']['dir']}/private/#{node['openstack']['dashboard']['ssl']['key']}" +key_mode = 00640 +key_owner = 'root' +case node['platform_family'] +when 'debian' # Don't know about fedora + key_group = 'ssl-cert' +else + key_group = 'root' +end + +if node['openstack']['dashboard']['ssl']['key_url'] + remote_file key_file do + source node['openstack']['dashboard']['ssl']['key_url'] + mode key_mode + owner key_owner + group key_group + + notifies :restart, 'service[apache2]', :immediately + notifies :run, 'execute[restore-selinux-context]', :immediately + end +else + cookbook_file key_file do + source 'horizon.key' + mode key_mode + owner key_owner + group key_group + + notifies :run, 'execute[restore-selinux-context]', :immediately + end +end + +directory "#{node['openstack']['dashboard']['dash_path']}/local" do + owner 'root' + group node['openstack']['dashboard']['horizon_group'] + mode 02770 + action :create +end + +# make sure this file has correct permission +file node['openstack']['dashboard']['secret_key_path'] do + owner node['openstack']['dashboard']['horizon_user'] + group node['openstack']['dashboard']['horizon_group'] + mode 00600 + # the only time the file should be created is if we have secret_key_content + # set, otherwise let apache create it when someone first accesses the + # dashboard + if node['openstack']['dashboard']['secret_key_content'].nil? + only_if { ::File.exists?(node['openstack']['dashboard']['secret_key_path']) } + else + content node['openstack']['dashboard']['secret_key_content'] + notifies :restart, 'service[apache2]' + end +end + +# stop apache bitching +directory "#{node["openstack"]["dashboard"]["dash_path"]}/.blackhole" do + owner 'root' + action :create +end + +template node['openstack']['dashboard']['apache']['sites-path'] do + source 'dash-site.erb' + owner 'root' + group 'root' + mode 00644 + + variables( + ssl_cert_file: "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/certs/#{node["openstack"]["dashboard"]["ssl"]["cert"]}", + ssl_key_file: "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/private/#{node["openstack"]["dashboard"]["ssl"]["key"]}" + ) + + notifies :run, 'execute[restore-selinux-context]', :immediately + notifies :reload, 'service[apache2]', :immediately end # ubuntu includes their own branding - we need to delete this until ubuntu makes this a # configurable paramter -package "openstack-dashboard-ubuntu-theme" do +package 'openstack-dashboard-ubuntu-theme' do action :purge - only_if { platform?("ubuntu")} + only_if { platform_family?('debian') } end # The `apache_site` provided by the apache2 cookbook # is not an LWRP. Guards do not apply to definitions. # http://tickets.opscode.com/browse/CHEF-778 -if platform?("debian","ubuntu") then - apache_site "000-default" do +if platform_family?('debian') + apache_site '000-default' do enable false end -elsif platform?("fedora") then - apache_site "default" do +elsif platform_family?('fedora', 'rhel') then + apache_site 'default' do enable false - notifies :run, "execute[restore-selinux-context]", :immediately + notifies :run, 'execute[restore-selinux-context]', :immediately end end -apache_site "openstack-dashboard" do +apache_site 'openstack-dashboard' do enable true - notifies :run, "execute[restore-selinux-context]", :immediately - notifies :reload, "service[apache2]", :immediately + notifies :run, 'execute[restore-selinux-context]', :immediately + notifies :reload, 'service[apache2]', :immediately end -execute "restore-selinux-context" do - command "restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :" +execute 'restore-selinux-context' do + command 'restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :' action :nothing - only_if { platform?("fedora") } + only_if { platform_family?('fedora') } end # TODO(shep) diff --git a/chef/cookbooks/openstack-dashboard/spec/default_spec.rb b/chef/cookbooks/openstack-dashboard/spec/default_spec.rb deleted file mode 100644 index d9376b5..0000000 --- a/chef/cookbooks/openstack-dashboard/spec/default_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-dashboard::default" do -end diff --git a/chef/cookbooks/openstack-dashboard/spec/server-fedora_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server-fedora_spec.rb index b299da9..25db168 100644 --- a/chef/cookbooks/openstack-dashboard/spec/server-fedora_spec.rb +++ b/chef/cookbooks/openstack-dashboard/spec/server-fedora_spec.rb @@ -1,56 +1,53 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-dashboard::server" do - before { dashboard_stubs } +describe 'openstack-dashboard::server' do - describe "fedora" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::FEDORA_OPTS - @chef_run.converge "openstack-dashboard::server" + describe 'fedora' do + + let(:runner) { ChefSpec::Runner.new(FEDORA_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it "deletes openstack-dashboard.conf" do - opts = ::FEDORA_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, true) - chef_run.converge "openstack-dashboard::server" - file = "/etc/httpd/conf.d/openstack-dashboard.conf" + include_context 'non_redhat_stubs' + include_context 'dashboard_stubs' - expect(chef_run).to delete_file file + it 'deletes openstack-dashboard.conf' do + file = '/etc/httpd/conf.d/openstack-dashboard.conf' + + expect(chef_run).to delete_file(file) end - it "doesn't remove the default ubuntu virtualhost" do - resource = @chef_run.find_resource( - "execute", - "a2dissite 000-default" + it 'does not remove the default ubuntu virtualhost' do + resource = chef_run.find_resource( + 'execute', + 'a2dissite 000-default' ) expect(resource).to be_nil end - it "removes default virtualhost" do - resource = @chef_run.find_resource( - "execute", - "a2dissite default" + it 'removes default virtualhost' do + resource = chef_run.find_resource( + 'execute', + 'a2dissite default' ).to_hash expect(resource[:params]).to include( - :enable => false + enable: false ) end - it "notifies restore-selinux-context" do + it 'notifies restore-selinux-context' do pending "TODO: how to test this occured on apache_site 'default'" end - it "executes restore-selinux-context" do - opts = ::FEDORA_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, true) - chef_run.converge "openstack-dashboard::server" - cmd = "restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :" + it 'executes restore-selinux-context' do + cmd = 'restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :' - expect(chef_run).to execute_command cmd + expect(chef_run).not_to run_execute(cmd) end end end diff --git a/chef/cookbooks/openstack-dashboard/spec/server-opensuse_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server-opensuse_spec.rb deleted file mode 100644 index c018c0a..0000000 --- a/chef/cookbooks/openstack-dashboard/spec/server-opensuse_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-dashboard::server" do - before { dashboard_stubs } - - describe "opensuse" do - context "mysql backend" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - ::Chef::Recipe.any_instance.stub(:db).with("dashboard").and_return( - {"db_type" => "mysql", "db_name" => "flying_dolphin"}) - - @chef_run.converge "openstack-dashboard::server" - end - - it "installs mysql packages when mysql backend is configured" do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - ::Chef::Recipe.any_instance.stub(:db).with("dashboard").and_return( - {"db_type" => "mysql", "db_name" => "flying_dolphin"}) - @chef_run.converge "openstack-dashboard::server" - - expect(@chef_run).to upgrade_package "python-mysql" - end - end - - context "postgresql backend" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - ::Chef::Recipe.any_instance.stub(:db).with("dashboard").and_return( - {"db_type" => "postgresql", "db_name" => "flying_elephant"}) - @chef_run.converge "openstack-dashboard::server" - end - - it "installs packages" do - expect(@chef_run).to upgrade_package "openstack-dashboard" - end - - it "installs postgresql packages" do - expect(@chef_run).to upgrade_package "python-psycopg2" - end - - it "creates local_settings.py" do - file = @chef_run.template "/usr/share/openstack-dashboard/openstack_dashboard/local/local_settings.py" - - expect(@chef_run).to create_file_with_content(file.name, "autogenerated") - end - - it "creates .blackhole dir with proper owner" do - dir = "/usr/share/openstack-dashboard/openstack_dashboard/.blackhole" - - expect(@chef_run.directory(dir)).to be_owned_by "root" - end - - it "creates an openstack-dashboard virtual host with proper DocRoot" do - # XXX this should be hardcoded to /etc/apache2/... , but the - # upstream cookbook is broken for SUSE - # see for e.g. http://tickets.opscode.com/browse/COOK-2434 - file = @chef_run.template "#{@chef_run.node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" - - expect(@chef_run).to create_file_with_content(file.name, - "DocumentRoot /usr/share/openstack-dashboard/openstack_dashboard/.blackhole/") - end - end - end -end diff --git a/chef/cookbooks/openstack-dashboard/spec/server-redhat_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server-redhat_spec.rb index b437d1e..8558633 100644 --- a/chef/cookbooks/openstack-dashboard/spec/server-redhat_spec.rb +++ b/chef/cookbooks/openstack-dashboard/spec/server-redhat_spec.rb @@ -1,138 +1,114 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-dashboard::server" do - before { dashboard_stubs } +describe 'openstack-dashboard::server' do - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-dashboard::server" + describe 'redhat' do + + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it "executes set-selinux-permissive" do - opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, true) - chef_run.converge "openstack-dashboard::server" - cmd = "/sbin/setenforce Permissive" + include_context 'dashboard_stubs' + include_context 'redhat_stubs' - expect(chef_run).to execute_command cmd + it 'executes set-selinux-permissive' do + cmd = '/sbin/setenforce Permissive' + + expect(chef_run).to run_execute(cmd) end - it "installs packages" do - expect(@chef_run).to upgrade_package "openstack-dashboard" - expect(@chef_run).to upgrade_package "MySQL-python" + it 'installs packages' do + expect(chef_run).to upgrade_package('openstack-dashboard') + expect(chef_run).to upgrade_package('MySQL-python') end - it "executes set-selinux-enforcing" do - opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, true) - chef_run.converge "openstack-dashboard::server" - cmd = "/sbin/setenforce Enforcing ; restorecon -R /etc/httpd" - - expect(chef_run).to execute_command cmd - end - - describe "local_settings" do - before do - @file = @chef_run.template "/etc/openstack-dashboard/local_settings" - end - - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "rh specific template" do - expect(@chef_run).to create_file_with_content @file.name, "WEBROOT" + it 'installs db2 python packages if explicitly told' do + node.set['openstack']['db']['dashboard']['service_type'] = 'db2' + %w{python-ibm-db python-ibm-db-django python-ibm-db-sa}.each do |pkg| + expect(chef_run).to upgrade_package(pkg) end end - describe "certs" do - before do - @crt = @chef_run.cookbook_file "/etc/pki/tls/certs/horizon.pem" - @key = @chef_run.cookbook_file "/etc/pki/tls/private/horizon.key" - end + it 'executes set-selinux-enforcing' do + cmd = '/sbin/setenforce Enforcing ; restorecon -R /etc/httpd' - it "has proper owner" do - expect(@crt).to be_owned_by "root", "root" - expect(@key).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @crt.mode)).to eq "644" - expect(sprintf("%o", @key.mode)).to eq "640" - end - - it "notifies restore-selinux-context" do - expect(@crt).to notify "execute[restore-selinux-context]", :run - expect(@key).to notify "execute[restore-selinux-context]", :run - end + expect(chef_run).to run_execute(cmd) end - describe "openstack-dashboard virtual host" do - before do - f = "/etc/httpd/conf.d/openstack-dashboard" - @file = @chef_run.template f + describe 'local_settings' do + let(:file) { chef_run.template('/etc/openstack-dashboard/local_settings') } + + it 'has proper owner' do + expect(file.owner).to eq('root') + expect(file.group).to eq('root') end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq('644') end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "sets the ServerName directive " do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| - n.set["openstack"]["dashboard"]["server_hostname"] = "spec-test-host" + it 'has urls set' do + [ + %r(^LOGIN_URL = '/auth/login/'$), + %r(^LOGOUT_URL = '/auth/logout/'$), + /^LOGIN_REDIRECT_URL = '\/'$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) end - chef_run.converge "openstack-dashboard::server" - - expect(chef_run).to create_file_with_content @file.name, "spec-test-host" - end - - it "notifies restore-selinux-context" do - expect(@file).to notify "execute[restore-selinux-context]", :run end end - it "deletes openstack-dashboard.conf" do - opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, true) - chef_run.converge "openstack-dashboard::server" - file = "/etc/httpd/conf.d/openstack-dashboard.conf" + describe 'certs' do + let(:crt) { chef_run.cookbook_file('/etc/pki/tls/certs/horizon.pem') } + let(:key) { chef_run.cookbook_file('/etc/pki/tls/private/horizon.key') } - expect(chef_run).to delete_file file + it 'has proper owner' do + [crt, key].each do |file| + expect(file.owner).to eq('root') + expect(file.group).to eq('root') + end + end + + it 'has proper modes' do + expect(sprintf('%o', crt.mode)).to eq('644') + expect(sprintf('%o', key.mode)).to eq('640') + end + + it 'notifies restore-selinux-context' do + expect(crt).to notify('execute[restore-selinux-context]').to(:run) + expect(key).to notify('execute[restore-selinux-context]').to(:run) + end end - it "does not remove openstack-dashboard-ubuntu-theme package" do - opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, false) - chef_run.converge "openstack-dashboard::server" + it 'deletes openstack-dashboard.conf' do + file = '/etc/httpd/conf.d/openstack-dashboard.conf' - expect(chef_run).not_to purge_package "openstack-dashboard-ubuntu-theme" + expect(chef_run).to delete_file(file) end - it "doesn't remove default apache site" do - pending "TODO: how to properly test this" + it 'does not remove openstack-dashboard-ubuntu-theme package' do + + expect(chef_run).not_to purge_package('openstack-dashboard-ubuntu-theme') end - it "doesn't execute restore-selinux-context" do - opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, false) - chef_run.converge "openstack-dashboard::server" - cmd = "restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :" + it 'does not execute restore-selinux-context' do + cmd = 'restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :' - expect(chef_run).not_to execute_command cmd + expect(chef_run).not_to run_execute(cmd) + end + + it 'sets the WSGI daemon user to attribute default' do + file = chef_run.template('/etc/httpd/sites-available/openstack-dashboard') + expect(chef_run).to render_file(file.name).with_content('WSGIDaemonProcess dashboard user=apache') + end + + it 'has correct ownership on file with attribute defaults' do + file = chef_run.file('/usr/share/openstack-dashboard/openstack_dashboard/local/.secret_key_store') + expect(file.owner).to eq('apache') + expect(file.group).to eq('apache') end end end diff --git a/chef/cookbooks/openstack-dashboard/spec/server-suse_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server-suse_spec.rb new file mode 100644 index 0000000..81848e5 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/spec/server-suse_spec.rb @@ -0,0 +1,81 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-dashboard::server' do + + describe 'suse' do + + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + include_context 'non_redhat_stubs' + include_context 'dashboard_stubs' + + context 'mysql backend' do + + include_context 'mysql_backend' + + it 'installs mysql packages when mysql backend is configured' do + expect(chef_run).to upgrade_package('python-mysql') + end + end + + describe 'local_settings.py' do + let(:file) { chef_run.template('/srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py') } + + it 'does not have urls set' do + [ + /^LOGIN_URL =$/, + /^LOGOUT_URL =$/, + /^LOGIN_REDIRECT_URL =$/ + ].each do |line| + expect(chef_run).to_not render_file(file.name).with_content(line) + end + end + end + + context 'postgresql backend' do + + include_context 'postgresql_backend' + let(:file) { chef_run.template('/srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py') } + + it 'installs packages' do + expect(chef_run).to upgrade_package('openstack-dashboard') + end + + it 'installs postgresql packages' do + expect(chef_run).to upgrade_package('python-psycopg2') + end + + it 'creates local_settings.py' do + expect(chef_run).to render_file(file.name).with_content('autogenerated') + end + + it 'creates .blackhole dir with proper owner' do + dir = '/srv/www/openstack-dashboard/openstack_dashboard/.blackhole' + expect(chef_run.directory(dir).owner).to eq('root') + end + + it 'does not execute openstack-dashboard syncdb by default' do + cmd = 'python manage.py syncdb --noinput' + expect(chef_run).not_to run_execute(cmd).with( + cwd: '/srv/www/openstack-dashboard', + environment: { + 'PYTHONPATH' => '/etc/openstack-dashboard:' \ + '/srv/www/openstack-dashboard:' \ + '$PYTHONPATH' + } + ) + end + end + + it 'has correct ownership on file with attribute defaults' do + file = chef_run.file('/srv/www/openstack-dashboard/openstack_dashboard/local/.secret_key_store') + expect(file.owner).to eq('wwwrun') + expect(file.group).to eq('www') + end + end +end diff --git a/chef/cookbooks/openstack-dashboard/spec/server_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server_spec.rb index b660cb6..6dda1c4 100644 --- a/chef/cookbooks/openstack-dashboard/spec/server_spec.rb +++ b/chef/cookbooks/openstack-dashboard/spec/server_spec.rb @@ -1,238 +1,757 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-dashboard::server" do - before { dashboard_stubs } +shared_examples 'virtualhost port configurator' do |port_attribute_name, port_attribute_value| + let(:virtualhost_directive) { "" } + before do + node.set['openstack']['dashboard'][port_attribute_name] = port_attribute_value + end - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-dashboard::server" + it "sets Listen and NameVirtualHost directives when apache's listen_ports does not include #{port_attribute_value}" do + node.set['apache']['listen_ports'] = [port_attribute_value.to_i + 1] + %w(Listen NameVirtualHost).each do |directive| + expect(chef_run).to render_file(file.name).with_content(/^#{directive} \*:#{port_attribute_value}$/) + end + end + + it "does not set Listen and NameVirtualHost directives when apache's listen_ports include #{port_attribute_value}" do + node.set['apache']['listen_ports'] = [port_attribute_value] + chef_run.converge(described_recipe) + %w(Listen NameVirtualHost).each do |directive| + expect(chef_run).not_to render_file(file.name).with_content(/^#{directive} \*:#{port_attribute_value}$/) + end + end + + it 'sets the VirtualHost directive' do + expect(chef_run).to render_file(file.name).with_content(/^#{virtualhost_directive}$/) + end + + context 'server_hostname' do + it 'sets the value if the server_hostname is present' do + node.set['openstack']['dashboard']['server_hostname'] = 'server_hostname_value' + expect(chef_run).to render_file(file.name).with_content(/^#{virtualhost_directive}\s*ServerName server_hostname_value$/) end - it "doesn't execute set-selinux-permissive" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, false) - chef_run.converge "openstack-dashboard::server" - cmd = "/sbin/setenforce Permissive" + it 'does not set the value if the server_hostname is not present' do + node.set['openstack']['dashboard']['server_hostname'] = nil + expect(chef_run).not_to render_file(file.name).with_content(/^#{virtualhost_directive}\s*ServerName$/) + end + end +end - expect(chef_run).not_to execute_command cmd +describe 'openstack-dashboard::server' do + + describe 'ubuntu' do + + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it "installs apache packages" do - expect(@chef_run).to include_recipe "apache2" - expect(@chef_run).to include_recipe "apache2::mod_wsgi" - expect(@chef_run).to include_recipe "apache2::mod_rewrite" - expect(@chef_run).to include_recipe "apache2::mod_ssl" + let(:chef_run_session_sql) do + node.set['openstack']['dashboard']['session_backend'] = 'sql' + runner.converge(described_recipe) end - it "doesn't execute set-selinux-enforcing" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, false) - chef_run.converge "openstack-dashboard::server" - cmd = "/sbin/setenforce Enforcing ; restorecon -R /etc/httpd" + include_context 'non_redhat_stubs' + include_context 'dashboard_stubs' - expect(chef_run).not_to execute_command cmd + it 'does not execute set-selinux-permissive' do + cmd = '/sbin/setenforce Permissive' + expect(chef_run).not_to run_execute(cmd) end - it "installs packages" do - expect(@chef_run).to upgrade_package "node-less" - expect(@chef_run).to upgrade_package "openstack-dashboard" - expect(@chef_run).to upgrade_package "python-mysqldb" + it 'installs apache packages' do + expect(chef_run).to include_recipe('apache2') + expect(chef_run).to include_recipe('apache2::mod_wsgi') + expect(chef_run).to include_recipe('apache2::mod_rewrite') + expect(chef_run).to include_recipe('apache2::mod_ssl') end - describe "local_settings.py" do - before do - @file = @chef_run.template "/etc/openstack-dashboard/local_settings.py" + it 'does not execute set-selinux-enforcing' do + cmd = '/sbin/setenforce Enforcing ; restorecon -R /etc/httpd' + expect(chef_run).not_to run_execute(cmd) + end + + it 'installs packages' do + expect(chef_run).to upgrade_package('lessc') + expect(chef_run).to upgrade_package('openstack-dashboard') + expect(chef_run).to upgrade_package('python-mysqldb') + end + + describe 'local_settings.py' do + let(:file) { chef_run.template('/etc/openstack-dashboard/local_settings.py') } + + it 'has proper owner' do + expect(file.owner).to eq('root') + expect(file.group).to eq('root') end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq('644') end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "has the customer banner" do - expect(@chef_run).to create_file_with_content @file.name, "autogenerated" - end - - it "has the memcached servers" do - expect(@chef_run).to create_file_with_content @file.name, "hostA" - end - - it "does not configure caching when backend == memcache and no servers provided" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - ::Chef::Recipe.any_instance.stub(:memcached_servers). - and_return nil - chef_run.converge "openstack-dashboard::server" - - expect(chef_run).not_to create_file_with_content @file.name, - "django.core.cache.backends.memcached.MemcachedCache" - end - - it "does not configure caching when memcache_servers is empty" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - ::Chef::Recipe.any_instance.stub(:memcached_servers). - and_return [] - chef_run.converge "openstack-dashboard::server" - - expect(chef_run).not_to create_file_with_content @file.name, - "django.core.cache.backends.memcached.MemcachedCache" - end - - it "has some plugins enabled" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["dashboard"]["plugins"] = ["testPlugin1" ] + context 'template contents' do + it 'has the customer banner' do + node.set['openstack']['dashboard']['custom_template_banner'] = 'custom_template_banner_value' + expect(chef_run).to render_file(file.name).with_content(/^custom_template_banner_value$/) end - chef_run.converge "openstack-dashboard::server" - expect(chef_run).to create_file_with_content @file.name, "testPlugin1" - end + context 'debug setting' do + context 'set to true' do + before do + node.set['openstack']['dashboard']['debug'] = true + end - it "notifies apache2 restart" do - expect(@file).to notify "service[apache2]", :restart - end + it 'has a true value for the DEBUG attribute' do + expect(chef_run).to render_file(file.name).with_content(/^DEBUG = True$/) + end - it "does not configure ssl proxy when ssl_offload is false" do - expect(@chef_run).not_to( - create_file_with_content @file.name, "SECURE_PROXY_SSL_HEADER") - end + it 'sets the console logging level to DEBUG' do + expect(chef_run).to render_file(file.name).with_content(/^\s*'level': 'DEBUG',$/) + end + end - it "configures ssl proxy when ssl_offload is set to true" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["dashboard"]["ssl_offload"] = true + context 'set to false' do + before do + node.set['openstack']['dashboard']['debug'] = false + end + + it 'has a false value for the DEBUG attribute' do + expect(chef_run).to render_file(file.name).with_content(/^DEBUG = False$/) + end + + it 'sets the console logging level to INFO' do + expect(chef_run).to render_file(file.name).with_content(/^\s*'level': 'INFO',$/) + end + end end - chef_run.converge "openstack-dashboard::server" - expect(chef_run).to( - create_file_with_content @file.name, "SECURE_PROXY_SSL_HEADER") + it 'has some allowed hosts set' do + node.set['openstack']['dashboard']['allowed_hosts'] = ['dashboard.example.net'] + expect(chef_run).to render_file(file.name).with_content(/^ALLOWED_HOSTS = \["dashboard.example.net"\]$/) + end + + context 'ssl offload' do + let(:secure_proxy_string) { 'SECURE_PROXY_SSL_HEADER = \(\'HTTP_X_FORWARDED_PROTOCOL\', \'https\'\)' } + it 'does not configure ssl proxy when ssl_offload is false' do + node.set['openstack']['dashboard']['ssl_offload'] = false + expect(chef_run).not_to render_file(file.name).with_content(/^#{secure_proxy_string}$/) + end + + it 'configures ssl proxy when ssl_offload is set to true' do + node.set['openstack']['dashboard']['ssl_offload'] = true + expect(chef_run).to render_file(file.name).with_content(/^#{secure_proxy_string}$/) + end + end + + context 'ssl settings' do + context 'use_ssl enabled' do + before do + node.set['openstack']['dashboard']['use_ssl'] = true + end + + context 'csrf_cookie_secure setting' do + it 'sets secure csrf cookie to true when the attribute is enabled' do + node.set['openstack']['dashboard']['csrf_cookie_secure'] = true + expect(chef_run).to render_file(file.name).with_content(/^CSRF_COOKIE_SECURE = True$/) + end + + it 'sets secure csrf cookie to false when the attribute is disabled' do + node.set['openstack']['dashboard']['csrf_cookie_secure'] = false + expect(chef_run).to render_file(file.name).with_content(/^CSRF_COOKIE_SECURE = False$/) + end + end + + context 'session_cookie_secure setting' do + it 'set secure csrf cookie to true when the sttribute is enabled' do + node.set['openstack']['dashboard']['session_cookie_secure'] = true + expect(chef_run).to render_file(file.name).with_content(/^SESSION_COOKIE_SECURE = True$/) + end + + it 'set secure csrf cookie to false when the sttribute is disabled' do + node.set['openstack']['dashboard']['session_cookie_secure'] = false + expect(chef_run).to render_file(file.name).with_content(/^SESSION_COOKIE_SECURE = False$/) + end + end + end + + it 'does not set secure csrf nor secure session cookie settings when use_ssl is disabled' do + node.set['openstack']['dashboard']['use_ssl'] = false + [/^CSRF_COOKIE_SECURE$/, /^SESSION_COOKIE_SECURE$/].each do |setting| + expect(chef_run).not_to render_file(file.name).with_content(setting) + end + end + end + + it 'does not have urls set' do + [ + /^LOGIN_URL =$/, + /^LOGOUT_URL =$/, + /^LOGIN_REDIRECT_URL =$/ + ].each do |line| + expect(chef_run).to_not render_file(file.name).with_content(line) + end + end + + it 'has a identity api verision setting' do + node.set['openstack']['dashboard']['identity_api_version'] = 'identity_api_version_value' + expect(chef_run).to render_file(file.name).with_content(/^\s*"identity": identity_api_version_value$/) + end + + context 'keystone multidomain support' do + it 'sets to true when the attribute is enabled' do + node.set['openstack']['dashboard']['keystone_multidomain_support'] = true + expect(chef_run).to render_file(file.name).with_content(/^OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True$/) + end + + it 'sets to false when the attribute is disabled' do + node.set['openstack']['dashboard']['keystone_multidomain_support'] = false + expect(chef_run).to render_file(file.name).with_content(/^OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = False$/) + end + end + + it 'has a keystone default domain setting if identity api version is 3' do + node.set['openstack']['dashboard']['identity_api_version'] = 3 + node.set['openstack']['dashboard']['keystone_default_domain'] = 'keystone_default_domain_value' + expect(chef_run).to render_file(file.name).with_content(/^OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = "keystone_default_domain_value"$/) + end + + it 'has a console_type setting' do + node.set['openstack']['dashboard']['console_type'] = 'console_type_value' + expect(chef_run).to render_file(file.name).with_content(/^CONSOLE_TYPE = "console_type_value"$/) + end + + it 'has a help_url setting' do + node.set['openstack']['dashboard']['help_url'] = 'help_url_value' + expect(chef_run).to render_file(file.name).with_content(/\s*'help_url': "help_url_value",$/) + end + + context 'simple ip management' do + it 'enables the setting when the attribute is set' do + node.set['openstack']['dashboard']['simple_ip_management'] = true + expect(chef_run).to render_file(file.name).with_content('HORIZON_CONFIG["simple_ip_management"] = True') + end + + it 'disables the setting when the attribute is not set' do + node.set['openstack']['dashboard']['simple_ip_management'] = false + expect(chef_run).to render_file(file.name).with_content('HORIZON_CONFIG["simple_ip_management"] = False') + end + end + + it 'has default password_autocomplete setting' do + node.set['openstack']['dashboard']['password_autocomplete'] = 'password_autocomplete_value' + expect(chef_run).to render_file(file.name).with_content(/^HORIZON_CONFIG\["password_autocomplete"\] = "password_autocomplete_value"$/) + end + + it 'has configurable secret_key_path setting' do + node.set['openstack']['dashboard']['secret_key_path'] = 'secret_key_path_value' + expect(chef_run).to render_file(file.name).with_content(/^SECRET_KEY = secret_key.generate_or_read_from_file\(os.path.realpath\('secret_key_path_value'\)\)$/) + end + + context 'session backend' do + it 'sets the session engine to file when it is the session backend' do + node.set['openstack']['dashboard']['session_backend'] = 'file' + expect(chef_run).to render_file(file.name).with_content(/^SESSION_ENGINE = 'django.contrib.sessions.backends.file'$/) + end + + context 'memcached as session backend' do + let(:memcached_session_engine_setting) { /^SESSION_ENGINE = 'django.contrib.sessions.backends.cache'$/ } + context 'with memcache servers' do + it 'sets the session engine attribute' do + expect(chef_run).to render_file(file.name).with_content(memcached_session_engine_setting) + end + + it 'sets the location of the caches to the memcached servers addresses' do + expect(chef_run).to render_file(file.name).with_content(/^\s*'LOCATION': \[\s*'hostA:port',\s*'hostB:port',\s*\]$/) + end + end + + context 'without memcache servers' do + [nil, []].each do |empty_value| + it "does not configure caching when backend == memcache and #{empty_value} provided as memcache servers" do + Chef::Recipe.any_instance.stub(:memcached_servers) + .and_return(empty_value) + + expect(chef_run).not_to render_file(file.name) + .with_content(memcached_session_engine_setting) + end + end + end + end + + it 'sets the session engine to db when sql is the session backend' do + node.set['openstack']['dashboard']['session_backend'] = 'sql' + expect(chef_run).to render_file(file.name).with_content(/^SESSION_ENGINE = 'django.contrib.sessions.backends.db'$/) + end + end + + it 'has a keystone url' do + expect(chef_run).to render_file(file.name).with_content(%r(OPENSTACK_KEYSTONE_URL = "http://127.0.0.1:5000/v2.0")) + end + + it 'has a keystone admin url' do + expect(chef_run).to render_file(file.name).with_content(%r(OPENSTACK_KEYSTONE_ADMIN_URL = "http://127.0.0.1:35357/v2.0")) + end + + it 'has a keystone default role' do + node.set['openstack']['dashboard']['keystone_default_role'] = 'keystone_default_role_value' + expect(chef_run).to render_file(file.name).with_content(/^OPENSTACK_KEYSTONE_DEFAULT_ROLE = "keystone_default_role_value"$/) + end + + context 'keystone_backend settings' do + %w(native ldap).each do |keystone_backend_name| + it "sets the backend name to #{keystone_backend_name}" do + node.set['openstack']['dashboard']['keystone_backend']['name'] = keystone_backend_name + expect(chef_run).to render_file(file.name).with_content(/^\s*'name': '#{keystone_backend_name}',$/) + end + end + + %w(can_edit_user can_edit_group can_edit_project can_edit_domain can_edit_role).each do |keystone_setting| + it "enables the #{keystone_setting} keystone backend setting when the attribute is True" do + node.set['openstack']['dashboard']['keystone_backend'][keystone_setting] = true + expect(chef_run).to render_file(file.name).with_content(/^\s*\'#{keystone_setting}\': True,$/) + end + + it "disables the #{keystone_setting} keystone backend setting when the attribute is False" do + node.set['openstack']['dashboard']['keystone_backend'][keystone_setting] = false + expect(chef_run).to render_file(file.name).with_content(/^\s*\'#{keystone_setting}\': False,$/) + end + end + end + + context 'neutron settings' do + %w(enable_lb enable_quotas).each do |neutron_setting| + it "enables the #{neutron_setting} setting when the attributes is True" do + node.set['openstack']['dashboard']['neutron'][neutron_setting] = true + expect(chef_run).to render_file(file.name).with_content(/^\s*\'#{neutron_setting}\': True,$/) + end + + it "disables the #{neutron_setting} setting when the attributes is False" do + node.set['openstack']['dashboard']['neutron'][neutron_setting] = false + expect(chef_run).to render_file(file.name).with_content(/^\s*\'#{neutron_setting}\': False,$/) + end + end + end + + %w(horizon openstack_dashboard novaclient cinderclient keystoneclient + glanceclient neutronclient heatclient ceilometerclient troveclient + swiftclient openstack_auth nose.plugins.manager django).each do |component| + it "sets the logger level for #{component}" do + node.set['openstack']['dashboard']['log_level'][component] = "#{component}_log_level_value" + expect(chef_run).to render_file(file.name).with_content( + /^\s*'#{component}': {\s*'handlers': \['console'\],\s*'level': '#{component}_log_level_value',$/) + end + end + + { 'mysql' => 'django.db.backends.mysql', + 'sqlite' => 'django.db.backends.sqlite3', + 'postgresql' => 'django.db.backends.postgresql_psycopg2', + 'db2' => 'ibm_db_django' }.each do |service_type, backend| + context "#{service_type} database settings" do + before do + Chef::Recipe.any_instance.stub(:db) + .with('dashboard') + .and_return('service_type' => service_type, + 'db_name' => "#{service_type}_db", + 'host' => "#{service_type}_host") + node.set['openstack']['db']['dashboard']['username'] = "#{service_type}_user" + node.set['openstack']['dashboard']['platform'] = { "#{service_type}_python_packages" => %w(pkg1 pkg2) } + end + + [/^\s*'ENGINE': '#{backend}',$/, + /^\s*'NAME': '#{service_type}_db',$/].each do |cfg| + it "configures the #{service_type} backend with #{cfg}" do + expect(chef_run).to render_file(file.name).with_content(cfg) + end + end + + [/^\s*'USER': '#{service_type}_user',$/, + /^\s*'PASSWORD': 'test-passes',$/, + /^\s*'HOST': '#{service_type}_host',$/].each do |cfg| + unless service_type == 'sqlite' + it "configures the #{service_type} backend with #{cfg}" do + expect(chef_run).to render_file(file.name).with_content(cfg) + end + end + end + end + end + + context 'plugins' do + let(:mod_regex) { /^mod = sys.modules\['openstack_dashboard.settings'\]$/ } + context 'plugins enabled' do + let(:plugins) { %w(testPlugin1 testPlugin2) } + before do + node.set['openstack']['dashboard']['plugins'] = plugins + end + + it 'shows the mod setting' do + expect(chef_run).to render_file(file.name).with_content(mod_regex) + end + + it 'shows enabled plugins as installed apps' do + plugins.each do |plugin| + expect(chef_run).to render_file(file.name).with_content(/^mod\.INSTALLED_APPS \+= \('#{plugin}', \)$/) + end + end + end + + it 'does not show the mod setting if there are no plugins' do + node.set['openstack']['dashboard']['plugins'] = nil + expect(chef_run).not_to render_file(file.name).with_content(mod_regex) + end + end + end + + it 'notifies apache2 restart' do + expect(file).to notify('service[apache2]').to(:restart) + end + + end + + describe 'openstack-dashboard syncdb' do + sync_db_cmd = 'python manage.py syncdb --noinput' + sync_db_environment = { + 'PYTHONPATH' => '/etc/openstack-dashboard:' \ + '/usr/share/openstack-dashboard:' \ + '$PYTHONPATH' + } + + it 'does not execute when session_backend is not sql' do + expect(chef_run).not_to run_execute(sync_db_cmd).with( + cwd: node['openstack']['dashboard']['django_path'], + environment: sync_db_environment + ) + end + + it 'executes when session_backend is sql' do + expect(chef_run_session_sql).to run_execute(sync_db_cmd).with( + cwd: node['openstack']['dashboard']['django_path'], + environment: sync_db_environment + ) + end + + it 'does not execute when the migrate attribute is set to false' do + node.set['openstack']['db']['dashboard']['migrate'] = false + expect(chef_run_session_sql).not_to run_execute(sync_db_cmd).with( + cwd: node['openstack']['dashboard']['django_path'], + environment: sync_db_environment + ) + end + + it 'executes when database backend is sqlite' do + node.set['openstack']['db']['dashboard']['service_type'] = 'sqlite' + expect(chef_run_session_sql).to run_execute(sync_db_cmd).with( + cwd: node['openstack']['dashboard']['django_path'], + environment: sync_db_environment + ) end end - it "executes openstack-dashboard syncdb" do - cmd = "python manage.py syncdb --noinput" - expect(@chef_run).to execute_command(cmd).with( - :cwd => "/usr/share/openstack-dashboard", - :environment => { - "PYTHONPATH" => "/etc/openstack-dashboard:" \ - "/usr/share/openstack-dashboard:" \ - "$PYTHONPATH" + describe 'certs' do + let(:crt) { chef_run.cookbook_file('/etc/ssl/certs/horizon.pem') } + let(:key) { chef_run.cookbook_file('/etc/ssl/private/horizon.key') } + let(:remote_key) { chef_run.remote_file('/etc/ssl/private/horizon.key') } + + it 'has proper owner' do + expect(crt.owner).to eq('root') + expect(crt.group).to eq('root') + expect(key.owner).to eq('root') + expect(key.group).to eq('ssl-cert') + end + + it 'has proper modes' do + expect(sprintf('%o', crt.mode)).to eq('644') + expect(sprintf('%o', key.mode)).to eq('640') + end + + it 'notifies restore-selinux-context' do + expect(crt).to notify('execute[restore-selinux-context]').to(:run) + expect(key).to notify('execute[restore-selinux-context]').to(:run) + end + + it 'does not download certs if not needed' do + expect(chef_run).not_to create_remote_file('/etc/ssl/certs/horizon.pem') + expect(chef_run).not_to create_remote_file('/etc/ssl/private/horizon.key') + end + + it 'downloads certs if needed and restarts apache' do + node.set['openstack']['dashboard']['ssl']['cert_url'] = 'http://server/mycert.pem' + node.set['openstack']['dashboard']['ssl']['key_url'] = 'http://server/mykey.key' + expect(chef_run).to create_remote_file('/etc/ssl/certs/horizon.pem') + expect(chef_run).to create_remote_file('/etc/ssl/private/horizon.key') + expect(remote_key).to notify('service[apache2]').to(:restart) + end + end + + it 'creates .blackhole dir with proper owner' do + dir = '/usr/share/openstack-dashboard/openstack_dashboard/.blackhole' + + expect(chef_run.directory(dir).owner).to eq('root') + end + + describe 'openstack-dashboard virtual host' do + let(:file) { chef_run.template('/etc/apache2/sites-available/openstack-dashboard') } + + it 'has proper owner' do + expect(file.owner).to eq('root') + expect(file.group).to eq('root') + end + + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq('644') + end + + context 'template content' do + let(:rewrite_ssl_directive) { /^\s*RewriteEngine On\s*RewriteCond \%\{HTTPS\} off$/ } + let(:default_rewrite_rule) { %r(^\s*RewriteRule \^\(\.\*\)\$ https\://%\{HTTP_HOST\}%\{REQUEST_URI\} \[L,R\]$) } + + it 'has the default banner' do + node.set['openstack']['dashboard']['custom_template_banner'] = 'custom_template_banner_value' + expect(chef_run).to render_file(file.name).with_content(/^custom_template_banner_value$/) + end + + it_should_behave_like 'virtualhost port configurator', 'http_port', 8080 + + context 'with use_ssl enabled' do + before do + node.set['openstack']['dashboard']['use_ssl'] = true + end + + it_should_behave_like 'virtualhost port configurator', 'https_port', 4433 + + it 'shows rewrite ssl directive' do + expect(chef_run).to render_file(file.name).with_content(rewrite_ssl_directive) + end + + context 'rewrite rule' do + it 'shows the default rewrite rule when http_port is 80 and https_port is 443' do + node.set['openstack']['dashboard']['http_port'] = 80 + node.set['openstack']['dashboard']['https_port'] = 443 + expect(chef_run).to render_file(file.name).with_content(default_rewrite_rule) + end + + it 'shows the parameterized rewrite rule when http_port is different from 80' do + https_port_value = 443 + node.set['openstack']['dashboard']['http_port'] = 81 + node.set['openstack']['dashboard']['https_port'] = https_port_value + expect(chef_run).to render_file(file.name) + .with_content(%r(^\s*RewriteRule \^\(\.\*\)\$ https://%\{SERVER_NAME\}:#{https_port_value}%\{REQUEST_URI\} \[L,R\]$)) + end + + it 'shows the parameterized rewrite rule when https_port is different from 443' do + https_port_value = 444 + node.set['openstack']['dashboard']['http_port'] = 80 + node.set['openstack']['dashboard']['https_port'] = https_port_value + expect(chef_run).to render_file(file.name) + .with_content(%r(^\s*RewriteRule \^\(\.\*\)\$ https://%\{SERVER_NAME\}:#{https_port_value}%\{REQUEST_URI\} \[L,R\]$)) + end + end + + it 'shows ssl certificate related directives' do + node.set['openstack']['dashboard']['ssl']['dir'] = 'ssl_dir_value' + node.set['openstack']['dashboard']['ssl']['cert'] = 'ssl_cert_value' + node.set['openstack']['dashboard']['ssl']['key'] = 'ssl_key_value' + + [/^\s*SSLEngine on$/, + %r(^\s*SSLCertificateFile ssl_dir_value/certs/ssl_cert_value$), + %r(^\s*SSLCertificateKeyFile ssl_dir_value/private/ssl_key_value$)].each do |ssl_certificate_directive| + expect(chef_run).to render_file(file.name).with_content(ssl_certificate_directive) + end + end + end + + context 'with use_ssl disabled' do + before do + node.set['openstack']['dashboard']['use_ssl'] = false + end + + it 'does not show rewrite ssl directive' do + expect(chef_run).not_to render_file(file.name).with_content(rewrite_ssl_directive) + end + + it 'does not show the default rewrite rule' do + node.set['openstack']['dashboard']['http_port'] = 80 + node.set['openstack']['dashboard']['https_port'] = 443 + expect(chef_run).not_to render_file(file.name).with_content(default_rewrite_rule) + end + + it 'does not show ssl certificate related directives' do + [/^\s*SSLEngine on$/, + /^\s*SSLCertificateFile/, + /^\s*SSLCertificateKeyFile/].each do |ssl_certificate_directive| + expect(chef_run).not_to render_file(file.name).with_content(ssl_certificate_directive) + end + end + end + + it 'shows the ServerAdmin' do + node.set['apache']['contact'] = 'apache_contact_value' + expect(chef_run).to render_file(file.name).with_content(/\s*ServerAdmin apache_contact_value$/) + end + + it 'sets the WSGI script alias defaults' do + expect(chef_run).to render_file(file.name).with_content(%r(^\s*WSGIScriptAlias / /usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi$)) + end + + it 'sets the WSGI script alias' do + node.set['openstack']['dashboard']['wsgi_path'] = 'wsgi_path_value' + node.set['openstack']['dashboard']['webroot'] = 'root' + expect(chef_run).to render_file(file.name).with_content(/^\s*WSGIScriptAlias root wsgi_path_value$/) + end + + it 'sets the WSGI daemon process' do + node.set['openstack']['dashboard']['horizon_user'] = 'horizon_user_value' + node.set['openstack']['dashboard']['horizon_group'] = 'horizon_group_value' + node.set['openstack']['dashboard']['dash_path'] = 'dash_path_value' + expect(chef_run).to render_file(file.name).with_content( + /^\s*WSGIDaemonProcess dashboard user=horizon_user_value group=horizon_group_value processes=3 threads=10 python-path=dash_path_value$/) + end + + it 'has the default DocRoot' do + node.set['openstack']['dashboard']['dash_path'] = 'dash_path_value' + expect(chef_run).to render_file(file.name) + .with_content(%r(\s*DocumentRoot dash_path_value/.blackhole/$)) + end + + it 'sets the right Alias path for /static' do + node.set['openstack']['dashboard']['static_path'] = 'static_path_value' + expect(chef_run).to render_file(file.name).with_content(/^\s+Alias \/static static_path_value$/) + end + + %w(dash_path static_path).each do |dir_attribute| + it "sets the #{dir_attribute} directory directive" do + node.set['openstack']['dashboard'][dir_attribute] = "#{dir_attribute}_value" + expect(chef_run).to render_file(file.name).with_content(/^\s*$/) + end + end + + context 'log directives' do + before do + node.set['apache']['log_dir'] = 'log_dir_value' + end + + it 'sets de ErrorLog directive' do + node.set['openstack']['dashboard']['error_log'] = 'error_log_value' + expect(chef_run).to render_file(file.name).with_content(/^\s*ErrorLog log_dir_value\/error_log_value$/) + end + + it 'sets de CustomLog directive' do + node.set['openstack']['dashboard']['access_log'] = 'access_log_value' + expect(chef_run).to render_file(file.name).with_content(/^\s*CustomLog log_dir_value\/access_log_value combined$/) + end + end + + it 'sets wsgi socket prefix if wsgi_socket_prefix attribute is preset' do + node.set['openstack']['dashboard']['wsgi_socket_prefix'] = '/var/run/wsgi' + expect(chef_run).to render_file(file.name).with_content(%r(^WSGISocketPrefix /var/run/wsgi$)) + end + + it 'omits wsgi socket prefix if wsgi_socket_prefix attribute is not preset' do + node.set['openstack']['dashboard']['wsgi_socket_prefix'] = nil + expect(chef_run).not_to render_file(file.name).with_content(/^WSGISocketPrefix $/) + end + end + + it 'notifies restore-selinux-context' do + expect(file).to notify('execute[restore-selinux-context]').to(:run) + end + end + + describe 'secret_key_path file' do + secret_key_path = '/var/lib/openstack-dashboard/secret_key' + let(:file) { chef_run.file(secret_key_path) } + + it 'has correct ownership' do + expect(file.owner).to eq('horizon') + expect(file.group).to eq('horizon') + end + + it 'has correct mode' do + expect(file.mode).to eq(00600) + end + + it 'does not notify apache2 restart' do + expect(file).not_to notify('service[apache2]').to(:restart) + end + + it 'has configurable path and ownership settings' do + node.set['openstack']['dashboard']['secret_key_path'] = 'somerandompath' + node.set['openstack']['dashboard']['horizon_user'] = 'somerandomuser' + node.set['openstack']['dashboard']['horizon_group'] = 'somerandomgroup' + file = chef_run.file('somerandompath') + expect(file.owner).to eq('somerandomuser') + expect(file.group).to eq('somerandomgroup') + end + + describe 'secret_key_content set' do + before do + node.set['openstack']['dashboard']['secret_key_content'] = 'somerandomcontent' + end + + it 'has configurable secret_key_content setting' do + expect(chef_run).to render_file(file.name).with_content('somerandomcontent') + end + + it 'notifies apache2 restart when secret_key_content set' do + expect(file).to notify('service[apache2]').to(:restart) + end + end + end + + it 'does not delete openstack-dashboard.conf' do + file = '/etc/httpd/conf.d/openstack-dashboard.conf' + + expect(chef_run).not_to delete_file(file) + end + + it 'removes openstack-dashboard-ubuntu-theme package' do + expect(chef_run).to purge_package('openstack-dashboard-ubuntu-theme') + end + + it 'calls apache_site to disable 000-default virtualhost' do + + resource = chef_run.find_resource('execute', + 'a2dissite 000-default').to_hash + expect(resource).to include( + action: 'run', + params: { + enable: false, + name: '000-default' } ) end - describe "certs" do - before do - @crt = @chef_run.cookbook_file "/etc/ssl/certs/horizon.pem" - @key = @chef_run.cookbook_file "/etc/ssl/private/horizon.key" - end + it 'calls apache_site to enable openstack-dashboard virtualhost' do - it "has proper owner" do - expect(@crt).to be_owned_by "root", "root" - expect(@key).to be_owned_by "root", "ssl-cert" - end - - it "has proper modes" do - expect(sprintf("%o", @crt.mode)).to eq "644" - expect(sprintf("%o", @key.mode)).to eq "640" - end - - it "notifies restore-selinux-context" do - expect(@crt).to notify "execute[restore-selinux-context]", :run - expect(@key).to notify "execute[restore-selinux-context]", :run - end + resource = chef_run.find_resource('execute', + 'a2ensite openstack-dashboard').to_hash + expect(resource).to include( + action: 'run', + params: { + enable: true, + notifies: [:reload, 'service[apache2]', :immediately], + name: 'openstack-dashboard' + } + ) end - it "creates .blackhole dir with proper owner" do - dir = "/usr/share/openstack-dashboard/openstack_dashboard/.blackhole" - - expect(@chef_run.directory(dir)).to be_owned_by "root" + it 'notifies apache2 restart' do + pending 'TODO: how to test when tied to an LWRP' end - describe "openstack-dashboard virtual host" do - before do - f = "/etc/apache2/sites-available/openstack-dashboard.conf" - @file = @chef_run.template f - end + it 'does not execute restore-selinux-context' do + cmd = 'restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :' - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "has the default banner" do - expect(@chef_run).to create_file_with_content @file.name, "autogenerated" - end - - it "has the default DocRoot" do - expect(@chef_run).to create_file_with_content @file.name, - "DocumentRoot /usr/share/openstack-dashboard/openstack_dashboard/.blackhole/" - end - - it "sets the ServerName directive " do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["dashboard"]["server_hostname"] = "spec-test-host" - end - chef_run.converge "openstack-dashboard::server" - - expect(chef_run).to create_file_with_content @file.name, "spec-test-host" - end - - it "notifies restore-selinux-context" do - expect(@file).to notify "execute[restore-selinux-context]", :run - end + expect(chef_run).not_to run_execute(cmd) end - it "does not delete openstack-dashboard.conf" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, false) - chef_run.converge "openstack-dashboard::server" - file = "/etc/httpd/conf.d/openstack-dashboard.conf" - - expect(chef_run).not_to delete_file file - end - - it "removes openstack-dashboard-ubuntu-theme package" do - expect(@chef_run).to purge_package "openstack-dashboard-ubuntu-theme" - end - - it "removes default virtualhost" do - opts = ::UBUNTU_OPTS.merge(:step_into => ["apache_site"]) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.converge "openstack-dashboard::server" - cmd = "/usr/sbin/a2dissite 000-default" - - expect(chef_run).to execute_command cmd - end - - it "enables virtualhost" do - opts = ::UBUNTU_OPTS.merge(:step_into => ["apache_site"]) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.converge "openstack-dashboard::server" - cmd = "/usr/sbin/a2ensite openstack-dashboard" - - expect(chef_run).to execute_command cmd - end - - it "notifies apache2 restart" do - pending "TODO: how to test when tied to an LWRP" - end - - it "doesn't execute restore-selinux-context" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command(/.*/, false) - chef_run.converge "openstack-dashboard::server" - cmd = "restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :" - - expect(chef_run).not_to execute_command cmd + it 'has group write mode on path' do + path = chef_run.directory("#{chef_run.node['openstack']['dashboard']['dash_path']}/local") + expect(path.mode).to eq(02770) + expect(path.group).to eq(chef_run.node['openstack']['dashboard']['horizon_group']) end end end diff --git a/chef/cookbooks/openstack-dashboard/spec/spec_helper.rb b/chef/cookbooks/openstack-dashboard/spec/spec_helper.rb index 6506821..50689af 100644 --- a/chef/cookbooks/openstack-dashboard/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-dashboard/spec/spec_helper.rb @@ -1,30 +1,67 @@ -require "chefspec" +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' -::LOG_LEVEL = :fatal -::FEDORA_OPTS = { - :platform => "fedora", - :version => "18", - :log_level => ::LOG_LEVEL +ChefSpec::Coverage.start! { add_filter 'openstack-dashboard' } + +LOG_LEVEL = :fatal +FEDORA_OPTS = { + platform: 'fedora', + version: '18', + log_level: LOG_LEVEL } -::REDHAT_OPTS = { - :platform => "redhat", - :version => "6.3", - :log_level => ::LOG_LEVEL +REDHAT_OPTS = { + platform: 'redhat', + version: '6.5', + log_level: LOG_LEVEL } -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } -::OPENSUSE_OPTS = { - :platform => "opensuse", - :version => "12.3", - :log_level => ::LOG_LEVEL +SUSE_OPTS = { + platform: 'suse', + version: '11.03', + log_level: LOG_LEVEL } -def dashboard_stubs - ::Chef::Recipe.any_instance.stub(:memcached_servers). - and_return ["hostA:port", "hostB:port"] - ::Chef::Recipe.any_instance.stub(:db_password).with("horizon"). - and_return "test-pass" +shared_context 'dashboard_stubs' do + before do + Chef::Recipe.any_instance.stub(:memcached_servers) + .and_return ['hostA:port', 'hostB:port'] + Chef::Recipe.any_instance.stub(:get_password) + .with('db', 'horizon') + .and_return('test-passes') + end +end + +shared_context 'redhat_stubs' do + before do + stub_command("[ ! -e /etc/httpd/conf/httpd.conf ] && [ -e /etc/redhat-release ] && [ $(/sbin/sestatus | grep -c '^Current mode:.*enforcing') -eq 1 ]").and_return(true) + stub_command("[ -e /etc/httpd/conf/httpd.conf ] && [ -e /etc/redhat-release ] && [ $(/sbin/sestatus | grep -c '^Current mode:.*permissive') -eq 1 ] && [ $(/sbin/sestatus | grep -c '^Mode from config file:.*enforcing') -eq 1 ]").and_return(true) + end +end + +shared_context 'non_redhat_stubs' do + before do + stub_command("[ ! -e /etc/httpd/conf/httpd.conf ] && [ -e /etc/redhat-release ] && [ $(/sbin/sestatus | grep -c '^Current mode:.*enforcing') -eq 1 ]").and_return(false) + stub_command("[ -e /etc/httpd/conf/httpd.conf ] && [ -e /etc/redhat-release ] && [ $(/sbin/sestatus | grep -c '^Current mode:.*permissive') -eq 1 ] && [ $(/sbin/sestatus | grep -c '^Mode from config file:.*enforcing') -eq 1 ]").and_return(false) + end +end + +shared_context 'postgresql_backend' do + before do + Chef::Recipe.any_instance.stub(:db) + .with('dashboard') + .and_return('service_type' => 'postgresql', 'db_name' => 'flying_elephant') + end +end + +shared_context 'mysql_backend' do + before do + Chef::Recipe.any_instance.stub(:db) + .with('dashboard') + .and_return('service_type' => 'mysql', 'db_name' => 'flying_dolphin') + end end diff --git a/chef/cookbooks/openstack-dashboard/templates/default/dash-site.erb b/chef/cookbooks/openstack-dashboard/templates/default/dash-site.erb index 922eca0..4ef18d0 100644 --- a/chef/cookbooks/openstack-dashboard/templates/default/dash-site.erb +++ b/chef/cookbooks/openstack-dashboard/templates/default/dash-site.erb @@ -1,28 +1,34 @@ <%= node["openstack"]["dashboard"]["custom_template_banner"] %> - + +<% unless node['apache']['listen_ports'].map(&:to_i).uniq.include?(node['openstack']['dashboard']['http_port'].to_i) %> +Listen *:<%= node['openstack']['dashboard']['http_port'].to_i%> +NameVirtualHost *:<%= node['openstack']['dashboard']['http_port'].to_i%> +<% end -%> +> <% if node["openstack"]["dashboard"]["server_hostname"] -%> ServerName <%= node["openstack"]["dashboard"]["server_hostname"] %> <% end -%> -<% if node["openstack"]["dashboard"]["use_ssl"] %> +<% if eval(node['openstack']['dashboard']['use_ssl']) -%> RewriteEngine On RewriteCond %{HTTPS} off +<% if node['openstack']['dashboard']['http_port'].to_i != 80 or node['openstack']['dashboard']['https_port'].to_i != 443 %> + RewriteRule ^(.*)$ https://%{SERVER_NAME}:<%= node['openstack']['dashboard']['https_port'] %>%{REQUEST_URI} [L,R] +<% else -%> RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R] +<% end -%> - -<% if %w{debian ubuntu}.include?(node["platform"]) -%> - WSGISocketPrefix /var/run/apache2/wsgi -<% else -%> - WSGISocketPrefix run/wsgi +<% unless node['apache']['listen_ports'].map(&:to_i).uniq.include?(node['openstack']['dashboard']['https_port'].to_i) %> +Listen *:<%= node['openstack']['dashboard']['https_port'].to_i%> +NameVirtualHost *:<%= node['openstack']['dashboard']['https_port'].to_i%> <% end -%> - - +> <% if node["openstack"]["dashboard"]["server_hostname"] -%> ServerName <%= node["openstack"]["dashboard"]["server_hostname"] %> <% end -%> -<% end %> +<% end -%> ServerAdmin <%= node["apache"]["contact"] %> - WSGIScriptAlias / <%= node["openstack"]["dashboard"]["wsgi_path"] %> - WSGIDaemonProcess dashboard user=<%= node["apache"]["user"] %> group=<%= node["apache"]["group"] %> processes=3 threads=10 python-path=<%= node["openstack"]["dashboard"]["dash_path"] %> + WSGIScriptAlias <%= node["openstack"]["dashboard"]["webroot"] %> <%= node["openstack"]["dashboard"]["wsgi_path"] %> + WSGIDaemonProcess dashboard user=<%= node['openstack']['dashboard']['horizon_user'] %> group=<%= node['openstack']['dashboard']['horizon_group'] %> processes=3 threads=10 python-path=<%= node["openstack"]["dashboard"]["dash_path"] %> WSGIProcessGroup dashboard DocumentRoot <%= node["openstack"]["dashboard"]["dash_path"] %>/.blackhole/ @@ -47,11 +53,11 @@ allow from all - <% if node["openstack"]["dashboard"]["use_ssl"] %> + <% if eval(node['openstack']['dashboard']['use_ssl']) -%> SSLEngine on SSLCertificateFile <%= @ssl_cert_file %> SSLCertificateKeyFile <%= @ssl_key_file %> - <% end %> + <% end -%> # Allow custom files to overlay the site (such as logo.png) RewriteEngine On @@ -62,3 +68,7 @@ LogLevel warn CustomLog <%= node["apache"]["log_dir"] %>/<%= node["openstack"]["dashboard"]["access_log"] %> combined + +<% unless node["openstack"]["dashboard"]["wsgi_socket_prefix"].nil? %> +WSGISocketPrefix <%= node["openstack"]["dashboard"]["wsgi_socket_prefix"] %> +<% end %> diff --git a/chef/cookbooks/openstack-dashboard/templates/default/local_settings.py.erb b/chef/cookbooks/openstack-dashboard/templates/default/local_settings.py.erb index 55de5fe..665eab1 100644 --- a/chef/cookbooks/openstack-dashboard/templates/default/local_settings.py.erb +++ b/chef/cookbooks/openstack-dashboard/templates/default/local_settings.py.erb @@ -9,13 +9,22 @@ from openstack_dashboard import exceptions DEBUG = <%= node["openstack"]["dashboard"]["debug"] ? "True" : "False" %> TEMPLATE_DEBUG = DEBUG -<% if %w(fedora redhat centos scientific).include? node.platform -%> -WEBROOT='' -LOGIN_URL = WEBROOT+'/auth/login/' -LOGOUT_URL = WEBROOT+'/auth/logout/' -#LOGIN_REDIRECT_URL = WEBROOT+'/syspanel' -LOGIN_REDIRECT_URL = WEBROOT+'/' +<% if node["openstack"]["dashboard"]["login_url"] %> +LOGIN_URL = '<%= node['openstack']['dashboard']['login_url'] %>' <% end %> +<% if node["openstack"]["dashboard"]["logout_url"] %> +LOGOUT_URL = '<%= node['openstack']['dashboard']['logout_url'] %>' +<% end %> +<% if node["openstack"]["dashboard"]["login_redirect_url"] %> +LOGIN_REDIRECT_URL = '<%= node['openstack']['dashboard']['login_redirect_url'] %>' +<% end %> + +# Required for Django 1.5. +# If horizon is running in production (DEBUG is False), set this +# with the list of host/domain names that the application can serve. +# For more information see: +# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = <%= node['openstack']['dashboard']['allowed_hosts'] %> <% if node["openstack"]["dashboard"]["ssl_offload"] %> # Set SSL proxy settings: @@ -28,8 +37,33 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') # If Horizon is being served through SSL, then uncomment the following two # settings to better secure the cookies from security exploits -#CSRF_COOKIE_SECURE = True -#SESSION_COOKIE_SECURE = True +<% if eval(node['openstack']['dashboard']['use_ssl']) -%> +CSRF_COOKIE_SECURE = <%= node["openstack"]["dashboard"]["csrf_cookie_secure"] ? "True" : "False" %> +SESSION_COOKIE_SECURE = <%= node["openstack"]["dashboard"]["session_cookie_secure"] ? "True" : "False" %> +<% end -%> + +# Overrides for OpenStack API versions. Use this setting to force the +# OpenStack dashboard to use a specfic API version for a given service API. +# NOTE: The version should be formatted as it appears in the URL for the +# service API. For example, The identity service APIs have inconsistent +# use of the decimal point, so valid options would be "2.0" or "3". +OPENSTACK_API_VERSIONS = { + "identity": <%= node["openstack"]["dashboard"]["identity_api_version"] %> +} + +# Set this to True if running on multi-domain model. When this is enabled, it +# will require user to enter the Domain name in addition to username for login. +OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = <%= node["openstack"]["dashboard"]["keystone_multidomain_support"] ? "True" : "False" %> + +# Overrides the default domain used when running on single-domain model +# with Keystone V3. All entities will be created in the default domain. +<% if node["openstack"]["dashboard"]["identity_api_version"] == 3 %> +OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = "<%= node["openstack"]["dashboard"]["keystone_default_domain"] %>" +<% end %> + +# Set Console type: +# valid options would be "AUTO", "VNC" or "SPICE" +CONSOLE_TYPE = "<%= node["openstack"]["dashboard"]["console_type"] %>" # Default OpenStack Dashboard configuration. HORIZON_CONFIG = { @@ -42,7 +76,7 @@ HORIZON_CONFIG = { 'fade_duration': 1500, 'types': ['alert-success', 'alert-info'] }, - 'help_url': "http://docs.openstack.org", + 'help_url': "<%= node["openstack"]["dashboard"]["help_url"] %>", 'exceptions': {'recoverable': exceptions.RECOVERABLE, 'not_found': exceptions.NOT_FOUND, 'unauthorized': exceptions.UNAUTHORIZED}, @@ -56,10 +90,10 @@ HORIZON_CONFIG = { # Disable simplified floating IP address management for deployments with # multiple floating IP pools or complex network requirements. -# HORIZON_CONFIG["simple_ip_management"] = False +HORIZON_CONFIG["simple_ip_management"] = <%= node['openstack']['dashboard']['simple_ip_management'] ? 'True' : 'False' %> # Turn off browser autocompletion for the login form if so desired. -# HORIZON_CONFIG["password_autocomplete"] = "off" +HORIZON_CONFIG["password_autocomplete"] = "<%= node['openstack']['dashboard']['password_autocomplete'] %>" LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) @@ -72,8 +106,8 @@ LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) # behind a load-balancer). Either you have to make sure that a session gets all # requests routed to the same dashboard instance or you set the same SECRET_KEY # for all of them. -# from horizon.utils import secret_key -# SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH, '.secret_key_store')) +from horizon.utils import secret_key +SECRET_KEY = secret_key.generate_or_read_from_file(os.path.realpath('<%= node['openstack']['dashboard']['secret_key_path'] %>')) # We recommend you use memcached for development; otherwise after every reload # of the django development server, you will have to login again. To use @@ -99,6 +133,8 @@ CACHES = { when "sql" %> SESSION_ENGINE = 'django.contrib.sessions.backends.db' +<% when "signed_cookies" %> +SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' <% end %> # Send email to the console by default @@ -132,31 +168,63 @@ OPENSTACK_KEYSTONE_DEFAULT_ROLE = "<%= node["openstack"]["dashboard"]["keystone_ # # TODO(tres): Remove these once Keystone has an API to identify auth backend. OPENSTACK_KEYSTONE_BACKEND = { - 'name': 'native', - 'can_edit_user': True, - 'can_edit_project': True + 'name': '<%= node["openstack"]["dashboard"]["keystone_backend"]["name"] %>', + 'can_edit_user': <%= node["openstack"]["dashboard"]["keystone_backend"]["can_edit_user"] ? "True" : "False" %>, + 'can_edit_group': <%= node["openstack"]["dashboard"]["keystone_backend"]["can_edit_group"] ? "True" : "False" %>, + 'can_edit_project': <%= node["openstack"]["dashboard"]["keystone_backend"]["can_edit_project"] ? "True" : "False" %>, + 'can_edit_domain': <%= node["openstack"]["dashboard"]["keystone_backend"]["can_edit_domain"] ? "True" : "False" %>, + 'can_edit_role': <%= node["openstack"]["dashboard"]["keystone_backend"]["can_edit_role"] ? "True" : "False" %>, } OPENSTACK_HYPERVISOR_FEATURES = { 'can_set_mount_point': True, - - # NOTE: as of Grizzly this is not yet supported in Nova so enabling this - # setting will not do anything useful - 'can_encrypt_volumes': False } -# The OPENSTACK_QUANTUM_NETWORK settings can be used to enable optional -# services provided by quantum. Currently only the load balancer service -# is available. -OPENSTACK_QUANTUM_NETWORK = { - 'enable_lb': False +# The OPENSTACK_NEUTRON_NETWORK settings can be used to enable optional +# services provided by neutron. Options currenly available are load +# balancer service, security groups, quotas, VPN service. +OPENSTACK_NEUTRON_NETWORK = { + 'enable_lb': <%= node['openstack']['dashboard']['neutron']['enable_lb'] ? 'True' : 'False' %>, + 'enable_firewall': False, + 'enable_quotas': <%= node['openstack']['dashboard']['neutron']['enable_quotas'] ? 'True' : 'False' %>, + 'enable_vpn': False, + # The profile_support option is used to detect if an external router can be + # configured via the dashboard. When using specific plugins the + # profile_support can be turned on if needed. + 'profile_support': None, + #'profile_support': 'cisco', } +# The OPENSTACK_IMAGE_BACKEND settings can be used to customize features +# in the OpenStack Dashboard related to the Image service, such as the list +# of supported image formats. +# OPENSTACK_IMAGE_BACKEND = { +# 'image_formats': [ +# ('', ''), +# ('aki', _('AKI - Amazon Kernel Image')), +# ('ami', _('AMI - Amazon Machine Image')), +# ('ari', _('ARI - Amazon Ramdisk Image')), +# ('iso', _('ISO - Optical Disk Image')), +# ('qcow2', _('QCOW2 - QEMU Emulator')), +# ('raw', _('Raw')), +# ('vdi', _('VDI')), +# ('vhd', _('VHD')), +# ('vmdk', _('VMDK')) +# ] +# } + # OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints # in the Keystone service catalog. Use this setting when Horizon is running # external to the OpenStack environment. The default is 'internalURL'. #OPENSTACK_ENDPOINT_TYPE = "publicURL" +# SECONDARY_ENDPOINT_TYPE specifies the fallback endpoint type to use in the +# case that OPENSTACK_ENDPOINT_TYPE is not present in the endpoints +# in the Keystone service catalog. Use this setting when Horizon is running +# external to the OpenStack environment. The default is None. This +# value should differ from OPENSTACK_ENDPOINT_TYPE if used. +#SECONDARY_ENDPOINT_TYPE = "publicURL" + # The number of objects (Swift containers/objects or images) to display # on a single page before providing a paging element (a "more" link) # to paginate results. @@ -167,6 +235,36 @@ API_RESULT_PAGE_SIZE = 20 # of your entire OpenStack installation, and hopefully be in UTC. TIME_ZONE = "UTC" +# When launching an instance, the menu of available flavors is +# sorted by RAM usage, ascending. Provide a callback method here +# (and/or a flag for reverse sort) for the sorted() method if you'd +# like a different behaviour. For more info, see +# http://docs.python.org/2/library/functions.html#sorted +# CREATE_INSTANCE_FLAVOR_SORT = { +# 'key': my_awesome_callback_method, +# 'reverse': False, +# } + +# The Horizon Policy Enforcement engine uses these values to load per service +# policy rule files. The content of these files should match the files the +# OpenStack services are using to determine role based access control in the +# target installation. + +# Path to directory containing policy.json files +#POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf") +# Map of local copy of service policy files +#POLICY_FILES = { +# 'identity': 'keystone_policy.json', +# 'compute': 'nova_policy.json' +#} + +# Trove user and database extension support. By default support for +# creating users and databases on database instances is turned on. +# To disable these extensions set the permission here to something +# unusable such as ["!"]. +# TROVE_ADD_USER_PERMS = [] +# TROVE_ADD_DATABASE_PERMS = [] + LOGGING = { 'version': 1, # When set to True this will disable all logging except @@ -198,45 +296,204 @@ LOGGING = { }, 'horizon': { 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["horizon"] %>', 'propagate': False, }, 'openstack_dashboard': { 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["openstack_dashboard"] %>', 'propagate': False, }, 'novaclient': { 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["novaclient"] %>', + 'propagate': False, + }, + 'cinderclient': { + 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["cinderclient"] %>', 'propagate': False, }, 'keystoneclient': { 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["keystoneclient"] %>', 'propagate': False, }, 'glanceclient': { 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["glanceclient"] %>', + 'propagate': False, + }, + 'neutronclient': { + 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["neutronclient"] %>', + 'propagate': False, + }, + 'heatclient': { + 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["heatclient"] %>', + 'propagate': False, + }, + 'ceilometerclient': { + 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["ceilometerclient"] %>', + 'propagate': False, + }, + 'troveclient': { + 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["troveclient"] %>', + 'propagate': False, + }, + 'swiftclient': { + 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["swiftclient"] %>', + 'propagate': False, + }, + 'openstack_auth': { + 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["openstack_auth"] %>', 'propagate': False, }, 'nose.plugins.manager': { 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["nose.plugins.manager"] %>', 'propagate': False, - } + }, + 'django': { + 'handlers': ['console'], + 'level': '<%= node["openstack"]["dashboard"]["log_level"]["django"] %>', + 'propagate': False, + }, + 'iso8601': { + 'handlers': ['null'], + 'propagate': False, + }, } } -<% django_backends = {'mysql' => 'mysql', - 'postgresql' => 'postgresql_psycopg2'} - engine = django_backends[@db_info['db_type']] %> +SECURITY_GROUP_RULES = { + 'all_tcp': { + 'name': 'ALL TCP', + 'ip_protocol': 'tcp', + 'from_port': '1', + 'to_port': '65535', + }, + 'all_udp': { + 'name': 'ALL UDP', + 'ip_protocol': 'udp', + 'from_port': '1', + 'to_port': '65535', + }, + 'all_icmp': { + 'name': 'ALL ICMP', + 'ip_protocol': 'icmp', + 'from_port': '-1', + 'to_port': '-1', + }, + 'ssh': { + 'name': 'SSH', + 'ip_protocol': 'tcp', + 'from_port': '22', + 'to_port': '22', + }, + 'smtp': { + 'name': 'SMTP', + 'ip_protocol': 'tcp', + 'from_port': '25', + 'to_port': '25', + }, + 'dns': { + 'name': 'DNS', + 'ip_protocol': 'tcp', + 'from_port': '53', + 'to_port': '53', + }, + 'http': { + 'name': 'HTTP', + 'ip_protocol': 'tcp', + 'from_port': '80', + 'to_port': '80', + }, + 'pop3': { + 'name': 'POP3', + 'ip_protocol': 'tcp', + 'from_port': '110', + 'to_port': '110', + }, + 'imap': { + 'name': 'IMAP', + 'ip_protocol': 'tcp', + 'from_port': '143', + 'to_port': '143', + }, + 'ldap': { + 'name': 'LDAP', + 'ip_protocol': 'tcp', + 'from_port': '389', + 'to_port': '389', + }, + 'https': { + 'name': 'HTTPS', + 'ip_protocol': 'tcp', + 'from_port': '443', + 'to_port': '443', + }, + 'smtps': { + 'name': 'SMTPS', + 'ip_protocol': 'tcp', + 'from_port': '465', + 'to_port': '465', + }, + 'imaps': { + 'name': 'IMAPS', + 'ip_protocol': 'tcp', + 'from_port': '993', + 'to_port': '993', + }, + 'pop3s': { + 'name': 'POP3S', + 'ip_protocol': 'tcp', + 'from_port': '995', + 'to_port': '995', + }, + 'ms_sql': { + 'name': 'MS SQL', + 'ip_protocol': 'tcp', + 'from_port': '1433', + 'to_port': '1433', + }, + 'mysql': { + 'name': 'MYSQL', + 'ip_protocol': 'tcp', + 'from_port': '3306', + 'to_port': '3306', + }, + 'rdp': { + 'name': 'RDP', + 'ip_protocol': 'tcp', + 'from_port': '3389', + 'to_port': '3389', + }, +} + +<% django_backends = {'mysql' => 'django.db.backends.mysql', + 'sqlite' => 'django.db.backends.sqlite3', + 'postgresql' => 'django.db.backends.postgresql_psycopg2', + 'db2' => 'ibm_db_django'} + engine = django_backends[@db_info['service_type']] %> # A dictionary containing the settings for all databases to be used with # Django. It is a nested dictionary whose contents maps database aliases # to a dictionary containing the options for an individual database. DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.<%= engine %>', + 'ENGINE': '<%= engine %>', 'NAME': '<%= @db_info["db_name"] %>', - 'USER': '<%= node["openstack"]["dashboard"]["db"]["username"] %>', +<% unless @db_info['service_type'] == 'sqlite' %> + 'USER': '<%= node["openstack"]["db"]["dashboard"]["username"] %>', 'PASSWORD': '<%= @db_pass %>', 'HOST': '<%= @db_info["host"] %>', +<% end %> 'default-character-set': 'utf8' }, } diff --git a/chef/cookbooks/openstack-identity/.rubocop.yml b/chef/cookbooks/openstack-identity/.rubocop.yml new file mode 100644 index 0000000..94a5234 --- /dev/null +++ b/chef/cookbooks/openstack-identity/.rubocop.yml @@ -0,0 +1,31 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 + +# TODO(galstom21) +# The rescue exception statements in providers/**.rb need to be modified, +# to rescue specific exceptions. +RescueException: + Exclude: + - providers/register.rb diff --git a/chef/cookbooks/openstack-identity/.tailor b/chef/cookbooks/openstack-identity/.tailor deleted file mode 100644 index 99f0dcf..0000000 --- a/chef/cookbooks/openstack-identity/.tailor +++ /dev/null @@ -1,25 +0,0 @@ -Tailor.config do |config| - config.formatters "text" - config.file_set '**/*.rb' do |style| - style.max_line_length 80, level: :off - style.allow_camel_case_methods false, level: :error - style.allow_hard_tabs false, level: :error - style.allow_screaming_snake_case_classes false, level: :error - style.allow_trailing_line_spaces false, level: :error - style.allow_invalid_ruby false, level: :warn - style.indentation_spaces 2, level: :error - style.max_code_lines_in_class 300, level: :error - style.max_code_lines_in_method 30, level: :error - style.spaces_after_comma 1, level: :error - style.spaces_after_lbrace 1, level: :error - style.spaces_after_lbracket 0, level: :error - style.spaces_after_lparen 0, level: :error - style.spaces_before_comma 0, level: :error - style.spaces_before_lbrace 1, level: :error - style.spaces_before_rbrace 1, level: :error - style.spaces_before_rbracket 0, level: :error - style.spaces_before_rparen 0, level: :error - style.spaces_in_empty_braces 0, level: :error - style.trailing_newlines 1, level: :error - end -end diff --git a/chef/cookbooks/openstack-identity/Berksfile.lock b/chef/cookbooks/openstack-identity/Berksfile.lock deleted file mode 100644 index 368e102..0000000 --- a/chef/cookbooks/openstack-identity/Berksfile.lock +++ /dev/null @@ -1,37 +0,0 @@ -{ - "sha": "591cb6e4f1ccfb699c80c54dca3009a15e14b06f", - "sources": { - "openstack-identity": { - "path": "." - }, - "openstack-common": { - "locked_version": "0.3.0", - "git": "git://github.com/stackforge/cookbook-openstack-common.git", - "ref": "ae80d36e8f8d5705e01bb6c14238eccb5450a229" - }, - "apt": { - "locked_version": "2.0.0" - }, - "database": { - "locked_version": "1.4.0" - }, - "mysql": { - "locked_version": "3.0.2" - }, - "openssl": { - "locked_version": "1.0.2" - }, - "build-essential": { - "locked_version": "1.4.0" - }, - "postgresql": { - "locked_version": "3.0.2" - }, - "aws": { - "locked_version": "0.101.2" - }, - "xfs": { - "locked_version": "1.1.0" - } - } -} diff --git a/chef/cookbooks/openstack-identity/CHANGELOG.md b/chef/cookbooks/openstack-identity/CHANGELOG.md new file mode 100644 index 0000000..41b2f22 --- /dev/null +++ b/chef/cookbooks/openstack-identity/CHANGELOG.md @@ -0,0 +1,57 @@ +# CHANGELOG for cookbook-openstack-identity +This file is used to list changes made in each version of cookbook-openstack-identity. +## 9.2.1 +* Add support for TLS in [ldap] + +## 9.2.0 +* Add support for miscellaneous options (like in Compute) + +## 9.1.1 +* Fix package action to allow updates + +## 9.1.0 +* Add token flushing cronjob + +## 9.0.0 +* Upgrade to Icehouse + +## 8.1.3 +* Remove duplicate service and admin ports attributes that are in Common LP1281108 + +## 8.1.2 +### Bug +* Fix the DB2 ODBC driver issue + +## 8.1.1 +* Adding guard on register LWRP (:create_service) to not run if backend is 'templated' +* Adding guard on register LWRP (:create_endpoint) to not run if backend is 'templated' + +## 8.1.0 +* Add client recipe + +## 8.0.0 +* Updating to Havana +* Updating cookbook-openstack-common dep from 0.3.0 to 0.4.7 + +## 7.2.0: +* Allow orchestration and cloudformation as service/endpoint types. + +## 7.1.0: +* Add new attribute default["openstack"]["identity"]["policy"]["backend"], default is 'sql'. + +## 7.0.2: +### Bug +* Do not delete the sqlite database when node.openstack.db.identity.db_type is set to sqlite. +* Added `does not delete keystone.db when configured to use sqlite` test case for this scenario + +## 7.0.1: +* Fixed _python_packages issue when setting node.openstack.db.identity.db_type to sqlite. +* Added `converges when configured to use sqlite db backend` test case for this scenario. + +## 7.0.0: +* Initial release of cookbook-openstack-identity. + +- - - +Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. + +The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. diff --git a/chef/cookbooks/openstack-identity/Gemfile b/chef/cookbooks/openstack-identity/Gemfile index 04ef97e..ccfa456 100644 --- a/chef/cookbooks/openstack-identity/Gemfile +++ b/chef/cookbooks/openstack-identity/Gemfile @@ -1,9 +1,9 @@ -source "https://rubygems.org" +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 2.0.3" -gem "chefspec", "~> 1.3.0" -gem "foodcritic" -gem "strainer" -gem "tailor" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' diff --git a/chef/cookbooks/openstack-identity/Gemfile.lock b/chef/cookbooks/openstack-identity/Gemfile.lock index 2d00f7d..2954927 100644 --- a/chef/cookbooks/openstack-identity/Gemfile.lock +++ b/chef/cookbooks/openstack-identity/Gemfile.lock @@ -1,34 +1,36 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.0.0) + activesupport (3.2.17) i18n (~> 0.6, >= 0.6.4) - minitest (~> 4.2) - multi_json (~> 1.3) - thread_safe (~> 0.1) - tzinfo (~> 0.3.37) - addressable (2.3.5) - akami (1.2.0) + multi_json (~> 1.0) + addressable (2.3.6) + akami (1.2.1) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - atomic (1.1.10) - berkshelf (2.0.5) - activesupport (>= 3.2.0) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) + activesupport (~> 3.2.0) addressable (~> 2.3.4) buff-shell_out (~> 0.1) - celluloid (>= 0.14.0) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) minitar (~> 0.5.4) rbzip2 (~> 0.2.0) retryable (~> 1.3.3) - ridley (~> 1.2.1) + ridley (~> 1.5.0) solve (>= 0.5.0) thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) buff-ruby_engine (0.1.0) - buff-shell_out (0.1.0) + buff-shell_out (0.1.1) buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) @@ -36,113 +38,127 @@ GEM celluloid-io (0.14.1) celluloid (>= 0.14.1) nio4r (>= 0.4.5) - chef (11.4.4) - erubis - highline (>= 1.6.9) - json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) net-ssh (~> 2.6) - net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (1.3.1) - chef (>= 10.0) - erubis - fauxhai (>= 0.1.1, < 2.0) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.8.4) - builder (>= 2.1.2) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.7) - multipart-post (~> 1.1) - fauxhai (1.1.1) - httparty + faraday (0.8.9) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) net-ssh ohai - ffi (1.9.0) - foodcritic (2.1.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) - rak (~> 1.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.0.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.4) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - mime-types (1.23) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.5) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.1.0) - multi_json (1.7.7) - multi_xml (0.5.4) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) multipart-post (1.2.0) - net-http-persistent (2.8) - net-ssh (2.6.7) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - net-ssh-multi (1.1) - net-ssh (>= 2.1.4) - net-ssh-gateway (>= 0.99.0) - nio4r (0.4.6) - nokogiri (1.5.10) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.16.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) - rak (1.4) + rainbow (2.0.0) + rake (10.2.2) rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (1.2.3) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) buff-extensions (~> 0.3) + buff-ignore (~> 1.1) buff-shell_out (~> 0.1) celluloid (~> 0.14.0) celluloid-io (~> 0.14.0) @@ -153,18 +169,24 @@ GEM mixlib-authentication (>= 1.3.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) varia_model (~> 0.1) winrm (~> 1.1.0) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.1) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) savon (0.9.5) akami (~> 1.0) @@ -174,37 +196,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.6.0) - strainer (3.0.3) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - text-table (1.2.3) thor (0.18.1) - thread_safe (0.1.0) - atomic - timers (1.1.0) - tins (0.8.2) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.37) uuidtools (2.1.4) - varia_model (0.1.0) - buff-extensions (~> 0.1) + varia_model (0.3.2) + buff-extensions (~> 0.2) hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -214,10 +228,10 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 2.0.3) - chef (~> 11.4.4) - chefspec (~> 1.3.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - tailor diff --git a/chef/cookbooks/openstack-identity/README.md b/chef/cookbooks/openstack-identity/README.md index be43628..6d7a73a 100644 --- a/chef/cookbooks/openstack-identity/README.md +++ b/chef/cookbooks/openstack-identity/README.md @@ -20,6 +20,12 @@ The following cookbooks are dependencies: Usage ===== +client +------ + +Installs the keystone client packages + + server ------ @@ -59,6 +65,7 @@ Register users, tenants, roles, services and endpoints with Keystone - api_ver: API Version for Keystone server - Accepted values are [ "/v2.0" ] - auth_token: Auth Token for communication with Keystone server +- misc_keystone: Array of strings to be added to the keystone.conf file ### :create_tenant Specific Attributes @@ -92,7 +99,8 @@ Register users, tenants, roles, services and endpoints with Keystone - service_name: Name of service - service_description: Description of service - service_type: Type of service to create - - Accepted values are [ "image", "identity", "compute", "storage", "ec2", "volume" ] + - Accepted values are [ "image", "identity", "compute", "storage", "ec2", "volume", "object-store", "metering", "network", "orchestration", "cloudformation" ] +- **NOTE:** call will be skipped if `openstack['identity']['catalog']['backend']` is set to 'templated' ### :create_endpoint Specific Attributes @@ -102,7 +110,8 @@ Register users, tenants, roles, services and endpoints with Keystone - endpoint_publicurl: URL to public endpoint - Default is same as endpoint_internalURL - service_type: Type of service to create endpoint for - - Accepted values are [ "image", "identity", "compute", "storage", "ec2", "volume" ] + - Accepted values are [ "image", "identity", "compute", "storage", "ec2", "volume", "object-store", "metering", "network", "orchestration", "cloudformation" ] +- **NOTE:** call will be skipped if `openstack['identity']['catalog']['backend']` is set to 'templated' ### Examples @@ -224,10 +233,9 @@ Create EC2 credentials for a given user in the specified tenant Attributes ========== +Please refer to the Common cookbook for more attributes. + * `openstack['identity']['db_server_chef_role']` - The name of the Chef role that knows about the db server -* `openstack['identity']['bind_interface']` - Interface to bind keystone to -* `openstack['identity']['service_port']` - Port to listen on for client functions -* `openstack['identity']['admin_port']` - Port to listen on for admin functions * `openstack['identity']['user']` - User keystone runs as * `openstack['identity']['group']` - Group keystone runs as * `openstack['identity']['db']` - Name of keystone database @@ -237,24 +245,78 @@ Attributes * `openstack['identity']['api_ipaddress']` - IP address for the keystone API to bind to. _TODO_: Rename to bind_address * `openstack['identity']['verbose']` - Enables/disables verbose output for keystone API server * `openstack['identity']['debug']` - Enables/disables debug output for keystone API server -* `openstack['identity']['service_port']` - Port for the keystone service API to bind to -* `openstack['identity']['admin_port']` - Port for the keystone admin service to bind to * `openstack['identity']['admin_token']` - Admin token for bootstraping keystone server * `openstack['identity']['roles']` - Array of roles to create in the keystone server * `openstack['identity']['users']` - Array of users to create in the keystone server +* `openstack['identity']['pastefile_url']` - Specify the URL for a keystone-paste.ini file that will override the default packaged file +TODO: Add DB2 support on other platforms +* `openstack['identity']['platform']['db2_python_packages']` - Array of DB2 python packages, only available on redhat platform +* `openstack['identity']['token']['expiration']` - Token validity time in seconds +* `openstack['identity']['catalog']['backend']` - Storage mechanism for the keystone service catalog +* `openstack['identity']["control_exchange"]` - The AMQP exchange to connect to if using RabbitMQ or Qpid, defaults to openstack +* `openstack['identity']['rpc_backend']` - The messaging module to use +* `openstack['identity']['rpc_thread_pool_size']` - Size of RPC thread pool +* `openstack['identity']['rpc_conn_pool_size']` - Size of RPC connection pool +* `openstack['identity']['rpc_response_timeout']` - Seconds to wait for a response from call or multicall +* `openstack['identity']['ldap']['url']` - LDAP host URL (default: 'ldap://localhost') +* `openstack['identity']['ldap']['user']` - LDAP bind DN (default: 'dc=Manager,dc=example,dc=com') +* `openstack['identity']['ldap']['password']` - LDAP bind password (default: nil) +* `openstack['identity']['ldap']['use_tls']` - Use TLS for LDAP (default: false) +* `openstack['identity']['ldap']['tls_cacertfile']` - Path to CA cert file (default: nil) +* `openstack['identity']['ldap']['tls_cacertdir']` - Path to CA cert directory (default: nil) +* `openstack['identity']['ldap']['tls_req_cert']` - CA cert check ('demand', 'allow' or 'never', default: 'demand') +* `openstack['identity']['misc_keystone']` - **Array of strings to be added to keystone.conf** + +Most `openstack['identity']['ldap']` attributes map directly to the corresponding config options in keystone.conf's `[ldap]` backend. They are primarily used when configuring `openstack['identity']['identity']['backend']` and/or `openstack["identity"]["assignment"]["backend"]` as `ldap` (both default to `sql`). + +The `openstack['identity']['ldap']['use_tls']` option should not be used in conjunction with an `ldaps://` url. When the latter is used (and `openstack['identity']['ldap']['use_tls'] = false`), the certificate path and validation will instead be subject to the OS's LDAP config. + +If `openstack['identity']['ldap']['tls_cacertfile']` is set, `openstack['identity']['ldap']['tls_cacertdir']` will be ignored. Set `openstack['identity']['ldap']['tls_cacertfile']` to `nil` if `openstack['identity']['ldap']['tls_cacertdir']` is desired. +Values of `openstack['identity']['ldap']['tls_req_cert']` correspond to the standard options permitted by the TLS_REQCERT TLS option (`never` performs no validation of certs, `allow` performs some basic name checks but no thorough CA validation, `demand` requires the certificate chain to be valid for the connection to succeed). + + +The following attributes are defined in attributes/default.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack['endpoints']['identity-bind']['host']` - The IP address to bind the identity services to +* `openstack['endpoints']['identity-bind']['scheme']` - Unused +* `openstack['endpoints']['identity-bind']['port']` - Unused +* `openstack['endpoints']['identity-bind']['path']` - Unused +* `openstack['endpoints']['identity-bind']['bind_interface']` - The interface name to bind the identity services to + +If the value of the 'bind_interface' attribute is non-nil, then the identity service will be bound to the first IP address on that interface. If the value of the 'bind_interface' attribute is nil, then the identity service will be bound to the IP address specified in the host attribute. + + + +### Token flushing +When managing tokens with an SQL backend the token database may grow unboundedly as new tokens are issued and expired +tokens are not disposed of. Expired tokens may need to be kept around in order to allow for auditability. + +It is up to deployers to define when their tokens can be safely deleted. Keystone provides a tool to purge expired tokens, +and the server recipe can create a cronjob to run that tool. By default the cronjob will be configured to run hourly. + +The flush tokens cronjob configuration parameters are listed below: + +* `openstack['identity']['token_flush_cron']['enabled']` - Boolean indicating whether the flush tokens cronjob is enabled. It is by default enabled if the token backend is 'sql'. +* `openstack['identity']['token_flush_cron']['log_file']` - The log file for the flush tokens tool. +* `openstack['identity']['token_flush_cron']['hour']` - The hour at which the flush tokens cronjob should run (values 0 - 23). +* `openstack['identity']['token_flush_cron']['minute']` - The minute at which the flush tokens cronjob should run (values 0 - 59). +* `openstack']['identity']['token_flush_cron']['day']` - The day of the month when the flush tokens cronjob should run (values 1 - 31). +* `openstack['identity']['token_flush_cron']['weekday']` = The day of the week at which the flush tokens cronjob should run (values 0 - 6, where Sunday is 0). Testing ===== -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. -Tests are defined in Strainerfile. +Berkshelf +===== -To run tests: - - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -271,11 +333,14 @@ Author:: Jay Pipes () Author:: John Dewey () Author:: Sean Gallagher () Author:: Ionut Artarisi () +Author:: Chen Zhiwei (zhiwchen@cn.ibm.com) +Author:: Eric Zhou (zyouzhou@cn.ibm.com) Copyright 2012, Rackspace US, Inc. Copyright 2012-2013, Opscode, Inc. Copyright 2012-2013, AT&T Services, Inc. Copyright 2013, SUSE Linux GmbH +Copyright 2013-2014, IBM, Corp. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/openstack-identity/Strainerfile b/chef/cookbooks/openstack-identity/Strainerfile index 7e292b4..9a2ec64 100644 --- a/chef/cookbooks/openstack-identity/Strainerfile +++ b/chef/cookbooks/openstack-identity/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK -foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-identity/TESTING.md b/chef/cookbooks/openstack-identity/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-identity/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-identity/attributes/default.rb b/chef/cookbooks/openstack-identity/attributes/default.rb index 1c90bf6..b01dcff 100644 --- a/chef/cookbooks/openstack-identity/attributes/default.rb +++ b/chef/cookbooks/openstack-identity/attributes/default.rb @@ -1,9 +1,11 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-identity # Recipe:: default # # Copyright 2012-2013, AT&T Services, Inc. # Copyright 2013, Opscode, Inc. +# Copyright 2013, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,114 +22,208 @@ # Set to some text value if you want templated config files # to contain a custom banner at the top of the written file -default["openstack"]["identity"]["custom_template_banner"] = " +default['openstack']['identity']['custom_template_banner'] = " # This file autogenerated by Chef # Do not edit, changes will be overwritten " # Adding these as blank # this needs to be here for the initial deep-merge to work -default["credentials"]["EC2"]["admin"]["access"] = "" -default["credentials"]["EC2"]["admin"]["secret"] = "" +default['credentials']['EC2']['admin']['access'] = '' +default['credentials']['EC2']['admin']['secret'] = '' -default["openstack"]["identity"]["db"]["username"] = "keystone" -# Execute database migrations. There are cases where migrations should not be -# executed. For example when upgrading a zone, and the identity database is -# replicated across many zones. -default["openstack"]["identity"]["db"]["migrate"] = true +default['openstack']['identity']['verbose'] = 'False' +default['openstack']['identity']['debug'] = 'False' -default["openstack"]["identity"]["verbose"] = "False" -default["openstack"]["identity"]["debug"] = "False" +# Specify a location to retrieve keystone-paste.ini from +default['openstack']['identity']['pastefile_url'] = nil -default["openstack"]["identity"]["service_port"] = "5000" -default["openstack"]["identity"]["admin_port"] = "35357" -default["openstack"]["identity"]["region"] = "RegionOne" - -default["openstack"]["identity"]["bind_interface"] = "lo" +default['openstack']['identity']['region'] = node['openstack']['region'] +default['openstack']['identity']['token']['expiration'] = '86400' # Logging stuff -default["openstack"]["identity"]["syslog"]["use"] = false -default["openstack"]["identity"]["syslog"]["facility"] = "LOG_LOCAL2" -default["openstack"]["identity"]["syslog"]["config_facility"] = "local2" +default['openstack']['identity']['syslog']['use'] = false +default['openstack']['identity']['syslog']['facility'] = 'LOG_LOCAL2' +default['openstack']['identity']['syslog']['config_facility'] = 'local2' -# default["openstack"]["identity"]["roles"] = [ "admin", "Member", "KeystoneAdmin", "KeystoneServiceAdmin", "sysadmin", "netadmin" ] -default["openstack"]["identity"]["roles"] = [ "admin", "Member", "KeystoneAdmin", "KeystoneServiceAdmin" ] +# RPC attributes +default['openstack']['identity']['control_exchange'] = 'openstack' +default['openstack']['identity']['rpc_thread_pool_size'] = 240 +default['openstack']['identity']['rpc_conn_pool_size'] = 100 +default['openstack']['identity']['rpc_response_timeout'] = 60 +case node['openstack']['mq']['service_type'] +when 'rabbitmq' + default['openstack']['identity']['rpc_backend'] = 'rabbit' +when 'qpid' + default['openstack']['identity']['rpc_backend'] = 'qpid' +end -#TODO(shep): this should probably be derived from keystone.users hash keys -default["openstack"]["identity"]["tenants"] = [ "admin", "service"] +default['openstack']['identity']['admin_user'] = 'admin' +default['openstack']['identity']['admin_tenant_name'] = 'admin' -default["openstack"]["identity"]["admin_user"] = "admin" -default["openstack"]["identity"]["admin_tenant_name"] = "admin" - -default["openstack"]["identity"]["users"] = { - default["openstack"]["identity"]["admin_user"] => { - "default_tenant" => default["openstack"]["identity"]["admin_tenant_name"], - "roles" => { - "admin" => [ "admin" ], - "KeystoneAdmin" => [ "admin" ], - "KeystoneServiceAdmin" => [ "admin" ] - } - }, - "monitoring" => { - "password" => "", - "default_tenant" => "service", - "roles" => { - "Member" => [ "admin" ] - } +default['openstack']['identity']['users'] = { + default['openstack']['identity']['admin_user'] => { + 'default_tenant' => default['openstack']['identity']['admin_tenant_name'], + 'roles' => { + 'admin' => ['admin'], + 'KeystoneAdmin' => ['admin'], + 'KeystoneServiceAdmin' => ['admin'] } + } } # PKI signing. Corresponds to the [signing] section of keystone.conf -# Note this section is only written if node["openstack"]["auth"]["straegy"] == "pki" -default["openstack"]["identity"]["signing"]["basedir"] = "/etc/keystone/ssl" -default["openstack"]["identity"]["signing"]["certfile"] = "/etc/keystone/ssl/certs/signing_cert.pem" -default["openstack"]["identity"]["signing"]["keyfile"] = "/etc/keystone/ssl/private/signing_key.pem" -default["openstack"]["identity"]["signing"]["ca_certs"] = "/etc/keystone/ssl/certs/ca.pem" -default["openstack"]["identity"]["signing"]["key_size"] = "1024" -default["openstack"]["identity"]["signing"]["valid_days"] = "3650" -default["openstack"]["identity"]["signing"]["ca_password"] = nil +# Note this section is only written if node['openstack']['auth']['strategy'] == 'pki' +default['openstack']['identity']['signing']['basedir'] = '/etc/keystone/ssl' +default['openstack']['identity']['signing']['certfile'] = "#{node['openstack']['identity']['signing']['basedir']}/certs/signing_cert.pem" +default['openstack']['identity']['signing']['keyfile'] = "#{node['openstack']['identity']['signing']['basedir']}/private/signing_key.pem" +default['openstack']['identity']['signing']['ca_certs'] = "#{node['openstack']['identity']['signing']['basedir']}/certs/ca.pem" +default['openstack']['identity']['signing']['certfile_url'] = nil +default['openstack']['identity']['signing']['keyfile_url'] = nil +default['openstack']['identity']['signing']['ca_certs_url'] = nil +default['openstack']['identity']['signing']['key_size'] = '2048' +default['openstack']['identity']['signing']['valid_days'] = '3650' +default['openstack']['identity']['signing']['ca_password'] = nil # These switches set the various drivers for the different Keystone components -default["openstack"]["identity"]["identity"]["backend"] = "sql" -default["openstack"]["identity"]["token"]["backend"] = "sql" -default["openstack"]["identity"]["catalog"]["backend"] = "sql" +default['openstack']['identity']['identity']['backend'] = 'sql' +default['openstack']['identity']['assignment']['backend'] = 'sql' +# default['openstack']['identity']['token']['backend'] = 'sql' +default['openstack']['identity']['token']['backend'] = 'memcache' +default['openstack']['identity']['catalog']['backend'] = 'sql' +default['openstack']['identity']['policy']['backend'] = 'sql' + +# LDAP backend general settings +default['openstack']['identity']['ldap']['url'] = 'ldap://localhost' +default['openstack']['identity']['ldap']['user'] = 'dc=Manager,dc=example,dc=com' +default['openstack']['identity']['ldap']['password'] = nil +default['openstack']['identity']['ldap']['suffix'] = 'cn=example,cn=com' +default['openstack']['identity']['ldap']['use_dumb_member'] = false +default['openstack']['identity']['ldap']['allow_subtree_delete'] = false +default['openstack']['identity']['ldap']['dumb_member'] = 'cn=dumb,dc=example,dc=com' +default['openstack']['identity']['ldap']['page_size'] = 0 +default['openstack']['identity']['ldap']['alias_dereferencing'] = 'default' +default['openstack']['identity']['ldap']['query_scope'] = 'one' +default['openstack']['identity']['ldap']['use_tls'] = false +default['openstack']['identity']['ldap']['tls_cacertfile'] = nil +default['openstack']['identity']['ldap']['tls_cacertdir'] = nil +default['openstack']['identity']['ldap']['tls_req_cert'] = 'demand' + +# LDAP backend user related settings +default['openstack']['identity']['ldap']['user_tree_dn'] = nil +default['openstack']['identity']['ldap']['user_filter'] = nil +default['openstack']['identity']['ldap']['user_objectclass'] = 'inetOrgPerson' +default['openstack']['identity']['ldap']['user_id_attribute'] = 'cn' +default['openstack']['identity']['ldap']['user_name_attribute'] = 'sn' +default['openstack']['identity']['ldap']['user_mail_attribute'] = 'email' +default['openstack']['identity']['ldap']['user_pass_attribute'] = 'userPassword' +default['openstack']['identity']['ldap']['user_enabled_attribute'] = 'enabled' +default['openstack']['identity']['ldap']['user_domain_id_attribute'] = 'businessCategory' +default['openstack']['identity']['ldap']['user_enabled_mask'] = 0 +default['openstack']['identity']['ldap']['user_enabled_default'] = 'true' +default['openstack']['identity']['ldap']['user_attribute_ignore'] = 'tenant_id,tenants' +default['openstack']['identity']['ldap']['user_allow_create'] = true +default['openstack']['identity']['ldap']['user_allow_update'] = true +default['openstack']['identity']['ldap']['user_allow_delete'] = true +default['openstack']['identity']['ldap']['user_enabled_emulation'] = false +default['openstack']['identity']['ldap']['user_enabled_emulation_dn'] = nil + +# LDAP backend tenant related settings +default['openstack']['identity']['ldap']['tenant_tree_dn'] = nil +default['openstack']['identity']['ldap']['tenant_filter'] = nil +default['openstack']['identity']['ldap']['tenant_objectclass'] = 'groupOfNames' +default['openstack']['identity']['ldap']['tenant_id_attribute'] = 'cn' +default['openstack']['identity']['ldap']['tenant_member_attribute'] = 'member' +default['openstack']['identity']['ldap']['tenant_name_attribute'] = 'ou' +default['openstack']['identity']['ldap']['tenant_desc_attribute'] = 'description' +default['openstack']['identity']['ldap']['tenant_enabled_attribute'] = 'enabled' +default['openstack']['identity']['ldap']['tenant_domain_id_attribute'] = 'businessCategory' +default['openstack']['identity']['ldap']['tenant_attribute_ignore'] = nil +default['openstack']['identity']['ldap']['tenant_allow_create'] = true +default['openstack']['identity']['ldap']['tenant_allow_update'] = true +default['openstack']['identity']['ldap']['tenant_allow_delete'] = true +default['openstack']['identity']['ldap']['tenant_enabled_emulation'] = false +default['openstack']['identity']['ldap']['tenant_enabled_emulation_dn'] = nil + +# LDAP backend role related settings +default['openstack']['identity']['ldap']['role_tree_dn'] = nil +default['openstack']['identity']['ldap']['role_filter'] = nil +default['openstack']['identity']['ldap']['role_objectclass'] = 'organizationalRole' +default['openstack']['identity']['ldap']['role_id_attribute'] = 'cn' +default['openstack']['identity']['ldap']['role_name_attribute'] = 'ou' +default['openstack']['identity']['ldap']['role_member_attribute'] = 'roleOccupant' +default['openstack']['identity']['ldap']['role_attribute_ignore'] = nil +default['openstack']['identity']['ldap']['role_allow_create'] = true +default['openstack']['identity']['ldap']['role_allow_update'] = true +default['openstack']['identity']['ldap']['role_allow_delete'] = true + +# LDAP backend group related settings +default['openstack']['identity']['ldap']['group_tree_dn'] = nil +default['openstack']['identity']['ldap']['group_filter'] = nil +default['openstack']['identity']['ldap']['group_objectclass'] = 'groupOfNames' +default['openstack']['identity']['ldap']['group_id_attribute'] = 'cn' +default['openstack']['identity']['ldap']['group_name_attribute'] = 'ou' +default['openstack']['identity']['ldap']['group_member_attribute'] = 'member' +default['openstack']['identity']['ldap']['group_desc_attribute'] = 'description' +default['openstack']['identity']['ldap']['group_domain_id_attribute'] = 'businessCategory' +default['openstack']['identity']['ldap']['group_attribute_ignore'] = nil +default['openstack']['identity']['ldap']['group_allow_create'] = true +default['openstack']['identity']['ldap']['group_allow_update'] = true +default['openstack']['identity']['ldap']['group_allow_delete'] = true + +# Token flushing cronjob +default['openstack']['identity']['token_flush_cron']['enabled'] = node['openstack']['identity']['token']['backend'] == 'sql' +default['openstack']['identity']['token_flush_cron']['log_file'] = '/var/log/keystone/token-flush.log' +default['openstack']['identity']['token_flush_cron']['hour'] = '*' +default['openstack']['identity']['token_flush_cron']['minute'] = '0' +default['openstack']['identity']['token_flush_cron']['day'] = '*' +default['openstack']['identity']['token_flush_cron']['weekday'] = '*' + +# Misc option support +# Allow additional strings to be added to keystone.conf +# For example: ['# Comment', 'key=value'] +default['openstack']['identity']['misc_keystone'] = [] # platform defaults -case platform -when "fedora", "redhat", "centos" # :pragma-foodcritic: ~FC024 - won't fix this - default["openstack"]["identity"]["user"] = "keystone" - default["openstack"]["identity"]["group"] = "keystone" - default["openstack"]["identity"]["platform"] = { - "mysql_python_packages" => [ "MySQL-python" ], - "postgresql_python_packages" => [ "python-psycopg2" ], - "memcache_python_packages" => [ "python-memcached" ], - "keystone_packages" => [ "openstack-keystone" ], - "keystone_service" => "openstack-keystone", - "keystone_process_name" => "keystone-all", - "package_options" => "" +case platform_family +when 'fedora', 'rhel' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['identity']['user'] = 'keystone' + default['openstack']['identity']['group'] = 'keystone' + default['openstack']['identity']['platform'] = { + 'mysql_python_packages' => ['MySQL-python'], + 'db2_python_packages' => ['python-ibm-db', 'python-ibm-db-sa'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'memcache_python_packages' => ['python-memcached'], + 'keystone_packages' => ['openstack-keystone'], + 'keystone_client_packages' => ['python-keystoneclient'], + 'keystone_service' => 'openstack-keystone', + 'keystone_process_name' => 'keystone-all', + 'package_options' => '' } -when "suse" - default["openstack"]["identity"]["user"] = "openstack-keystone" - default["openstack"]["identity"]["group"] = "openstack-keystone" - default["openstack"]["identity"]["platform"] = { - "mysql_python_packages" => [ "python-mysql" ], - "postgresql_python_packages" => [ "python-psycopg2" ], - "memcache_python_packages" => [ "python-python-memcached" ], - "keystone_packages" => [ "openstack-keystone" ], - "keystone_service" => "openstack-keystone", - "keystone_process_name" => "keystone-all", - "package_options" => "" +when 'suse' + default['openstack']['identity']['user'] = 'openstack-keystone' + default['openstack']['identity']['group'] = 'openstack-keystone' + default['openstack']['identity']['platform'] = { + 'mysql_python_packages' => ['python-mysql'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'memcache_python_packages' => ['python-python-memcached'], + 'keystone_packages' => ['openstack-keystone'], + 'keystone_client_packages' => ['python-keystoneclient'], + 'keystone_service' => 'openstack-keystone', + 'keystone_process_name' => 'keystone-all', + 'package_options' => '' } -when "ubuntu" - default["openstack"]["identity"]["user"] = "keystone" - default["openstack"]["identity"]["group"] = "keystone" - default["openstack"]["identity"]["platform"] = { - "mysql_python_packages" => [ "python-mysqldb" ], - "postgresql_python_packages" => [ "python-psycopg2" ], - "memcache_python_packages" => [ "python-memcache" ], - "keystone_packages" => [ "keystone" ], - "keystone_service" => "keystone", - "keystone_process_name" => "keystone-all", - "package_options" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" +when 'debian' + default['openstack']['identity']['user'] = 'keystone' + default['openstack']['identity']['group'] = 'keystone' + default['openstack']['identity']['platform'] = { + 'mysql_python_packages' => ['python-mysqldb'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'memcache_python_packages' => ['python-memcache'], + 'keystone_packages' => ['keystone'], + 'keystone_client_packages' => ['python-keystoneclient'], + 'keystone_service' => 'keystone', + 'keystone_process_name' => 'keystone-all', + 'package_options' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" } end diff --git a/chef/cookbooks/openstack-identity/files/default/keystone_plugin.py b/chef/cookbooks/openstack-identity/files/default/keystone_plugin.py deleted file mode 100644 index 3c97035..0000000 --- a/chef/cookbooks/openstack-identity/files/default/keystone_plugin.py +++ /dev/null @@ -1,96 +0,0 @@ -# -# Copyright 2012, Rackspace US, Inc. -# -# 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. -# - -from keystoneclient.v2_0 import Client as KeystoneClient - -import collectd - -global NAME, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL, VERBOSE_LOGGING - -NAME = "keystone_plugin" -OS_USERNAME = "username" -OS_PASSWORD = "password" -OS_TENANT_NAME = "tenantname" -OS_AUTH_URL = "http://localhost:5000/v2.0" -VERBOSE_LOGGING = False - - -def get_stats(user, passwd, tenant, url): - keystone = KeystoneClient(username=user, password=passwd, tenant_name=tenant, auth_url=url) - data = dict() - - # Define list of keys to query for - keys = ('tenants','users','roles','services','endpoints') - for key in keys: - data["openstack.keystone.%s.count" % key] = len(keystone.__getattribute__(key).list()) - - tenant_list = keystone.tenants.list() - for tenant in tenant_list: - data["openstack.keystone.tenants.tenants.%s.users.count" % tenant.name] = len(keystone.tenants.list_users(tenant.id)) - - ########## - # debug - #for key in data.keys(): - # print "%s = %s" % (key, data[key]) - ########## - - return data - -def configure_callback(conf): - """Received configuration information""" - global OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL, VERBOSE_LOGGING - for node in conf.children: - if node.key == "Username": - OS_USERNAME = node.values[0] - elif node.key == "Password": - OS_PASSWORD = node.values[0] - elif node.key == "TenantName": - OS_TENANT_NAME = node.values[0] - elif node.key == "AuthURL": - OS_AUTH_URL = node.values[0] - elif node.key == "Verbose": - VERBOSE_LOGGING = node.values[0] - else: - logger("warn", "Unknown config key: %s" % node.key) - - -def read_callback(): - logger("verb", "read_callback") - info = get_stats(OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL) - - if not info: - logger("err", "No information received") - return - - for key in info.keys(): - logger('verb', 'Dispatching %s : %i' % (key, int(info[key]))) - val = collectd.Values(plugin=key) - val.type = 'gauge' - val.values = [int(info[key])] - val.dispatch() - - -def logger(t, msg): - if t == 'err': - collectd.error('%s: %s' % (NAME, msg)) - if t == 'warn': - collectd.warning('%s: %s' % (NAME, msg)) - elif t == 'verb' and VERBOSE_LOGGING == True: - collectd.info('%s: %s' % (NAME, msg)) - -collectd.register_config(configure_callback) -collectd.warning("Initializing keystone plugin") -collectd.register_read(read_callback) diff --git a/chef/cookbooks/openstack-identity/libraries/default.rb b/chef/cookbooks/openstack-identity/libraries/default.rb new file mode 100644 index 0000000..ccb46e8 --- /dev/null +++ b/chef/cookbooks/openstack-identity/libraries/default.rb @@ -0,0 +1,43 @@ +# encoding: UTF-8 +# # +# # Cookbook Name:: openstack-identity +# # libraries::master_election +# # +# # Author: sam.su@huawei.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. +# # +# +def node_election(role, tag, chef_environment = nil) + chef_environment = chef_environment || node.chef_environment + master = search(:node, "run_list:role\\[#{role}\\] AND \ + chef_environment:#{chef_environment} AND \ + tags:#{tag}") || [] + if master.empty? + nodes = search(:node, "run_list:role\\[#{role}\\] AND \ + chef_environment:#{chef_environment}") || [] + nodes = nodes.sort_by { |node| node.name } unless nodes.empty? + if nodes.empty? or node.name.eql?(nodes.first.name) + node.tags << tag unless node.tags.include?(tag) + node.save + end + + if nodes.empty? + return node + end + + return nodes.first + else + return master.first + end +end diff --git a/chef/cookbooks/openstack-identity/libraries/matchers.rb b/chef/cookbooks/openstack-identity/libraries/matchers.rb new file mode 100644 index 0000000..1f88cc1 --- /dev/null +++ b/chef/cookbooks/openstack-identity/libraries/matchers.rb @@ -0,0 +1,51 @@ +# encoding: UTF-8 +if defined?(ChefSpec) + def create_service_openstack_identity_register(resource_name) + ChefSpec::Matchers::ResourceMatcher.new( + :openstack_identity_register, + :create_service, + resource_name) + end + + def create_endpoint_openstack_identity_register(resource_name) + ChefSpec::Matchers::ResourceMatcher.new( + :openstack_identity_register, + :create_endpoint, + resource_name) + end + + def create_tenant_openstack_identity_register(resource_name) + ChefSpec::Matchers::ResourceMatcher.new( + :openstack_identity_register, + :create_tenant, + resource_name) + end + + def create_user_openstack_identity_register(resource_name) + ChefSpec::Matchers::ResourceMatcher.new( + :openstack_identity_register, + :create_user, + resource_name) + end + + def create_role_openstack_identity_register(resource_name) + ChefSpec::Matchers::ResourceMatcher.new( + :openstack_identity_register, + :create_role, + resource_name) + end + + def grant_role_openstack_identity_register(resource_name) + ChefSpec::Matchers::ResourceMatcher.new( + :openstack_identity_register, + :grant_role, + resource_name) + end + + def create_ec2_credentials_openstack_identity_register(resource_name) + ChefSpec::Matchers::ResourceMatcher.new( + :openstack_identity_register, + :create_ec2_credentials, + resource_name) + end +end diff --git a/chef/cookbooks/openstack-identity/metadata.rb b/chef/cookbooks/openstack-identity/metadata.rb index cdfc4ee..e14a192 100644 --- a/chef/cookbooks/openstack-identity/metadata.rb +++ b/chef/cookbooks/openstack-identity/metadata.rb @@ -1,16 +1,17 @@ -name "openstack-identity" -maintainer "Opscode, Inc." -maintainer_email "matt@opscode.com" -license "Apache 2.0" -description "The OpenStack Identity service Keystone." +name 'openstack-identity' +maintainer 'Opscode, Inc.' +maintainer_email 'matt@opscode.com' +license 'Apache 2.0' +description 'The OpenStack Identity service Keystone.' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "7.0.0" +version '9.2.1' -recipe "openstack-identity::server", "Installs and Configures Keystone Service" -recipe "openstack-identity::registration", "Adds user, tenant, role and endpoint records to Keystone" +recipe 'openstack-identity::client', 'Install packages required for keystone client' +recipe 'openstack-identity::server', 'Installs and Configures Keystone Service' +recipe 'openstack-identity::registration', 'Adds user, tenant, role and endpoint records to Keystone' %w{ ubuntu fedora redhat centos suse }.each do |os| supports os end -depends "openstack-common", "~> 0.4.0" +depends 'openstack-common', '~> 9.0' diff --git a/chef/cookbooks/openstack-identity/providers/register.rb b/chef/cookbooks/openstack-identity/providers/register.rb index 2e6fa09..3500118 100644 --- a/chef/cookbooks/openstack-identity/providers/register.rb +++ b/chef/cookbooks/openstack-identity/providers/register.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-identity # Provider:: register @@ -25,7 +26,8 @@ include Chef::Mixin::ShellOut include ::Openstack private -def generate_creds resource + +def generate_creds(resource) { 'OS_SERVICE_ENDPOINT' => resource.auth_uri, 'OS_SERVICE_TOKEN' => resource.bootstrap_token @@ -33,56 +35,74 @@ def generate_creds resource end private -def identity_command resource, cmd, args={} - keystonecmd = ['keystone'] << cmd - args.each { |key, val| - keystonecmd << "--#{key}" << val.to_s + +def generate_ec2_creds(resource) + { + 'OS_USERNAME' => resource.admin_user, + 'OS_PASSWORD' => resource.admin_pass, + 'OS_TENANT_NAME' => resource.admin_tenant_name, + 'OS_AUTH_URL' => resource.identity_endpoint } - Chef::Log.debug("Running identity command: #{keystonecmd}") - rc = shell_out(keystonecmd, :env => generate_creds(resource)) - if rc.exitstatus != 0 - raise RuntimeError, "#{rc.stderr} (#{rc.exitstatus})" +end + +private + +def identity_command(resource, cmd, args = {}) + keystonecmd = ['keystone'] << cmd + args.each do |key, val| + keystonecmd << "--#{key}" << val.to_s end + Chef::Log.debug("Running identity command: #{keystonecmd}") + rc = shell_out(keystonecmd, env: (cmd.include? 'ec2') ? generate_ec2_creds(resource) : generate_creds(resource)) + fail "#{rc.stderr} (#{rc.exitstatus})" if rc.exitstatus != 0 rc.stdout end private -def identity_uuid resource, type, key, value, args={}, uuid_field='id' + +def identity_uuid(resource, type, key, value, args = {}, uuid_field = 'id') # rubocop: disable ParameterLists + rc = nil begin output = identity_command resource, "#{type}-list", args output = prettytable_to_array(output) - output.each { |obj| - if obj.has_key?(uuid_field) and obj[key] == value - return obj[uuid_field] - end - } + rc = (type == 'endpoint') ? (search_uuid(output, uuid_field, key => value, 'region' => resource.endpoint_region)) : (search_uuid(output, uuid_field, key => value)) rescue RuntimeError => e - raise RuntimeError, "Could not lookup uuid for #{type}:#{key}=>#{value}. Error was #{e.message}" + raise "Could not lookup uuid for #{type}:#{key}=>#{value}. Error was #{e.message}" end - nil + rc +end + +private + +def search_uuid(output, uuid_field, required_hash = {}) + rc = nil + output.each do |obj| + rc = obj[uuid_field] if obj.key?(uuid_field) && required_hash.values - obj.values_at(*required_hash.keys) == [] + end + rc end action :create_service do - if node["openstack"]["identity"]["catalog"]["backend"] == "templated" - Chef::Log.info("Skipping service creation - templated catalog backend in use.") + if node['openstack']['identity']['catalog']['backend'] == 'templated' + Chef::Log.info('Skipping service creation - templated catalog backend in use.') new_resource.updated_by_last_action(false) else begin - service_uuid = identity_uuid new_resource, "service", "type", new_resource.service_type + service_uuid = identity_uuid new_resource, 'service', 'type', new_resource.service_type - unless service_uuid - identity_command new_resource, "service-create", - { 'type' => new_resource.service_type, - 'name' => new_resource.service_name, - 'description' => new_resource.service_description } - Chef::Log.info("Created service '#{new_resource.service_name}'") - new_resource.updated_by_last_action(true) - else + if service_uuid Chef::Log.info("Service Type '#{new_resource.service_type}' already exists.. Not creating.") Chef::Log.info("Service UUID: #{service_uuid}") new_resource.updated_by_last_action(false) + else + identity_command(new_resource, 'service-create', + 'type' => new_resource.service_type, + 'name' => new_resource.service_name, + 'description' => new_resource.service_description) + Chef::Log.info("Created service '#{new_resource.service_name}'") + new_resource.updated_by_last_action(true) end - rescue Exception => e + rescue StandardError => e Chef::Log.error("Unable to create service '#{new_resource.service_name}'") Chef::Log.error("Error was: #{e.message}") new_resource.updated_by_last_action(false) @@ -91,33 +111,33 @@ action :create_service do end action :create_endpoint do - if node["openstack"]["identity"]["catalog"]["backend"] == "templated" - Chef::Log.info("Skipping endpoint creation - templated catalog backend in use.") + if node['openstack']['identity']['catalog']['backend'] == 'templated' + Chef::Log.info('Skipping endpoint creation - templated catalog backend in use.') new_resource.updated_by_last_action(false) else begin - service_uuid = identity_uuid new_resource, "service", "type", new_resource.service_type + service_uuid = identity_uuid new_resource, 'service', 'type', new_resource.service_type unless service_uuid Chef::Log.error("Unable to find service type '#{new_resource.service_type}'") new_resource.updated_by_last_action(false) next end - endpoint_uuid = identity_uuid new_resource, "endpoint", "service_id", service_uuid - unless endpoint_uuid - identity_command new_resource, "endpoint-create", - { 'region' => new_resource.endpoint_region, - 'service_id' => service_uuid, - 'publicurl' => new_resource.endpoint_publicurl, - 'internalurl' => new_resource.endpoint_internalurl, - 'adminurl' => new_resource.endpoint_adminurl } - Chef::Log.info("Created endpoint for service type '#{new_resource.service_type}'") - new_resource.updated_by_last_action(true) - else + endpoint_uuid = identity_uuid new_resource, 'endpoint', 'service_id', service_uuid + if endpoint_uuid Chef::Log.info("Endpoint already exists for Service Type '#{new_resource.service_type}' already exists.. Not creating.") new_resource.updated_by_last_action(false) + else + identity_command(new_resource, 'endpoint-create', + 'region' => new_resource.endpoint_region, + 'service_id' => service_uuid, + 'publicurl' => new_resource.endpoint_publicurl, + 'internalurl' => new_resource.endpoint_internalurl, + 'adminurl' => new_resource.endpoint_adminurl) + Chef::Log.info("Created endpoint for service type '#{new_resource.service_type}'") + new_resource.updated_by_last_action(true) end - rescue Exception => e + rescue StandardError => e Chef::Log.error("Unable to create endpoint for service type '#{new_resource.service_type}'") Chef::Log.error("Error was: #{e.message}") new_resource.updated_by_last_action(false) @@ -127,21 +147,21 @@ end action :create_tenant do begin - tenant_uuid = identity_uuid new_resource, "tenant", "name", new_resource.tenant_name + tenant_uuid = identity_uuid new_resource, 'tenant', 'name', new_resource.tenant_name - unless tenant_uuid - identity_command new_resource, "tenant-create", - { 'name' => new_resource.tenant_name, - 'description' => new_resource.tenant_description, - 'enabled' => new_resource.tenant_enabled } - Chef::Log.info("Created tenant '#{new_resource.tenant_name}'") - new_resource.updated_by_last_action(true) - else + if tenant_uuid Chef::Log.info("Tenant '#{new_resource.tenant_name}' already exists.. Not creating.") Chef::Log.info("Tenant UUID: #{tenant_uuid}") if tenant_uuid new_resource.updated_by_last_action(false) + else + identity_command(new_resource, 'tenant-create', + 'name' => new_resource.tenant_name, + 'description' => new_resource.tenant_description, + 'enabled' => new_resource.tenant_enabled) + Chef::Log.info("Created tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(true) end - rescue Exception => e + rescue StandardError => e Chef::Log.error("Unable to create tenant '#{new_resource.tenant_name}'") Chef::Log.error("Error was: #{e.message}") new_resource.updated_by_last_action(false) @@ -150,19 +170,19 @@ end action :create_role do begin - role_uuid = identity_uuid new_resource, "role", "name", new_resource.role_name + role_uuid = identity_uuid new_resource, 'role', 'name', new_resource.role_name - unless role_uuid - identity_command new_resource, "role-create", - { 'name' => new_resource.role_name } - Chef::Log.info("Created Role '#{new_resource.role_name}'") - new_resource.updated_by_last_action(true) - else + if role_uuid Chef::Log.info("Role '#{new_resource.role_name}' already exists.. Not creating.") Chef::Log.info("Role UUID: #{role_uuid}") new_resource.updated_by_last_action(false) + else + identity_command(new_resource, 'role-create', + 'name' => new_resource.role_name) + Chef::Log.info("Created Role '#{new_resource.role_name}'") + new_resource.updated_by_last_action(true) end - rescue Exception => e + rescue StandardError => e Chef::Log.error("Unable to create role '#{new_resource.role_name}'") Chef::Log.error("Error was: #{e.message}") new_resource.updated_by_last_action(false) @@ -171,21 +191,20 @@ end action :create_user do begin - tenant_uuid = identity_uuid new_resource, "tenant", "name", new_resource.tenant_name + tenant_uuid = identity_uuid new_resource, 'tenant', 'name', new_resource.tenant_name unless tenant_uuid Chef::Log.error("Unable to find tenant '#{new_resource.tenant_name}'") new_resource.updated_by_last_action(false) next end - output = identity_command new_resource, "user-list", {'tenant-id' => tenant_uuid} + output = identity_command(new_resource, 'user-list', + 'tenant-id' => tenant_uuid) users = prettytable_to_array output user_found = false - users.each { |user| - if user['name'] == new_resource.user_name - user_found = true - end - } + users.each do |user| + user_found = true if user['name'] == new_resource.user_name + end if user_found Chef::Log.info("User '#{new_resource.user_name}' already exists for tenant '#{new_resource.tenant_name}'") @@ -193,14 +212,14 @@ action :create_user do next end - identity_command new_resource, "user-create", - { 'name' => new_resource.user_name, - 'tenant-id' => tenant_uuid, - 'pass' => new_resource.user_pass, - 'enabled' => new_resource.user_enabled } + identity_command(new_resource, 'user-create', + 'name' => new_resource.user_name, + 'tenant-id' => tenant_uuid, + 'pass' => new_resource.user_pass, + 'enabled' => new_resource.user_enabled) Chef::Log.info("Created user '#{new_resource.user_name}' for tenant '#{new_resource.tenant_name}'") new_resource.updated_by_last_action(true) - rescue Exception => e + rescue StandardError => e Chef::Log.error("Unable to create user '#{new_resource.user_name}' for tenant '#{new_resource.tenant_name}'") Chef::Log.error("Error was: #{e.message}") new_resource.updated_by_last_action(false) @@ -209,42 +228,43 @@ end action :grant_role do begin - tenant_uuid = identity_uuid new_resource, "tenant", "name", new_resource.tenant_name + tenant_uuid = identity_uuid new_resource, 'tenant', 'name', new_resource.tenant_name unless tenant_uuid Chef::Log.error("Unable to find tenant '#{new_resource.tenant_name}'") new_resource.updated_by_last_action(false) next end - user_uuid = identity_uuid new_resource, "user", "name", new_resource.user_name + user_uuid = identity_uuid new_resource, 'user', 'name', new_resource.user_name unless tenant_uuid Chef::Log.error("Unable to find user '#{new_resource.user_name}'") new_resource.updated_by_last_action(false) next end - role_uuid = identity_uuid new_resource, "role", "name", new_resource.role_name + role_uuid = identity_uuid new_resource, 'role', 'name', new_resource.role_name unless tenant_uuid Chef::Log.error("Unable to find role '#{new_resource.role_name}'") new_resource.updated_by_last_action(false) next end - assigned_role_uuid = identity_uuid new_resource, "user-role", "name", new_resource.role_name, - { 'tenant-id' => tenant_uuid, - 'user-id' => user_uuid } - unless role_uuid == assigned_role_uuid - identity_command new_resource, "user-role-add", - { 'tenant-id' => tenant_uuid, - 'role-id' => role_uuid, - 'user-id' => user_uuid } - Chef::Log.info("Granted Role '#{new_resource.role_name}' to User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") - new_resource.updated_by_last_action(true) - else + assigned_role_uuid = identity_uuid(new_resource, 'user-role', 'name', + new_resource.role_name, + 'tenant-id' => tenant_uuid, + 'user-id' => user_uuid) + if role_uuid == assigned_role_uuid Chef::Log.info("Role '#{new_resource.role_name}' already granted to User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") new_resource.updated_by_last_action(false) + else + identity_command(new_resource, 'user-role-add', + 'tenant-id' => tenant_uuid, + 'role-id' => role_uuid, + 'user-id' => user_uuid) + Chef::Log.info("Granted Role '#{new_resource.role_name}' to User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(true) end - rescue Exception => e + rescue StandardError => e Chef::Log.error("Unable to grant role '#{new_resource.role_name}' to user '#{new_resource.user_name}'") Chef::Log.error("Error was: #{e.message}") new_resource.updated_by_last_action(false) @@ -253,26 +273,31 @@ end action :create_ec2_credentials do begin - tenant_uuid = identity_uuid new_resource, "tenant", "name", new_resource.tenant_name + tenant_uuid = identity_uuid new_resource, 'tenant', 'name', new_resource.tenant_name unless tenant_uuid Chef::Log.error("Unable to find tenant '#{new_resource.tenant_name}'") new_resource.updated_by_last_action(false) next end - user_uuid = identity_uuid new_resource, "user", "name", new_resource.user_name, {'tenant-id' => tenant_uuid} - unless tenant_uuid + user_uuid = identity_uuid(new_resource, 'user', 'name', + new_resource.user_name, + 'tenant-id' => tenant_uuid) + unless user_uuid Chef::Log.error("Unable to find user '#{new_resource.user_name}'") new_resource.updated_by_last_action(false) next end # this is not really a uuid, but this will work nonetheless - access = identity_uuid new_resource, "ec2-credentials", "tenant", new_resource.tenant_name, {'user-id' => user_uuid}, "access" - unless access - output = identity_command new_resource, "ec2-credentials-create", - { 'user-id' => user_uuid, - 'tenant-id' => tenant_uuid } + access = identity_uuid new_resource, 'ec2-credentials', 'tenant', new_resource.tenant_name, { 'user-id' => user_uuid }, 'access' + if access + Chef::Log.info("EC2 credentials already exist for '#{new_resource.user_name}' in tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(false) + else + output = identity_command(new_resource, 'ec2-credentials-create', + 'user-id' => user_uuid, + 'tenant-id' => tenant_uuid) Chef::Log.info("Created EC2 Credentials for User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") data = prettytable_to_array(output) @@ -286,16 +311,10 @@ action :create_ec2_credentials do node.save unless Chef::Config[:solo] new_resource.updated_by_last_action(true) end - else - Chef::Log.info("EC2 credentials already exist for '#{new_resource.user_name}' in tenant '#{new_resource.tenant_name}'") - new_resource.updated_by_last_action(false) end - rescue Exception => e + rescue StandardError => e Chef::Log.error("Unable to create EC2 Credentials for User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") Chef::Log.error("Error was: #{e.message}") new_resource.updated_by_last_action(false) end end - - - diff --git a/chef/cookbooks/openstack-identity/recipes/client.rb b/chef/cookbooks/openstack-identity/recipes/client.rb new file mode 100644 index 0000000..bdd4544 --- /dev/null +++ b/chef/cookbooks/openstack-identity/recipes/client.rb @@ -0,0 +1,32 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-identity +# Recipe:: client +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +platform_options = node['openstack']['identity']['platform'] +platform_options['keystone_client_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end diff --git a/chef/cookbooks/openstack-identity/recipes/default.rb b/chef/cookbooks/openstack-identity/recipes/default.rb index 9c0e915..638e738 100644 --- a/chef/cookbooks/openstack-identity/recipes/default.rb +++ b/chef/cookbooks/openstack-identity/recipes/default.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-identity # Recipe:: default diff --git a/chef/cookbooks/openstack-identity/recipes/registration.rb b/chef/cookbooks/openstack-identity/recipes/registration.rb index 5859222..b6d644c 100644 --- a/chef/cookbooks/openstack-identity/recipes/registration.rb +++ b/chef/cookbooks/openstack-identity/recipes/registration.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-identity # Recipe:: setup @@ -18,157 +19,128 @@ # limitations under the License. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -identity_admin_endpoint = endpoint "identity-admin" -identity_endpoint = endpoint "identity-api" - -admin_tenant_name = node["openstack"]["identity"]["admin_tenant_name"] -admin_user = node["openstack"]["identity"]["admin_user"] -admin_pass = user_password node["openstack"]["identity"]["admin_password"] +identity_admin_endpoint = endpoint 'identity-admin' +identity_endpoint = endpoint 'identity-api' auth_uri = ::URI.decode identity_admin_endpoint.to_s -bootstrap_token = secret "secrets", "#{node["openstack"]["identity"]["admin_token"]}" +# FIXME(invsblduck): RuboCop gating was enabled mid-review; +# Remove these variables in a separate commit if really not needed. +# rubocop:disable UselessAssignment +admin_tenant_name = node['openstack']['identity']['admin_tenant_name'] +admin_user = node['openstack']['identity']['admin_user'] +admin_pass = get_password 'user', node['openstack']['identity']['admin_user'] +# rubocop:enable UselessAssignment -# We need to bootstrap the keystone admin user so that calls -# to keystone_register will succeed, since those provider calls -# use the admin tenant/user/pass to get an admin token. -bash "bootstrap-keystone-admin" do - # A shortcut bootstrap command was added to python-keystoneclient - # in early Grizzly timeframe... but we need to do all the commands - # here manually since the python-keystoneclient package included - # in CloudArchive (for now) doesn't have it... - insecure = node["openstack"]["auth"]["validate_certs"] ? "" : " --insecure" - base_ks_cmd = "keystone#{insecure} --endpoint=#{auth_uri} --token=#{bootstrap_token}" - code <<-EOF -set -x -function get_id () { - echo `"$@" | grep ' id ' | awk '{print $4}'` -} -#{base_ks_cmd} tenant-list | grep #{admin_tenant_name} -if [[ $? -eq 1 ]]; then - ADMIN_TENANT=$(get_id #{base_ks_cmd} tenant-create --name=#{admin_tenant_name}) -else - ADMIN_TENANT=$(#{base_ks_cmd} tenant-list | grep #{admin_tenant_name} | awk '{print $2}') -fi -#{base_ks_cmd} role-list | grep admin -if [[ $? -eq 1 ]]; then - ADMIN_ROLE=$(get_id #{base_ks_cmd} role-create --name=admin) -else - ADMIN_ROLE=$(#{base_ks_cmd} role-list | grep admin | awk '{print $2}') -fi -#{base_ks_cmd} user-list | grep #{admin_user} -if [[ $? -eq 1 ]]; then - ADMIN_USER=$(get_id #{base_ks_cmd} user-create --name=#{admin_user} --pass="#{admin_pass}" --email=#{admin_user}@example.com) -else - ADMIN_USER=$(#{base_ks_cmd} user-list | grep #{admin_user} | awk '{print $2}') -fi -#{base_ks_cmd} user-role-list --user-id=$ADMIN_USER --tenant-id=$ADMIN_TENANT | grep admin -if [[ $? -eq 1 ]]; then - #{base_ks_cmd} user-role-add --user-id $ADMIN_USER --role-id $ADMIN_ROLE --tenant-id $ADMIN_TENANT -fi -exit 0 -EOF -end +bootstrap_token = get_secret 'openstack_identity_bootstrap_token' -node["openstack"]["identity"]["tenants"].each do |tenant_name| - ## Add openstack tenant ## +# FIXME(galstrom21): This needs to be refactored, to not use a +# MultilineBlockChain. +# Register all the tenants specified in the users hash +node['openstack']['identity']['users'].values.map do |user_info| + user_info['roles'].values.push(user_info['default_tenant']) +end.flatten.uniq.each do |tenant_name| # rubocop: disable MultilineBlockChain openstack_identity_register "Register '#{tenant_name}' Tenant" do auth_uri auth_uri bootstrap_token bootstrap_token tenant_name tenant_name tenant_description "#{tenant_name} Tenant" - tenant_enabled true # Not required as this is the default action :create_tenant end end -node["openstack"]["identity"]["roles"].each do |role_key| - openstack_identity_register "Register '#{role_key.to_s}' Role" do +# FIXME(galstrom21): This needs to be refactored, to not use a +# MultilineBlockChain. +# Register all the roles from the users hash +node['openstack']['identity']['users'].values.map do |user_info| + user_info['roles'].keys +end.flatten.uniq.each do |role_name| # rubocop: disable MultilineBlockChain + openstack_identity_register "Register '#{role_name.to_s}' Role" do auth_uri auth_uri bootstrap_token bootstrap_token - role_name role_key + role_name role_name action :create_role end end - -node['openstack']['services'].each_key do |service| - cu_user = node['openstack']['identity']["#{service}"]['username'] - cu_pass = node['openstack']['identity']["#{service}"]['password'] - cu_tenant = node['openstack']['identity']["#{service}"]['tenant'] - cu_role = node['openstack']['identity']["#{service}"]['role'] - - if "#{service}" != "identity" - openstack_identity_register "Register '#{service}' User" do - auth_uri auth_uri - bootstrap_token bootstrap_token - user_name cu_user - user_pass cu_pass - tenant_name cu_tenant - user_enabled true # Not required as this is the default - action :create_user - end - - openstack_identity_register "Grant #{cu_role} Role to #{cu_user} User in #{cu_tenant} Tenant" do - auth_uri auth_uri - bootstrap_token bootstrap_token - user_name cu_user - role_name cu_role - tenant_name cu_tenant - action :grant_role - end - end - - cu_service = node['openstack']['services']["#{service}"]['name'] - - openstack_identity_register "Register #{service} Service" do - auth_uri auth_uri - bootstrap_token bootstrap_token - service_name "#{cu_service}" - service_type "#{service}" - service_description "Openstack #{service} Service" - action :create_service - end - - if %Q/#{node['openstack']['services']["#{service}"]['status']}/ == "enable" - service_endpoint = endpoint "#{service}-api" - if service == "identity" or service == "compute-ec2" or service == "swift" - service_endpoint_admin = endpoint "#{service}-admin" - elsif - service_endpoint_admin = service_endpoint - end - node.set['openstack']["#{service}"]['adminURL'] = service_endpoint_admin.to_s - node.set['openstack']["#{service}"]['internalURL'] = service_endpoint.to_s - node.set['openstack']["#{service}"]['publicURL'] = service_endpoint.to_s - - openstack_identity_register "Register #{service} Endpoint" do - auth_uri auth_uri - bootstrap_token bootstrap_token - service_type "#{service}" - endpoint_region node["openstack"]["identity"]["region"] - endpoint_adminurl node['openstack']["#{service}"]['adminURL'] - endpoint_internalurl node['openstack']["#{service}"]['internalURL'] - endpoint_publicurl node['openstack']["#{service}"]['publicURL'] - action :create_endpoint - end - end -end - - -node["openstack"]["identity"]["users"].each do |username, user_info| - openstack_identity_register "Create EC2 credentials for '#{username}' user" do +node['openstack']['identity']['users'].each do |username, user_info| + pwd = get_password 'user', username + openstack_identity_register "Register '#{username}' User" do auth_uri auth_uri bootstrap_token bootstrap_token user_name username - tenant_name user_info["default_tenant"] + user_pass pwd + tenant_name user_info['default_tenant'] + user_enabled true # Not required as this is the default - action :create_ec2_credentials + action :create_user + end + + user_info['roles'].each do |rolename, tenant_list| + tenant_list.each do |tenantname| + openstack_identity_register "Grant '#{rolename}' Role to '#{username}' User in '#{tenantname}' Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + user_name username + role_name rolename + tenant_name tenantname + + action :grant_role + end + end end end + +openstack_identity_register 'Register Identity Service' do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name 'keystone' + service_type 'identity' + service_description 'Keystone Identity Service' + + action :create_service + not_if { node['openstack']['identity']['catalog']['backend'] == 'templated' } +end + +node.set['openstack']['identity']['adminURL'] = identity_admin_endpoint.to_s +node.set['openstack']['identity']['internalURL'] = identity_endpoint.to_s +node.set['openstack']['identity']['publicURL'] = identity_endpoint.to_s + +Chef::Log.info "Keystone AdminURL: #{identity_admin_endpoint.to_s}" +Chef::Log.info "Keystone InternalURL: #{identity_endpoint.to_s}" +Chef::Log.info "Keystone PublicURL: #{identity_endpoint.to_s}" + +openstack_identity_register 'Register Identity Endpoint' do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type 'identity' + endpoint_region node['openstack']['identity']['region'] + endpoint_adminurl node['openstack']['identity']['adminURL'] + endpoint_internalurl node['openstack']['identity']['adminURL'] + endpoint_publicurl node['openstack']['identity']['publicURL'] + + action :create_endpoint + not_if { node['openstack']['identity']['catalog']['backend'] == 'templated' } +end + +#node['openstack']['identity']['users'].each do |username, user_info| +# openstack_identity_register "Create EC2 credentials for '#{username}' user" do +# auth_uri auth_uri +# bootstrap_token bootstrap_token +# user_name username +# tenant_name user_info['default_tenant'] +# admin_tenant_name admin_tenant_name +# admin_user admin_user +# admin_pass admin_pass +# identity_endpoint identity_endpoint.to_s + +# action :create_ec2_credentials +# end +#end diff --git a/chef/cookbooks/openstack-identity/recipes/server.rb b/chef/cookbooks/openstack-identity/recipes/server.rb index e8b713c..4824f08 100644 --- a/chef/cookbooks/openstack-identity/recipes/server.rb +++ b/chef/cookbooks/openstack-identity/recipes/server.rb @@ -1,145 +1,203 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-identity # Recipe:: server # # Copyright 2012, Rackspace US, Inc. # Copyright 2012-2013, Opscode, Inc. +# Copyright 2013 SUSE LINUX Products GmbH. # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -if node["openstack"]["identity"]["syslog"]["use"] - include_recipe "openstack-common::logging" +if node['openstack']['identity']['syslog']['use'] + include_recipe 'openstack-common::logging' end -platform_options = node["openstack"]["identity"]["platform"] +platform_options = node['openstack']['identity']['platform'] -##### NOTE ##### -# https://bugs.launchpad.net/ubuntu/+source/keystone/+bug/931236 -################ - -db_type = node['openstack']['db']['identity']['db_type'] -platform_options["#{db_type}_python_packages"].each do |pkg| - package pkg do - action :install +db_type = node['openstack']['db']['identity']['service_type'] +unless db_type == 'sqlite' + platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + options platform_options['package_options'] + action :upgrade + end end end -platform_options["memcache_python_packages"].each do |pkg| +platform_options['memcache_python_packages'].each do |pkg| package pkg do - action :install - end -end - -platform_options["keystone_packages"].each do |pkg| - package pkg do - options platform_options["package_options"] - + options platform_options['package_options'] action :upgrade end end -execute "Keystone: sleep" do - command "sleep 10s" +platform_options['keystone_packages'].each do |pkg| + package pkg do + options platform_options['package_options'] + action :upgrade + end +end + +execute 'Keystone: sleep' do + command 'sleep 10s' action :nothing end -service "keystone" do - service_name platform_options["keystone_service"] - supports :status => true, :restart => true +service 'keystone' do + service_name platform_options['keystone_service'] + supports status: true, restart: true - action [ :enable ] + action [:enable] - notifies :run, "execute[Keystone: sleep]", :immediately + notifies :run, 'execute[Keystone: sleep]', :immediately end -directory "/etc/keystone" do - owner node["openstack"]["identity"]["user"] - group node["openstack"]["identity"]["group"] +directory '/etc/keystone' do + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] mode 00700 end -directory node["openstack"]["identity"]["signing"]["basedir"] do - owner node["openstack"]["identity"]["user"] - group node["openstack"]["identity"]["group"] - mode 00700 - - only_if { node["openstack"]["auth"]["strategy"] == "pki" } -end - -file "/var/lib/keystone/keystone.db" do +file '/var/lib/keystone/keystone.db' do action :delete + not_if { node['openstack']['db']['identity']['service_type'] == 'sqlite' } end -["/etc/keystone/keystone.conf", "/etc/keystone/logging.conf"].each do |file| - file file do - owner node["openstack"]["identity"]["user"] - group node["openstack"]["identity"]["group"] +if node['openstack']['auth']['strategy'] == 'pki' + certfile_url = node['openstack']['identity']['signing']['certfile_url'] + keyfile_url = node['openstack']['identity']['signing']['keyfile_url'] + ca_certs_url = node['openstack']['identity']['signing']['ca_certs_url'] + signing_basedir = node['openstack']['identity']['signing']['basedir'] + + directory signing_basedir do + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] mode 00700 end + + directory "#{signing_basedir}/certs" do + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00755 + end + + directory "#{signing_basedir}/private" do + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00750 + end + + if certfile_url.nil? || keyfile_url.nil? || ca_certs_url.nil? + keygen_node = node_election('os-identity', 'keystone_keygen') + if keygen_node.nil? + keygen_node = node + end + if node.name.eql?(keygen_node.name) + execute 'keystone-manage pki_setup' do + user node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + not_if { ::FileTest.exists? node['openstack']['identity']['signing']['keyfile'] } + end + %w{certfile keyfile ca_certs}.each do |name| + ruby_block "read #{name}" do + block do + file = node['openstack']['identity']['signing']["#{name}"] + if File.exists?(file) and !node['openstack']['identity']['signing'].attribute?("#{name}_data") + node.set['openstack']['identity']['signing']["#{name}_data"] = File.read(file) + node.save + end + end + end + end + + else + if keygen_node['openstack']['identity']['signing'].attribute?("#{name}_data") + %w{certfile keyfile ca_certs}.each do |name| + file node['openstack']['identity']['signing']["#{name}"] do + content keygen_node['openstack']['identity']['signing']["#{name}_data"] + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00640 + end + end + end + end + + else + remote_file node['openstack']['identity']['signing']['certfile'] do + source certfile_url + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00640 + + notifies :restart, 'service[keystone]', :delayed + end + + remote_file node['openstack']['identity']['signing']['keyfile'] do + source keyfile_url + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00640 + + notifies :restart, 'service[keystone]', :delayed + end + + remote_file node['openstack']['identity']['signing']['ca_certs'] do + source ca_certs_url + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00640 + + notifies :restart, 'service[keystone]', :delayed + end + end end -execute "keystone-manage pki_setup" do - user node["openstack"]["identity"]["user"] +bind_endpoint = endpoint 'identity-bind' +identity_admin_endpoint = endpoint 'identity-admin' +identity_endpoint = endpoint 'identity-api' +compute_endpoint = endpoint 'compute-api' +ec2_endpoint = endpoint 'compute-ec2-api' +image_endpoint = endpoint 'image-api' +network_endpoint = endpoint 'network-api' +volume_endpoint = endpoint 'block-storage-api' - only_if { node["openstack"]["auth"]["strategy"] == "pki" } - not_if { ::FileTest.exists? node["openstack"]["identity"]["signing"]["keyfile"] } -end - -identity_admin_endpoint = endpoint "identity-admin" -identity_endpoint = endpoint "identity-api" -compute_endpoint = endpoint "compute-api" -ec2_endpoint = endpoint "compute-ec2-api" -image_endpoint = endpoint "image-api" -network_endpoint = endpoint "network-api" -volume_endpoint = endpoint "volume-api" - -#db_user = node["openstack"]["identity"]["db"]["username"] -#db_pass = db_password "keystone" db_user = node['openstack']['db']['identity']['username'] -db_pass = db_password node['openstack']['db']['identity']['password'] +db_pass = get_password 'db', 'keystone' +sql_connection = db_uri('identity', db_user, db_pass) -sql_connection = db_uri("identity", db_user, db_pass) +bootstrap_token = get_secret 'openstack_identity_bootstrap_token' -bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" - -if node["openstack"]["ha"]["status"].eql?('enable') - ip_address = address_for node["openstack"]["identity"]["bind_interface"] -else - ip_address = node['openstack']['endpoints']['identity-api']['host'] -end +bind_address = bind_endpoint.host # If the search role is set, we search for memcache # servers via a Chef search. If not, we look at the # memcache.servers attribute. -memcache_servers = memcached_servers.join "," # from openstack-common lib - -uris = { - 'identity-admin' => identity_admin_endpoint.to_s.gsub('%25','%'), - 'identity' => identity_endpoint.to_s.gsub('%25','%'), - 'image' => image_endpoint.to_s.gsub('%25','%'), - 'compute' => compute_endpoint.to_s.gsub('%25','%'), - 'ec2' => ec2_endpoint.to_s.gsub('%25','%'), - 'network' => network_endpoint.to_s.gsub('%25','%'), - 'volume' => volume_endpoint.to_s.gsub('%25','%') -} +if node['openstack']['identity']['token']['backend'].eql?('memcache') + memcache_servers = memcached_servers('os-ops-caching').join ',' # from openstack-common lib + # number of seconds to wait before sockets timeout when the memcached server is down + # the default number is 3, here is going to set it as 0.1 + `sed -i "s/_SOCKET_TIMEOUT = 3/_SOCKET_TIMEOUT = 0.1/g" /usr/lib/python[0-9].[0-9]/site-packages/memcache.py` +end # These configuration endpoints must not have the path (v2.0, etc) # added to them, as these values are used in returning the version @@ -149,38 +207,85 @@ public_endpoint = "#{ie.scheme}://#{ie.host}:#{ie.port}/" ae = identity_admin_endpoint admin_endpoint = "#{ae.scheme}://#{ae.host}:#{ae.port}/" -template "/etc/keystone/keystone.conf" do - source "keystone.conf.erb" - owner node["openstack"]["identity"]["user"] - group node["openstack"]["identity"]["group"] - mode 00644 - variables( - :sql_connection => sql_connection, - :ip_address => ip_address, - "bootstrap_token" => bootstrap_token, - "memcache_servers" => memcache_servers, - "uris" => uris, - "public_endpoint" => public_endpoint, - "admin_endpoint" => admin_endpoint - ) +# If a keystone-paste.ini is specified use it +if node['openstack']['identity']['pastefile_url'] + remote_file '/etc/keystone/keystone-paste.ini' do + source node['openstack']['identity']['pastefile_url'] + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00644 - notifies :restart, "service[keystone]", :immediately + notifies :restart, 'service[keystone]', :delayed + end end -template "/etc/keystone/default_catalog.templates" do - source "default_catalog.templates.erb" - owner node["openstack"]["identity"]["user"] - group node["openstack"]["identity"]["group"] +template '/etc/keystone/keystone.conf' do + source 'keystone.conf.erb' + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] mode 00644 variables( - "uris" => uris + sql_connection: sql_connection, + bind_address: bind_address, + bootstrap_token: bootstrap_token, + memcache_servers: memcache_servers, + public_endpoint: public_endpoint, + public_port: identity_endpoint.port, + admin_endpoint: admin_endpoint, + admin_port: identity_admin_endpoint.port, + ldap: node['openstack']['identity']['ldap'], + token_expiration: node['openstack']['identity']['token']['expiration'] ) - notifies :restart, "service[keystone]", :immediately - only_if { node["openstack"]["identity"]["catalog"]["backend"] == "templated" } + notifies :restart, 'service[keystone]', :immediately +end + +# populate the templated catlog, if you're using the templated catalog backend +uris = { + 'identity-admin' => identity_admin_endpoint.to_s.gsub('%25', '%'), + 'identity' => identity_endpoint.to_s.gsub('%25', '%'), + 'image' => image_endpoint.to_s.gsub('%25', '%'), + 'compute' => compute_endpoint.to_s.gsub('%25', '%'), + 'ec2' => ec2_endpoint.to_s.gsub('%25', '%'), + 'network' => network_endpoint.to_s.gsub('%25', '%'), + 'volume' => volume_endpoint.to_s.gsub('%25', '%') +} + +template '/etc/keystone/default_catalog.templates' do + source 'default_catalog.templates.erb' + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00644 + variables( + uris: uris + ) + + notifies :restart, 'service[keystone]', :immediately + only_if { node['openstack']['identity']['catalog']['backend'] == 'templated' } end # sync db after keystone.conf is generated -execute "keystone-manage db_sync" do - only_if { node["openstack"]["identity"]["db"]["migrate"] } +execute 'keystone-manage db_sync' do + user node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + + only_if { node['openstack']['db']['identity']['migrate'] } end + +# Configure the flush tokens cronjob +should_run_cron = node['openstack']['identity']['token_flush_cron']['enabled'] && node['openstack']['identity']['token']['backend'] == 'sql' +log_file = node['openstack']['identity']['token_flush_cron']['log_file'] + +cron 'keystone-manage-token-flush' do + minute node['openstack']['identity']['token_flush_cron']['minute'] + hour node['openstack']['identity']['token_flush_cron']['hour'] + day node['openstack']['identity']['token_flush_cron']['day'] + weekday node['openstack']['identity']['token_flush_cron']['weekday'] + action should_run_cron ? :create : :delete + user node['openstack']['identity']['user'] + command %Q{ + `which keystone-manage` token_flush > #{log_file} 2>&1 && + echo keystone-manage token_flush ran at $(/bin/date) with exit code $? >> #{log_file} + }.gsub!(/\n/, '') +end +# TODO(luisg): We can remove the \n substitution in the cron command when https://tickets.opscode.com/browse/CHEF-5238 is fixed diff --git a/chef/cookbooks/openstack-identity/resources/register.rb b/chef/cookbooks/openstack-identity/resources/register.rb index 2e6b086..cb855de 100644 --- a/chef/cookbooks/openstack-identity/resources/register.rb +++ b/chef/cookbooks/openstack-identity/resources/register.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-identity # Resource:: register @@ -26,36 +27,42 @@ def initialize(*args) @action = :create end -Boolean = [TrueClass, FalseClass] +BOOLEAN = [TrueClass, FalseClass] -attribute :auth_uri, :kind_of => String -attribute :bootstrap_token, :kind_of => String +attribute :auth_uri, kind_of: String +attribute :bootstrap_token, kind_of: String # Used by both :create_service and :create_endpoint -attribute :service_type, :kind_of => String, :equal_to => [ "image", "identity", "compute", "storage", "ec2", "volume", "object-store", "metering", "network" ] +attribute :service_type, kind_of: String, equal_to: %w{image identity compute storage ec2 volume object-store metering network orchestration cloudformation} # :create_service specific attributes -attribute :service_name, :kind_of => String -attribute :service_description, :kind_of => String +attribute :service_name, kind_of: String +attribute :service_description, kind_of: String # :create_endpoint specific attributes -attribute :endpoint_region, :kind_of => String, :default => "RegionOne" -attribute :endpoint_adminurl, :kind_of => String -attribute :endpoint_internalurl, :kind_of => String -attribute :endpoint_publicurl, :kind_of => String +attribute :endpoint_region, kind_of: String, default: 'RegionOne' +attribute :endpoint_adminurl, kind_of: String +attribute :endpoint_internalurl, kind_of: String +attribute :endpoint_publicurl, kind_of: String # Used by both :create_tenant and :create_user -attribute :tenant_name, :kind_of => String +attribute :tenant_name, kind_of: String # :create_tenant specific attributes -attribute :tenant_description, :kind_of => String -attribute :tenant_enabled, :kind_of => Boolean, :default => true +attribute :tenant_description, kind_of: String +attribute :tenant_enabled, kind_of: BOOLEAN, default: true # :create_user specific attributes -attribute :user_name, :kind_of => String -attribute :user_pass, :kind_of => String -# attribute :user_email, :kind_of => String -attribute :user_enabled, :kind_of => Boolean, :default => true +attribute :user_name, kind_of: String +attribute :user_pass, kind_of: String +# attribute :user_email, kind_of: String +attribute :user_enabled, kind_of: BOOLEAN, default: true # Used by :create_role and :grant_role specific attributes -attribute :role_name, :kind_of => String +attribute :role_name, kind_of: String + +# Used by create_ec2_credentials +attribute :admin_tenant_name, kind_of: String +attribute :admin_user, kind_of: String +attribute :admin_pass, kind_of: String +attribute :identity_endpoint, kind_of: String diff --git a/chef/cookbooks/openstack-identity/spec/client-redhat_spec.rb b/chef/cookbooks/openstack-identity/spec/client-redhat_spec.rb new file mode 100644 index 0000000..d9d9fcd --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/client-redhat_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-identity::client' do + + describe 'redhat' do + + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'installs packages' do + expect(chef_run).to upgrade_package('python-keystoneclient') + end + end +end diff --git a/chef/cookbooks/openstack-identity/spec/client_spec.rb b/chef/cookbooks/openstack-identity/spec/client_spec.rb new file mode 100644 index 0000000..8988f4c --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/client_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-identity::client' do + + describe 'ubuntu' do + + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'installs packages' do + expect(chef_run).to upgrade_package('python-keystoneclient') + end + end +end diff --git a/chef/cookbooks/openstack-identity/spec/default_spec.rb b/chef/cookbooks/openstack-identity/spec/default_spec.rb deleted file mode 100644 index 808fe79..0000000 --- a/chef/cookbooks/openstack-identity/spec/default_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-identity::default" do -end diff --git a/chef/cookbooks/openstack-identity/spec/register_spec.rb b/chef/cookbooks/openstack-identity/spec/register_spec.rb index 04e6a3b..33848fa 100644 --- a/chef/cookbooks/openstack-identity/spec/register_spec.rb +++ b/chef/cookbooks/openstack-identity/spec/register_spec.rb @@ -1,215 +1,487 @@ -require_relative "spec_helper" +# encoding: UTF-8 -describe Chef::Provider::Execute do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-identity::default" - @node = @chef_run.node - @node.set["openstack"] = { - "identity" => { - "catalog" => { - "backend" => "sql" - } - } - } - @cookbook_collection = Chef::CookbookCollection.new([]) - @events = Chef::EventDispatch::Dispatcher.new - @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) +require_relative 'spec_helper' - @tenant_resource = Chef::Resource::OpenstackIdentityRegister.new("tenant1", @run_context) - @tenant_resource.tenant_name "tenant1" - @tenant_resource.tenant_description "tenant1 Tenant" +describe 'openstack-identity::default' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:cookbook_collection) { Chef::CookbookCollection.new([]) } + let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) } - @service_resource = Chef::Resource::OpenstackIdentityRegister.new("service1", @run_context) - @service_resource.service_type "compute" - @service_resource.service_name "service1" - @service_resource.service_description "service1 Service" + describe 'tenant_create' do + let(:resource) do + r = Chef::Resource::OpenstackIdentityRegister.new('tenant1', + run_context) + r.tenant_name('tenant1') + r.tenant_description('tenant1 Tenant') + r + end + let(:provider) do + Chef::Provider::OpenstackIdentityRegister.new(resource, run_context) + end - @endpoint_resource = Chef::Resource::OpenstackIdentityRegister.new("endpoint1", @run_context) - @endpoint_resource.endpoint_region "Region One" - @endpoint_resource.service_type "compute" - @endpoint_resource.endpoint_publicurl "http://public" - @endpoint_resource.endpoint_internalurl "http://internal" - @endpoint_resource.endpoint_adminurl "http://admin" + context 'when tenant does not already exist' do + before do + provider.stub(:identity_uuid) + .with(resource, 'tenant', 'name', 'tenant1') + provider.stub(:identity_command) + .with(resource, 'tenant-create', + 'name' => 'tenant1', + 'description' => 'tenant1 Tenant', + 'enabled' => true) + .and_return(true) + end - @role_resource = Chef::Resource::OpenstackIdentityRegister.new("role1", @run_context) - @role_resource.role_name "role1" + it 'should create a tenant' do + provider.run_action(:create_tenant) - @user_resource = Chef::Resource::OpenstackIdentityRegister.new("user1", @run_context) - @user_resource.user_name "user1" - @user_resource.tenant_name "tenant1" - @user_resource.user_pass "password" + expect(resource).to be_updated + end + end - @grant_resource = Chef::Resource::OpenstackIdentityRegister.new("grant1", @run_context) - @grant_resource.user_name "user1" - @grant_resource.tenant_name "tenant1" - @grant_resource.role_name "role1" + context 'when tenant does already exist' do + before do + provider.stub(:identity_uuid) + .with(resource, 'tenant', 'name', 'tenant1') + .and_return('1234567890ABCDEFGH') + end - @ec2_resource = Chef::Resource::OpenstackIdentityRegister.new("ec2", @run_context) - @ec2_resource.user_name "user1" - @ec2_resource.tenant_name "tenant1" - end + it 'should not create a tenant' do + provider.run_action(:create_tenant) - it "should create a tenant" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@tenant_resource, @run_context) - provider.stub!(:identity_uuid).with(@tenant_resource, "tenant", "name", "tenant1") - provider.stub!(:identity_command).with(@tenant_resource, "tenant-create", - {"name" => "tenant1", "description" => "tenant1 Tenant", "enabled" => true}) - provider.run_action(:create_tenant) - @tenant_resource.should be_updated - end - it "should not create a new tenant if already exists" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@tenant_resource, @run_context) - provider.stub!(:identity_uuid).with(@tenant_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") - provider.run_action(:create_tenant) - @tenant_resource.should_not be_updated - end - it "should create a service" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@service_resource, @run_context) - provider.stub!(:identity_uuid).with(@service_resource, "service", "type", "compute") - provider.stub!(:identity_command).with(@service_resource, "service-create", - {"type" => "compute", "name" => "service1", "description" => "service1 Service"}) - provider.run_action(:create_service) - @service_resource.should be_updated - end - it "should not create a service if already exists" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@service_resource, @run_context) - provider.stub!(:identity_uuid).with(@service_resource, "service", "type", "compute").and_return("1234567890ABCDEFGH") - provider.run_action(:create_service) - @service_resource.should_not be_updated - end - it "should not create a service if using a templated backend" do - node = Chef::Node.new - node.set["openstack"] = {"identity" => {"catalog" => { "backend" => "templated" }} } - cookbook_collection = Chef::CookbookCollection.new([]) - events = Chef::EventDispatch::Dispatcher.new - run_context = Chef::RunContext.new(node, cookbook_collection, events) - provider = Chef::Provider::OpenstackIdentityRegister.new(@service_resource, run_context) - provider.run_action(:create_service) - @service_resource.should_not be_updated - end - it "should create an endpoint" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@endpoint_resource, @run_context) - provider.stub!(:identity_uuid).with(@endpoint_resource, "service", "type", "compute").and_return("1234567890ABCDEFGH") - provider.stub!(:identity_uuid).with(@endpoint_resource, "endpoint", "service_id", "1234567890ABCDEFGH") - provider.stub!(:identity_command).with(@endpoint_resource, "endpoint-create", { - "region" => "Region One", "service_id" => "1234567890ABCDEFGH", "publicurl" => "http://public", - "internalurl" => "http://internal", "adminurl" => "http://admin"}) - provider.run_action(:create_endpoint) - @endpoint_resource.should be_updated - end - it "should not create a endpoint if already exists" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@endpoint_resource, @run_context) - provider.stub!(:identity_uuid).with(@endpoint_resource, "service", "type", "compute").and_return("1234567890ABCDEFGH") - provider.stub!(:identity_uuid).with(@endpoint_resource, "endpoint", "service_id", "1234567890ABCDEFGH").and_return("0987654321HGFEDCBA") - provider.run_action(:create_endpoint) - @endpoint_resource.should_not be_updated - end - it "should not create an endpoint if using a templated backend" do - node = Chef::Node.new - node.set["openstack"] = {"identity" => {"catalog" => { "backend" => "templated" }} } - cookbook_collection = Chef::CookbookCollection.new([]) - events = Chef::EventDispatch::Dispatcher.new - run_context = Chef::RunContext.new(node, cookbook_collection, events) - provider = Chef::Provider::OpenstackIdentityRegister.new(@endpoint_resource, run_context) - provider.run_action(:create_endpoint) - @endpoint_resource.should_not be_updated - end - it "should create a role" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@role_resource, @run_context) - provider.stub!(:identity_uuid).with(@role_resource, "role", "name", "role1") - provider.stub!(:identity_command).with(@role_resource, "role-create", {"name" => "role1"}) - provider.run_action(:create_role) - @role_resource.should be_updated - end - it "should not create a role if already exists" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@role_resource, @run_context) - provider.stub!(:identity_uuid).with(@role_resource, "role", "name", "role1").and_return("1234567890ABCDEFGH") - provider.run_action(:create_role) - @role_resource.should_not be_updated - end - it "should create a user" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@user_resource, @run_context) - provider.stub!(:identity_uuid).with(@user_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") - provider.stub!(:identity_command).with(@user_resource, "user-list", {"tenant-id" => "1234567890ABCDEFGH"}) - provider.stub!(:identity_command).with(@user_resource, "user-create", - {"name" => "user1", "tenant-id" => "1234567890ABCDEFGH", "pass" => "password", "enabled" => true}) - provider.stub!(:prettytable_to_array).and_return([]) - provider.run_action(:create_user) - @user_resource.should be_updated - end - it "should not create a user if already exists" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@user_resource, @run_context) - provider.stub!(:identity_uuid).with(@user_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") - provider.stub!(:identity_command).with(@user_resource, "user-list", {"tenant-id" => "1234567890ABCDEFGH"}) - provider.stub!(:prettytable_to_array).and_return([{"name" => "user1"}]) - provider.stub!(:identity_uuid).with(@user_resource, "user", "name", "user1").and_return("HGFEDCBA0987654321") - provider.run_action(:create_user) - @user_resource.should_not be_updated - end - it "should grant a role" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@grant_resource, @run_context) - provider.stub!(:identity_uuid).with(@grant_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") - provider.stub!(:identity_uuid).with(@grant_resource, "user", "name", "user1").and_return("HGFEDCBA0987654321") - provider.stub!(:identity_uuid).with(@grant_resource, "role", "name", "role1").and_return("ABC1234567890DEF") - provider.stub!(:identity_uuid).with(@grant_resource, "user-role", "name", "role1", - { "tenant-id" => "1234567890ABCDEFGH", "user-id" => "HGFEDCBA0987654321" }).and_return("ABCD1234567890EFGH") - provider.stub!(:identity_command).with(@grant_resource, "user-role-add", - {"tenant-id" => "1234567890ABCDEFGH", "role-id" => "ABC1234567890DEF", "user-id" => "HGFEDCBA0987654321"}) - provider.run_action(:grant_role) - @grant_resource.should be_updated - end - it "should not grant a role if already granted" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@grant_resource, @run_context) - provider.stub!(:identity_uuid).with(@grant_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") - provider.stub!(:identity_uuid).with(@grant_resource, "user", "name", "user1").and_return("HGFEDCBA0987654321") - provider.stub!(:identity_uuid).with(@grant_resource, "role", "name", "role1").and_return("ABC1234567890DEF") - provider.stub!(:identity_uuid).with(@grant_resource, "user-role", "name", "role1", - {"tenant-id" => "1234567890ABCDEFGH", "user-id" => "HGFEDCBA0987654321" }).and_return("ABC1234567890DEF") - provider.stub!(:identity_command).with(@grant_resource, "user-role-add", - {"tenant-id" => "1234567890ABCDEFGH", "role-id" => "ABC1234567890DEF", "user-id" => "HGFEDCBA0987654321"}) - provider.run_action(:grant_role) - @grant_resource.should_not be_updated - end - it "should grant ec2 creds" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@ec2_resource, @run_context) - provider.stub!(:identity_uuid).with(@ec2_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") - provider.stub!(:identity_uuid).with(@ec2_resource, "user", "name", "user1", - {"tenant-id" => "1234567890ABCDEFGH"}).and_return("HGFEDCBA0987654321") - provider.stub!(:identity_uuid).with(@ec2_resource, "ec2-credentials", "tenant", "tenant1", - {"user-id" => "HGFEDCBA0987654321"}, "access") - provider.stub!(:identity_command).with(@ec2_resource, "ec2-credentials-create", - {"user-id" => "HGFEDCBA0987654321", "tenant-id" => "1234567890ABCDEFGH"}) - provider.stub!(:prettytable_to_array).and_return([{"access" => "access", "secret" => "secret"}]) - provider.run_action(:create_ec2_credentials) - @ec2_resource.should be_updated - end - it "should grant ec2 creds if they already exist" do - provider = Chef::Provider::OpenstackIdentityRegister.new(@ec2_resource, @run_context) - provider.stub!(:identity_uuid).with(@ec2_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") - provider.stub!(:identity_uuid).with(@ec2_resource, "user", "name", "user1", - {"tenant-id" => "1234567890ABCDEFGH"}).and_return("HGFEDCBA0987654321") - provider.stub!(:identity_uuid).with(@ec2_resource, "ec2-credentials", "tenant", "tenant1", - {"user-id" => "HGFEDCBA0987654321"}, "access").and_return("ABC1234567890DEF") - provider.run_action(:create_ec2_credentials) - @ec2_resource.should_not be_updated - end + expect(resource).to_not be_updated + end + end + end - describe "#identity_command" do - it "should handle false values and long descriptions" do - provider = Chef::Provider::OpenstackIdentityRegister.new( - @user_resource, @run_context) + describe 'service_create' do + let(:resource) do + r = Chef::Resource::OpenstackIdentityRegister.new('service1', + run_context) + r.service_type('compute') + r.service_name('service1') + r.service_description('service1 Service') + r + end + let(:provider) do + Chef::Provider::OpenstackIdentityRegister.new(resource, run_context) + end - provider.stub!(:shell_out).with( - ["keystone", "user-create", "--enabled", "false", - "--description", "more than one word"], - {:env => {"OS_SERVICE_ENDPOINT" => nil, "OS_SERVICE_TOKEN" => nil}} - ).and_return double("shell_out", :exitstatus => 0, :stdout => "good") + context 'catalog.backend is sql' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'sql' + end - provider.send( - :identity_command, @user_resource, "user-create", - {"enabled" => false, "description" => "more than one word"} - ).should eq "good" + context 'when service does not already exist' do + it 'should create a service' do + provider.stub(:identity_uuid) + .with(resource, 'service', 'type', 'compute') + provider.stub(:identity_command) + .with(resource, 'service-create', + 'type' => 'compute', + 'name' => 'service1', + 'description' => 'service1 Service') + .and_return(true) + provider.run_action(:create_service) + + expect(resource).to be_updated + end + end + + context 'when service does not already exist' do + it 'should not create a service' do + provider.stub(:identity_uuid) + .with(resource, 'service', 'type', 'compute') + .and_return('1234567890ABCDEFGH') + provider.run_action(:create_service) + + expect(resource).to_not be_updated + end + end + end + + context 'catalog.backend is templated' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'templated' + end + + it 'should not create a service if using a templated backend' do + provider.run_action(:create_service) + expect(resource).to_not be_updated + end + end + end + + describe 'service_create' do + let(:resource) do + r = Chef::Resource::OpenstackIdentityRegister.new('endpoint1', + run_context) + r.endpoint_region('Region One') + r.service_type('compute') + r.endpoint_publicurl('http://public') + r.endpoint_internalurl('http://internal') + r.endpoint_adminurl('http://admin') + r + end + let(:provider) do + Chef::Provider::OpenstackIdentityRegister.new(resource, run_context) + end + + context 'catalog.backend is sql' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'sql' + end + + context 'when endpoint does not already exist' do + before do + provider.stub(:identity_uuid) + .with(resource, 'service', 'type', 'compute') + .and_return('1234567890ABCDEFGH') + provider.stub(:identity_uuid) + .with(resource, 'endpoint', 'service_id', '1234567890ABCDEFGH') + provider.stub(:identity_command) + .with(resource, 'endpoint-create', + 'region' => 'Region One', + 'service_id' => '1234567890ABCDEFGH', + 'publicurl' => 'http://public', + 'internalurl' => 'http://internal', + 'adminurl' => 'http://admin') + end + + it 'should create an endpoint' do + provider.run_action(:create_endpoint) + expect(resource).to be_updated + end + end + + context 'when endpoint does already exist' do + before do + provider.stub(:identity_uuid) + .with(resource, 'service', 'type', 'compute') + .and_return('1234567890ABCDEFGH') + provider.stub(:identity_uuid) + .with(resource, 'endpoint', 'service_id', '1234567890ABCDEFGH') + .and_return('0987654321HGFEDCBA') + end + + it 'should not create an endpoint' do + provider.run_action(:create_endpoint) + expect(resource).to_not be_updated + end + end + + context '#identity_uuid, when service id for Region One already exist' do + before do + output = ' | 000d9c447d124754a197fc612f9d63d7 | Region One | http://public | http://internal | http://admin | f9511a66e0484f3dbd1584065e8bab1c ' + output_array = [{ 'id' => '000d9c447d124754a197fc612f9d63d7', 'region' => 'Region One', 'publicurl' => 'http://public', 'internalurl' => 'http://internal', 'adminurl' => 'http://admin', 'service_id' => 'f9511a66e0484f3dbd1584065e8bab1c' }] + provider.stub(:identity_command) + .with(resource, 'endpoint-list', {}) + .and_return(output) + provider.stub(:prettytable_to_array) + .with(output) + .and_return(output_array) + end + + it 'endpoint uuid should be returned' do + provider.send(:identity_uuid, resource, 'endpoint', 'service_id', 'f9511a66e0484f3dbd1584065e8bab1c').should eq('000d9c447d124754a197fc612f9d63d7') + end + end + + context '#identity_uuid, when service id for Region Two does not exist' do + before do + output = ' | 000d9c447d124754a197fc612f9d63d7 | Region Two | http://public | http://internal | http://admin | f9511a66e0484f3dbd1584065e8bab1c ' + output_array = [{ 'id' => '000d9c447d124754a197fc612f9d63d7', 'region' => 'Region Two', 'publicurl' => 'http://public', 'internalurl' => 'http://internal', 'adminurl' => 'http://admin', 'service_id' => 'f9511a66e0484f3dbd1584065e8bab1c' }] + provider.stub(:identity_command) + .with(resource, 'endpoint-list', {}) + .and_return(output) + provider.stub(:prettytable_to_array) + .with(output) + .and_return(output_array) + end + + it 'no endpoint uuid should be returned' do + provider.send(:identity_uuid, resource, 'endpoint', 'service_id', 'f9511a66e0484f3dbd1584065e8bab1c').should eq(nil) + end + end + + context '#search_uuid' do + it 'required_hash only has key id' do + output_array = [{ 'id' => '000d9c447d124754a197fc612f9d63d7', 'region' => 'Region Two', 'publicurl' => 'http://public' }] + provider.send(:search_uuid, output_array, 'id' , 'id' => '000d9c447d124754a197fc612f9d63d7').should eq('000d9c447d124754a197fc612f9d63d7') + provider.send(:search_uuid, output_array, 'id' , 'id' => 'abc').should eq(nil) + end + + it 'required_hash has key id and region' do + output_array = [{ 'id' => '000d9c447d124754a197fc612f9d63d7', 'region' => 'Region Two', 'publicurl' => 'http://public' }] + provider.send(:search_uuid, output_array, 'id' , 'id' => '000d9c447d124754a197fc612f9d63d7', 'region' => 'Region Two').should eq('000d9c447d124754a197fc612f9d63d7') + provider.send(:search_uuid, output_array, 'id' , 'id' => '000d9c447d124754a197fc612f9d63d7', 'region' => 'Region One').should eq(nil) + provider.send(:search_uuid, output_array, 'id' , 'id' => '000d9c447d124754a197fc612f9d63d7', 'region' => 'Region Two', 'key' => 'value').should eq(nil) + end + end + end + + context 'catalog.backend is templated' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'templated' + end + + it 'should not create an endpoint' do + provider.run_action(:create_endpoint) + expect(resource).to_not be_updated + end + end + end + + describe 'role create' do + let(:resource) do + r = Chef::Resource::OpenstackIdentityRegister.new('role1', run_context) + r.role_name('role1') + r + end + let(:provider) do + Chef::Provider::OpenstackIdentityRegister.new(resource, run_context) + end + + context 'when role does not already exist' do + before do + provider.stub(:identity_uuid) + .with(resource, 'role', 'name', 'role1') + provider.stub(:identity_command) + .with(resource, 'role-create', + 'name' => 'role1') + end + + it 'should create a role' do + provider.run_action(:create_role) + expect(resource).to be_updated + end + end + + context 'when role already exist' do + before do + provider.stub(:identity_uuid) + .with(resource, 'role', 'name', 'role1') + .and_return('1234567890ABCDEFGH') + end + + it 'should not create a role' do + provider.run_action(:create_role) + expect(resource).to_not be_updated + end + end + end + + describe 'user create' do + let(:resource) do + r = Chef::Resource::OpenstackIdentityRegister.new('user1', run_context) + r.user_name('user1') + r.tenant_name('tenant1') + r.user_pass('password') + r + end + let(:provider) do + Chef::Provider::OpenstackIdentityRegister.new(resource, run_context) + end + + context 'when user does not already exist' do + before do + provider.stub(:identity_uuid) + .with(resource, 'tenant', 'name', 'tenant1') + .and_return('1234567890ABCDEFGH') + provider.stub(:identity_command) + .with(resource, 'user-list', + 'tenant-id' => '1234567890ABCDEFGH') + provider.stub(:identity_command) + .with(resource, 'user-create', + 'name' => 'user1', + 'tenant-id' => '1234567890ABCDEFGH', + 'pass' => 'password', + 'enabled' => true) + provider.stub(:prettytable_to_array) + .and_return([]) + end + + it 'should create a user' do + provider.run_action(:create_user) + expect(resource).to be_updated + end + end + + context 'when user already exist' do + before do + provider.stub(:identity_uuid) + .with(resource, 'tenant', 'name', 'tenant1') + .and_return('1234567890ABCDEFGH') + provider.stub(:identity_command) + .with(resource, 'user-list', + 'tenant-id' => '1234567890ABCDEFGH') + provider.stub(:prettytable_to_array) + .and_return([{ 'name' => 'user1' }]) + provider.stub(:identity_uuid) + .with(resource, 'user', 'name', 'user1') + .and_return('HGFEDCBA0987654321') + end + + it 'should not create a user' do + provider.run_action(:create_user) + expect(resource).to_not be_updated + end + end + + describe '#identity_command' do + it 'should handle false values and long descriptions' do + provider.stub(:shell_out) + .with(['keystone', 'user-create', '--enabled', + 'false', '--description', 'more than one word'], + env: { + 'OS_SERVICE_ENDPOINT' => nil, + 'OS_SERVICE_TOKEN' => nil }) + .and_return double('shell_out', exitstatus: 0, stdout: 'good') + + expect( + provider.send(:identity_command, resource, 'user-create', + 'enabled' => false, + 'description' => 'more than one word') + ).to eq('good') + end + end + end + + describe 'role grant' do + let(:resource) do + r = Chef::Resource::OpenstackIdentityRegister.new('grant1', run_context) + r.user_name('user1') + r.tenant_name('tenant1') + r.role_name('role1') + r + end + let(:provider) do + Chef::Provider::OpenstackIdentityRegister.new(resource, run_context) + end + + context 'when role has not already been granted' do + before do + provider.stub(:identity_uuid) + .with(resource, 'tenant', 'name', 'tenant1') + .and_return('1234567890ABCDEFGH') + provider.stub(:identity_uuid) + .with(resource, 'user', 'name', 'user1') + .and_return('HGFEDCBA0987654321') + provider.stub(:identity_uuid) + .with(resource, 'role', 'name', 'role1') + .and_return('ABC1234567890DEF') + provider.stub(:identity_uuid) + .with(resource, 'user-role', 'name', 'role1', + 'tenant-id' => '1234567890ABCDEFGH', + 'user-id' => 'HGFEDCBA0987654321') + .and_return('ABCD1234567890EFGH') + provider.stub(:identity_command) + .with(resource, 'user-role-add', + 'tenant-id' => '1234567890ABCDEFGH', + 'role-id' => 'ABC1234567890DEF', + 'user-id' => 'HGFEDCBA0987654321') + end + + it 'should grant a role' do + provider.run_action(:grant_role) + expect(resource).to be_updated + end + end + + context 'when role has already been granted' do + before do + provider.stub(:identity_uuid) + .with(resource, 'tenant', 'name', 'tenant1') + .and_return('1234567890ABCDEFGH') + provider.stub(:identity_uuid) + .with(resource, 'user', 'name', 'user1') + .and_return('HGFEDCBA0987654321') + provider.stub(:identity_uuid) + .with(resource, 'role', 'name', 'role1') + .and_return('ABC1234567890DEF') + provider.stub(:identity_uuid) + .with(resource, 'user-role', 'name', 'role1', + 'tenant-id' => '1234567890ABCDEFGH', + 'user-id' => 'HGFEDCBA0987654321') + .and_return('ABC1234567890DEF') + provider.stub(:identity_command) + .with(resource, 'user-role-add', + 'tenant-id' => '1234567890ABCDEFGH', + 'role-id' => 'ABC1234567890DEF', + 'user-id' => 'HGFEDCBA0987654321') + end + + it 'should not grant a role' do + provider.run_action(:grant_role) + expect(resource).to_not be_updated + end + end + end + + describe 'ec2_credentials create' do + let(:resource) do + r = Chef::Resource::OpenstackIdentityRegister.new('ec2', run_context) + r.user_name('user1') + r.tenant_name('tenant1') + r.admin_tenant_name('admintenant1') + r.admin_user('adminuser1') + r.admin_pass('password') + r.identity_endpoint('http://admin') + r + end + let(:provider) do + Chef::Provider::OpenstackIdentityRegister.new(resource, run_context) + end + + context 'when ec2 creds have not already been created' do + before do + provider.stub(:identity_uuid) + .with(resource, 'tenant', 'name', 'tenant1') + .and_return('1234567890ABCDEFGH') + provider.stub(:identity_uuid) + .with(resource, 'user', 'name', 'user1', + 'tenant-id' => '1234567890ABCDEFGH') + .and_return('HGFEDCBA0987654321') + provider.stub(:identity_uuid) + .with(resource, 'ec2-credentials', 'tenant', 'tenant1', + { 'user-id' => 'HGFEDCBA0987654321' }, 'access') + provider.stub(:identity_command) + .with(resource, 'ec2-credentials-create', + 'user-id' => 'HGFEDCBA0987654321', + 'tenant-id' => '1234567890ABCDEFGH') + provider.stub(:prettytable_to_array) + .and_return([{ 'access' => 'access', 'secret' => 'secret' }]) + end + + it 'should grant ec2 creds' do + provider.run_action(:create_ec2_credentials) + expect(resource).to be_updated + end + end + + context 'when ec2 creds have not already been created' do + before do + provider.stub(:identity_uuid) + .with(resource, 'tenant', 'name', 'tenant1') + .and_return('1234567890ABCDEFGH') + provider.stub(:identity_uuid) + .with(resource, 'user', 'name', 'user1', + 'tenant-id' => '1234567890ABCDEFGH') + .and_return('HGFEDCBA0987654321') + provider.stub(:identity_uuid) + .with(resource, 'ec2-credentials', 'tenant', 'tenant1', + { 'user-id' => 'HGFEDCBA0987654321' }, 'access') + .and_return('ABC1234567890DEF') + end + + it 'should grant ec2 creds if they already exist' do + provider.run_action(:create_ec2_credentials) + expect(resource).to_not be_updated + end + end end end end diff --git a/chef/cookbooks/openstack-identity/spec/registration_spec.rb b/chef/cookbooks/openstack-identity/spec/registration_spec.rb new file mode 100644 index 0000000..28d53a5 --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/registration_spec.rb @@ -0,0 +1,230 @@ +# encoding: UTF-8 +# + +require_relative 'spec_helper' + +describe 'openstack-identity::registration' do + describe 'ubuntu' do + let(:node) { runner.node } + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:chef_run) { runner.converge(described_recipe) } + let(:node_add_user) do + node.set_unless['openstack']['identity']['users'] = { + 'user1' => { + 'default_tenant' => 'default_tenant1', + 'password' => 'secret1', + 'roles' => { + 'role1' => ['role_tenant1'], + 'role2' => ['default_tenant1'] + } + } + } + end + + include_context 'identity_stubs' + + describe 'tenant registration' do + context 'default tenants' do + ['admin'].each do |tenant_name| + it "registers the #{tenant_name} tenant" do + expect(chef_run).to create_tenant_openstack_identity_register( + "Register '#{tenant_name}' Tenant" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: tenant_name, + tenant_description: "#{tenant_name} Tenant" + ) + end + end + end + + context 'configured tenants from users attribute' do + before { node_add_user } + ['default_tenant1', 'role_tenant1'].each do |tenant_name| + it "registers the #{tenant_name} tenant" do + expect(chef_run).to create_tenant_openstack_identity_register( + "Register '#{tenant_name}' Tenant" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: tenant_name, + tenant_description: "#{tenant_name} Tenant" + ) + end + end + end + end + + describe 'role registration' do + context 'default roles' do + %w{admin KeystoneAdmin KeystoneServiceAdmin}.each do |role_name| + it "registers the #{role_name} role" do + expect(chef_run).to create_role_openstack_identity_register( + "Register '#{role_name}' Role" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + role_name: role_name + ) + end + end + end + + context 'configured roles derived from users attribute' do + before { node_add_user } + + ['role1', 'role2'].each do |role_name| + it "registers the #{role_name} role" do + expect(chef_run).to create_role_openstack_identity_register( + "Register '#{role_name}' Role" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + role_name: role_name + ) + end + end + end + end + + describe 'user registration' do + context 'default users' do + user_admin = [ + 'admin', 'admin', + ['admin', 'KeystoneAdmin', 'KeystoneServiceAdmin'] + ] + + [user_admin].each do |user, tenant, roles| + context "#{user} user" do + it "registers the #{user} user" do + expect(chef_run).to create_user_openstack_identity_register( + "Register '#{user}' User" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + user_name: user, + user_pass: '', + tenant_name: tenant + ) + end + + roles.each do |role| + it "grants '#{role}' role to '#{user}' user in 'admin' tenant" do + expect(chef_run).to grant_role_openstack_identity_register( + "Grant '#{role}' Role to '#{user}' User in 'admin' Tenant" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + user_name: user, + role_name: role, + tenant_name: 'admin', + action: [:grant_role] + ) + end + end + + end + end + end + + context 'configured user' do + before { node_add_user } + + it 'registers the user1 user' do + expect(chef_run).to create_user_openstack_identity_register( + "Register 'user1' User" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + user_name: 'user1', + user_pass: 'secret1', + tenant_name: 'default_tenant1' + ) + end + + it "grants 'role1' role to 'user1' user in 'role_tenant1' tenant" do + expect(chef_run).to grant_role_openstack_identity_register( + "Grant 'role1' Role to 'user1' User in 'role_tenant1' Tenant" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + user_name: 'user1', + role_name: 'role1', + tenant_name: 'role_tenant1' + ) + end + end + end + + describe 'service registration' do + context 'with templated catalog backend' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'templated' + end + + it 'does not register identity service' do + expect(chef_run).to_not create_service_openstack_identity_register( + 'Register Identity Service' + ) + end + end + + context 'with sql catalog backend' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'sql' + end + it 'registers identity service' do + expect(chef_run).to create_service_openstack_identity_register( + 'Register Identity Service' + ).with( + service_name: 'keystone', + service_type: 'identity', + service_description: 'Keystone Identity Service' + ) + end + end + end + + describe 'endpoint registration' do + context 'with templated catalog backend' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'templated' + end + + it 'does not register identity endpoint' do + expect(chef_run).to_not create_endpoint_openstack_identity_register( + 'Register Identity Endpoint' + ) + end + end + + context 'with sql catalog backend' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'sql' + end + + it 'registers identity endpoint' do + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Identity Endpoint' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'identity', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:35357/v2.0', + endpoint_internalurl: 'http://127.0.0.1:35357/v2.0', + endpoint_publicurl: 'http://127.0.0.1:5000/v2.0' + ) + end + + it 'overrides identity endpoint region' do + node.set['openstack']['identity']['region'] = 'identityRegion' + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Identity Endpoint' + ).with(endpoint_region: 'identityRegion') + end + end + end + end +end diff --git a/chef/cookbooks/openstack-identity/spec/server-opensuse_spec.rb b/chef/cookbooks/openstack-identity/spec/server-opensuse_spec.rb deleted file mode 100644 index 77e18ea..0000000 --- a/chef/cookbooks/openstack-identity/spec/server-opensuse_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-identity::server" do - before { identity_stubs } - describe "suse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-identity::server" - end - - it "installs mysql python packages" do - expect(@chef_run).to install_package "python-mysql" - end - - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| - n.set["openstack"]["db"]["identity"]["db_type"] = "postgresql" - end - chef_run.converge "openstack-identity::server" - - expect(chef_run).to install_package "python-psycopg2" - end - - it "installs memcache python packages" do - expect(@chef_run).to install_package "python-python-memcached" - end - - it "installs keystone packages" do - expect(@chef_run).to upgrade_package "openstack-keystone" - end - - it "starts keystone on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-keystone" - end - - describe "/etc/keystone" do - before do - @dir = @chef_run.directory "/etc/keystone" - end - - it "has proper owner" do - expect(@dir).to be_owned_by "openstack-keystone", "openstack-keystone" - end - end - - describe "/etc/keystone/ssl" do - before do - opts = ::OPENSUSE_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts do |n| - n.set["openstack"]["auth"]["strategy"] = "pki" - end - chef_run.converge "openstack-identity::server" - @dir = chef_run.directory "/etc/keystone/ssl" - end - - it "has proper owner" do - expect(@dir). - to be_owned_by "openstack-keystone", "openstack-keystone" - end - end - - it "deletes keystone.db" do - expect(@chef_run).to delete_file "/var/lib/keystone/keystone.db" - end - - it "runs pki setup" do - opts = ::OPENSUSE_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts do |n| - n.set["openstack"]["auth"]["strategy"] = "pki" - end - chef_run.converge "openstack-identity::server" - cmd = "keystone-manage pki_setup" - - expect(chef_run).to execute_command(cmd).with( - :user => "openstack-keystone" - ) - end - - describe "keystone.conf" do - before do - @template = @chef_run.template "/etc/keystone/keystone.conf" - end - - it "has proper owner" do - expect(@template). - to be_owned_by "openstack-keystone", "openstack-keystone" - end - - it "template contents" do - pending "TODO: implement" - end - end - - describe "default_catalog.templates" do - before do - opts = ::OPENSUSE_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts do |n| - n.set["openstack"]["identity"]["catalog"]["backend"] = "templated" - end - chef_run.converge "openstack-identity::server" - @template = chef_run. - template "/etc/keystone/default_catalog.templates" - end - - it "has proper owner" do - expect(@template). - to be_owned_by "openstack-keystone", "openstack-keystone" - end - - it "template contents" do - pending "TODO: implement" - end - end - end -end diff --git a/chef/cookbooks/openstack-identity/spec/server-redhat_spec.rb b/chef/cookbooks/openstack-identity/spec/server-redhat_spec.rb index 7061d33..f3b15b0 100644 --- a/chef/cookbooks/openstack-identity/spec/server-redhat_spec.rb +++ b/chef/cookbooks/openstack-identity/spec/server-redhat_spec.rb @@ -1,36 +1,48 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# -describe "openstack-identity::server" do - before { identity_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-identity::server" +require_relative 'spec_helper' + +describe 'openstack-identity::server' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'identity_stubs' + + it 'converges when configured to use sqlite db backend' do + node.set['openstack']['db']['identity']['service_type'] = 'sqlite' + expect { chef_run }.to_not raise_error end - it "installs mysql python packages" do - expect(@chef_run).to install_package "MySQL-python" + it 'upgrades mysql python packages' do + expect(chef_run).to upgrade_package('MySQL-python') end - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| - n.set["openstack"]["db"]["identity"]["db_type"] = "postgresql" + it 'upgrades db2 python packages if explicitly told' do + node.set['openstack']['db']['identity']['service_type'] = 'db2' + + ['python-ibm-db', 'python-ibm-db-sa'].each do |pkg| + expect(chef_run).to upgrade_package(pkg) end - chef_run.converge "openstack-identity::server" - - expect(chef_run).to install_package "python-psycopg2" end - it "installs memcache python packages" do - expect(@chef_run).to install_package "python-memcached" + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['identity']['service_type'] = 'postgresql' + expect(chef_run).to upgrade_package('python-psycopg2') end - it "installs keystone packages" do - expect(@chef_run).to upgrade_package "openstack-keystone" + it 'upgrades memcache python packages' do + expect(chef_run).to upgrade_package('python-memcached') end - it "starts keystone on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-keystone" + it 'upgrades keystone packages' do + expect(chef_run).to upgrade_package('openstack-keystone') + end + + it 'starts keystone on boot' do + expect(chef_run).to enable_service('openstack-keystone') end end end diff --git a/chef/cookbooks/openstack-identity/spec/server-suse_spec.rb b/chef/cookbooks/openstack-identity/spec/server-suse_spec.rb new file mode 100644 index 0000000..37c5a2f --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/server-suse_spec.rb @@ -0,0 +1,86 @@ +# encoding: UTF-8 +# + +require_relative 'spec_helper' + +describe 'openstack-identity::server' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'identity_stubs' + + it 'converges when configured to use sqlite db backend' do + node.set['openstack']['db']['identity']['service_type'] = 'sqlite' + expect { chef_run }.to_not raise_error + end + + it 'upgrades mysql python packages' do + expect(chef_run).to upgrade_package('python-mysql') + end + + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['identity']['service_type'] = 'postgresql' + expect(chef_run).to upgrade_package('python-psycopg2') + end + + it 'upgrades memcache python packages' do + expect(chef_run).to upgrade_package('python-python-memcached') + end + + it 'upgrades keystone packages' do + expect(chef_run).to upgrade_package('openstack-keystone') + end + + it 'starts keystone on boot' do + expect(chef_run).to enable_service('openstack-keystone') + end + + describe '/etc/keystone' do + let(:dir) { chef_run.directory('/etc/keystone') } + + it 'has proper owner' do + expect(dir.owner).to eq('openstack-keystone') + expect(dir.group).to eq('openstack-keystone') + end + end + + describe '/etc/keystone/ssl' do + before { node.set['openstack']['auth']['strategy'] = 'pki' } + let(:dir) { chef_run.directory('/etc/keystone/ssl') } + + it 'has proper owner' do + expect(dir.owner).to eq('openstack-keystone') + expect(dir.group).to eq('openstack-keystone') + end + end + + it 'deletes keystone.db' do + expect(chef_run).to delete_file('/var/lib/keystone/keystone.db') + end + + describe 'keystone.conf' do + let(:template) { chef_run.template '/etc/keystone/keystone.conf' } + + it 'has proper owner' do + expect(template.owner).to eq('openstack-keystone') + expect(template.group).to eq('openstack-keystone') + end + end + + describe 'default_catalog.templates' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'templated' + end + let(:template) do + chef_run.template('/etc/keystone/default_catalog.templates') + end + + it 'has proper owner' do + expect(template.owner).to eq('openstack-keystone') + expect(template.group).to eq('openstack-keystone') + end + end + end +end diff --git a/chef/cookbooks/openstack-identity/spec/server_spec.rb b/chef/cookbooks/openstack-identity/spec/server_spec.rb index 1e2f75b..771ec53 100644 --- a/chef/cookbooks/openstack-identity/spec/server_spec.rb +++ b/chef/cookbooks/openstack-identity/spec/server_spec.rb @@ -1,260 +1,729 @@ -require_relative "spec_helper" +# encoding: UTF-8 +# -describe "openstack-identity::server" do - before { identity_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["identity"]["syslog"]["use"] = true - n.set["openstack"]["endpoints"]["identity-api"] = { - "host" => "127.0.1.1", - "port" => "5000", - "scheme" => "https" +require_relative 'spec_helper' + +describe 'openstack-identity::server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set_unless['openstack']['endpoints']['identity-bind'] = { + 'host' => '127.0.1.1' + } + node.set_unless['openstack']['endpoints']['identity-api'] = { + 'host' => '127.0.1.1', + 'port' => '5000', + 'scheme' => 'https' + } + node.set_unless['openstack']['endpoints']['identity-admin'] = { + 'host' => '127.0.1.1', + 'port' => '35357', + 'scheme' => 'https' + } + + runner.converge(described_recipe) + end + + include Helpers + include_context 'identity_stubs' + + it 'runs logging recipe if node attributes say to' do + node.set['openstack']['identity']['syslog']['use'] = true + expect(chef_run).to include_recipe('openstack-common::logging') + end + + it 'does not run logging recipe' do + expect(chef_run).not_to include_recipe('openstack-common::logging') + end + + it 'converges when configured to use sqlite db backend' do + node.set['openstack']['db']['identity']['service_type'] = 'sqlite' + expect { chef_run }.to_not raise_error + end + + it 'upgrades mysql python packages' do + expect(chef_run).to upgrade_package('python-mysqldb') + end + + it 'upgrades postgresql python packages if explicitly told' do + node.set['openstack']['db']['identity']['service_type'] = 'postgresql' + expect(chef_run).to upgrade_package('python-psycopg2') + end + + it 'upgrades memcache python packages' do + expect(chef_run).to upgrade_package('python-memcache') + end + + it 'upgrades keystone packages' do + expect(chef_run).to upgrade_package('keystone') + end + + it 'starts keystone on boot' do + expect(chef_run).to enable_service('keystone') + end + + it 'sleep on keystone service enable' do + expect(chef_run.service('keystone')).to notify( + 'execute[Keystone: sleep]').to(:run) + end + + it 'has flush tokens cronjob running every day at 3:30am' do + expect(chef_run).to create_cron('keystone-manage-token-flush').with_command(/`which keystone-manage` token_flush/) + expect(chef_run).to create_cron('keystone-manage-token-flush').with_minute('0') + expect(chef_run).to create_cron('keystone-manage-token-flush').with_hour('*') + expect(chef_run).to create_cron('keystone-manage-token-flush').with_day('*') + expect(chef_run).to create_cron('keystone-manage-token-flush').with_weekday('*') + end + + it 'deletes flush tokens cronjob when tokens backend is not sql' do + node.set['openstack']['identity']['token']['backend'] = 'notsql' + expect(chef_run).to delete_cron('keystone-manage-token-flush') + end + + describe '/etc/keystone' do + let(:dir) { chef_run.directory('/etc/keystone') } + + it 'has proper owner' do + expect(dir.owner).to eq('keystone') + expect(dir.group).to eq('keystone') + end + + it 'has proper modes' do + expect(sprintf('%o', dir.mode)).to eq('700') + end + end + + describe 'ssl directories' do + let(:ssl_dir) { '/etc/keystone/ssl' } + let(:certs_dir) { "#{ssl_dir}/certs" } + let(:private_dir) { "#{ssl_dir}/private" } + + describe 'without pki' do + before { node.set['openstack']['auth']['strategy'] = 'uuid' } + + it 'does not create /etc/keystone/ssl' do + expect(chef_run).not_to create_directory(ssl_dir) + end + + it 'does not create /etc/keystone/ssl/certs' do + expect(chef_run).not_to create_directory(certs_dir) + end + + it 'does not create /etc/keystone/ssl/private' do + expect(chef_run).not_to create_directory(private_dir) + end + end + + describe 'with pki' do + describe '/etc/keystone/ssl' do + let(:dir_resource) { chef_run.directory(ssl_dir) } + + it 'creates /etc/keystone/ssl' do + expect(chef_run).to create_directory(ssl_dir) + end + + it 'has proper owner' do + expect(dir_resource.owner).to eq('keystone') + expect(dir_resource.group).to eq('keystone') + end + + it 'has proper modes' do + expect(sprintf('%o', dir_resource.mode)).to eq('700') + end + end + + describe '/etc/keystone/ssl/certs' do + let(:dir_resource) { chef_run.directory(certs_dir) } + + it 'creates /etc/keystone/ssl/certs' do + expect(chef_run).to create_directory(certs_dir) + end + + it 'has proper owner' do + expect(dir_resource.owner).to eq('keystone') + expect(dir_resource.group).to eq('keystone') + end + + it 'has proper modes' do + expect(sprintf('%o', dir_resource.mode)).to eq('755') + end + end + + describe '/etc/keystone/ssl/private' do + let(:dir_resource) { chef_run.directory(private_dir) } + + it 'creates /etc/keystone/ssl/private' do + expect(chef_run).to create_directory(private_dir) + end + + it 'has proper owner' do + expect(dir_resource.owner).to eq('keystone') + expect(dir_resource.group).to eq('keystone') + end + + it 'has proper modes' do + expect(sprintf('%o', dir_resource.mode)).to eq('750') + end + end + end + end + + describe 'ssl files' do + describe 'with pki' do + describe 'with {certfile,keyfile,ca_certs}_url attributes set' do + before do + node.set['openstack']['identity']['signing']['certfile_url'] = 'http://www.test.com/signing_cert.pem' + node.set['openstack']['identity']['signing']['keyfile_url'] = 'http://www.test.com/signing_key.pem' + node.set['openstack']['identity']['signing']['ca_certs_url'] = 'http://www.test.com/ca.pem' + end + + describe 'cert file' do + let(:cert_file) { node['openstack']['identity']['signing']['certfile'] } + let(:file_resource) { chef_run.remote_file(cert_file) } + + it 'creates files' do + expect(chef_run).to create_remote_file(cert_file) + end + + it 'has proper owner' do + expect(file_resource.owner).to eq('keystone') + expect(file_resource.group).to eq('keystone') + end + + it 'has proper modes' do + expect(sprintf('%o', file_resource.mode)).to eq('640') + end + + it 'notifies keystone restart' do + expect(file_resource).to notify('service[keystone]').to(:restart) + end + end + + describe 'key file' do + let(:key_file) { node['openstack']['identity']['signing']['keyfile'] } + let(:file_resource) { chef_run.remote_file(key_file) } + + it 'creates file' do + expect(chef_run).to create_remote_file(key_file) + end + + it 'has proper owner' do + expect(file_resource.owner).to eq('keystone') + expect(file_resource.group).to eq('keystone') + end + + it 'has proper modes' do + expect(sprintf('%o', file_resource.mode)).to eq('640') + end + + it 'notifies keystone restart' do + expect(file_resource).to notify('service[keystone]').to(:restart) + end + end + + describe 'ca_certs' do + let(:ca_certs) { node['openstack']['identity']['signing']['ca_certs'] } + let(:file_resource) { chef_run.remote_file(ca_certs) } + + it 'creates file' do + expect(chef_run).to create_remote_file(ca_certs) + end + + it 'has proper owner' do + expect(file_resource.owner).to eq('keystone') + expect(file_resource.group).to eq('keystone') + end + + it 'has proper modes' do + expect(sprintf('%o', file_resource.mode)).to eq('640') + end + + it 'notifies keystone restart' do + expect(file_resource).to notify('service[keystone]').to(:restart) + end + end + end + + describe 'without {certfile,keyfile,ca_certs}_url attributes set' do + it 'does not create cert file' do + expect(chef_run).not_to create_remote_file(node['openstack']['identity']['signing']['certfile']) + end + + it 'does not create key file' do + expect(chef_run).not_to create_remote_file(node['openstack']['identity']['signing']['keyfile']) + end + + it 'does not create ca_certs file' do + expect(chef_run).not_to create_remote_file(node['openstack']['identity']['signing']['ca_certs']) + end + end + end + + describe 'without pki' do + before { node.set['openstack']['auth']['strategy'] = 'uuid' } + + it 'does not create cert file' do + expect(chef_run).not_to create_remote_file(node['openstack']['identity']['signing']['certfile']) + end + + it 'does not create key file' do + expect(chef_run).not_to create_remote_file(node['openstack']['identity']['signing']['keyfile']) + end + + it 'does not create ca_certs file' do + expect(chef_run).not_to create_remote_file(node['openstack']['identity']['signing']['ca_certs']) + end + end + end + + it 'deletes keystone.db' do + expect(chef_run).to delete_file('/var/lib/keystone/keystone.db') + end + + it 'does not delete keystone.db when configured to use sqlite' do + node.set['openstack']['db']['identity']['service_type'] = 'sqlite' + expect(chef_run).not_to delete_file('/var/lib/keystone/keystone.db') + end + + describe 'pki setup' do + let(:cmd) { 'keystone-manage pki_setup' } + + describe 'without pki' do + before { node.set['openstack']['auth']['strategy'] = 'uuid' } + it 'does not execute' do + expect(chef_run).to_not run_execute(cmd).with( + user: 'keystone', + group: 'keystone' + ) + end + end + + describe 'with pki' do + describe 'without {certfile,keyfile,ca_certs}_url attributes set' do + it 'executes' do + ::FileTest.should_receive(:exists?) + .with('/etc/keystone/ssl/private/signing_key.pem') + .and_return(false) + + expect(chef_run).to run_execute(cmd).with( + user: 'keystone', + group: 'keystone' + ) + end + end + + describe 'with {certfile,keyfile,ca_certs}_url attributes set' do + before do + node.set['openstack']['identity']['signing']['certfile_url'] = 'http://www.test.com/signing_cert.pem' + node.set['openstack']['identity']['signing']['keyfile_url'] = 'http://www.test.com/signing_key.pem' + node.set['openstack']['identity']['signing']['ca_certs_url'] = 'http://www.test.com/ca.pem' + end + + it 'does not execute' do + expect(chef_run).to_not run_execute(cmd).with( + user: 'keystone', + group: 'keystone' + ) + end + end + + it 'does not execute when dir exists' do + ::FileTest.should_receive(:exists?) + .with('/etc/keystone/ssl/private/signing_key.pem') + .and_return(true) + + expect(chef_run).not_to run_execute(cmd).with( + user: 'keystone', + group: 'keystone' + ) + end + end + end + + describe 'keystone.conf' do + let(:path) { '/etc/keystone/keystone.conf' } + let(:resource) { chef_run.template(path) } + + describe 'file properties' do + it 'has correct owner' do + expect(resource.owner).to eq('keystone') + expect(resource.group).to eq('keystone') + end + + it 'has correct modes' do + expect(sprintf('%o', resource.mode)).to eq('644') + end + end + + it 'templates misc_keystone array correctly' do + node.set['openstack']['identity']['misc_keystone'] = ['MISC1=OPTION1', 'MISC2=OPTION2'] + expect(chef_run).to render_file(path).with_content( + /^MISC1=OPTION1$/) + expect(chef_run).to render_file(path).with_content( + /^MISC2=OPTION2$/) + end + + it 'notifies keystone restart' do + expect(resource).to notify('service[keystone]').to(:restart) + end + + it 'has rpc_backend set for rabbit' do + node.set['openstack']['mq']['service_type'] = 'rabbitmq' + expect(chef_run).to render_file(path).with_content('rpc_backend=rabbit') + end + + it 'has rpc_backend set for qpid' do + node.set['openstack']['mq']['service_type'] = 'qpid' + expect(chef_run).to render_file(path).with_content('rpc_backend=qpid') + end + + describe '[DEFAULT] section' do + it 'has admin token' do + r = line_regexp('admin_token = bootstrap-token') + expect(chef_run).to render_file(path).with_content(r) + end + + describe 'bind_interface is nil' do + it 'has bind host from endpoint' do + r = line_regexp('bind_host = 127.0.1.1') + expect(chef_run).to render_file(path).with_content(r) + end + end + + describe 'bind_interface is eth0' do + before do + node.set['openstack']['endpoints']['identity-bind']['bind_interface'] = 'eth0' + ::Chef::Recipe.any_instance.stub(:address_for) + .and_return('10.0.0.2') + end + + it 'has bind host from interface ip' do + r = line_regexp('bind_host = 10.0.0.2') + expect(chef_run).to render_file(path).with_content(r) + end + end + + describe 'port numbers' do + ['public_port', 'admin_port'].each do |x| + it "has #{x}" do + expect(chef_run).to render_file(path).with_content(/^#{x} = \d+$/) + end + end + end + + describe 'logging verbosity' do + ['verbose', 'debug'].each do |x| + it "has #{x} option" do + r = line_regexp("#{x} = False") + expect(chef_run).to render_file(path).with_content(r) + end + end + end + + describe 'syslog configuration' do + log_file = /^log_file = \/\w+/ + log_conf = /^log_config = \/\w+/ + + it 'renders log_file correctly' do + expect(chef_run).to render_file(path).with_content(log_file) + expect(chef_run).not_to render_file(path).with_content(log_conf) + end + + it 'renders log_config correctly' do + node.set['openstack']['identity']['syslog']['use'] = true + + expect(chef_run).to render_file(path).with_content(log_conf) + expect(chef_run).not_to render_file(path).with_content(log_file) + end + end + + it 'has correct endpoints' do + # values correspond to node attrs set in chef_run above + pub = line_regexp('public_endpoint = https://127.0.1.1:5000/') + adm = line_regexp('admin_endpoint = https://127.0.1.1:35357/') + + expect(chef_run).to render_file(path).with_content(pub) + expect(chef_run).to render_file(path).with_content(adm) + end + end + + describe '[memcache] section' do + it 'has no servers by default' do + # `Openstack#memcached_servers' is stubbed in spec_helper.rb to + # return an empty array, so we expect an empty `servers' list. + r = line_regexp('servers = ') + expect(chef_run).to render_file(path).with_content(r) + end + + it 'has servers when hostnames are configured' do + # Re-stub `Openstack#memcached_servers' here + hosts = ['host1:111', 'host2:222'] + regex = line_regexp("servers = #{hosts.join(',')}") + + ::Chef::Recipe.any_instance.stub(:memcached_servers).and_return(hosts) + expect(chef_run).to render_file(path).with_content(regex) + end + end + + describe '[sql] section' do + it 'has a connection' do + r = /^connection = \w+/ + expect(chef_run).to render_file(path).with_content(r) + end + end + + describe '[ldap] section' do + describe 'optional attributes' do + optional_attrs = %w{group_tree_dn group_filter user_filter + user_tree_dn user_enabled_emulation_dn + group_attribute_ignore role_attribute_ignore + role_tree_dn role_filter tenant_tree_dn + tenant_enabled_emulation_dn tenant_filter + tenant_attribute_ignore use_tls} + + it 'does not configure attributes' do + optional_attrs.each do |a| + enabled = /^#{Regexp.quote(a)} = \w+/ + disabled = /^# #{Regexp.quote(a)} =$/ + + expect(chef_run).to render_file(path).with_content(disabled) + expect(chef_run).not_to render_file(path).with_content(enabled) + end + end + + context 'ssl settings' do + context 'when use_tls disabled' do + it 'does not set tls_ options if use_tls is disabled' do + [/^tls_cacertfile = /, /^tls_cacertdir = /, /^tls_req_cert = /].each do |setting| + expect(chef_run).not_to render_file(path).with_content(setting) + end + end + end + + context 'when use_tls enabled' do + before do + node.set['openstack']['identity']['ldap']['use_tls'] = true + end + + context 'when cert paths are configured' do + it 'has a tls_cacertfile when configured' do + node.set['openstack']['identity']['ldap']['tls_cacertfile'] = 'tls_cacertfile_value' + expect(chef_run).to render_file(path).with_content(/^tls_cacertfile = tls_cacertfile_value$/) + expect(chef_run).not_to render_file(path).with_content(/^tls_cacertdir = /) + end + it 'has a tls_cacertdir when configured and tls_cacertfile unset' do + node.set['openstack']['identity']['ldap']['tls_cacertfile'] = nil + node.set['openstack']['identity']['ldap']['tls_cacertdir'] = 'tls_cacertdir_value' + expect(chef_run).to render_file(path).with_content(/^tls_cacertdir = tls_cacertdir_value$/) + expect(chef_run).not_to render_file(path).with_content(/^tls_cacertfile = /) + end + end + + context 'when tls_req_cert validation disabled' do + it 'has a tls_req_cert set to never' do + node.set['openstack']['identity']['ldap']['tls_req_cert'] = 'never' + expect(chef_run).to render_file(path).with_content(/^tls_req_cert = never$/) + end + end + end + end + + end + + it 'has required attributes' do + required_attrs = %w{alias_dereferencing allow_subtree_delete + dumb_member group_allow_create group_allow_delete + group_allow_update group_desc_attribute + group_domain_id_attribute group_id_attribute + group_member_attribute group_name_attribute + group_objectclass page_size query_scope + role_allow_create role_allow_delete + role_allow_update role_id_attribute + role_member_attribute role_name_attribute + role_objectclass suffix tenant_allow_create + tenant_allow_delete tenant_allow_update + tenant_desc_attribute tenant_domain_id_attribute + tenant_enabled_attribute tenant_enabled_emulation + tenant_id_attribute tenant_member_attribute + tenant_name_attribute tenant_objectclass url + use_dumb_member user user_allow_create + user_allow_delete user_allow_update + user_attribute_ignore user_domain_id_attribute + user_enabled_attribute user_enabled_default + user_enabled_emulation user_enabled_mask + user_id_attribute user_mail_attribute + user_name_attribute user_objectclass + user_pass_attribute} + + required_attrs.each do |a| + expect(chef_run).to render_file(path).with_content( + /^#{Regexp.quote(a)} = \w+/) + end + end + end + + describe '[identity] section' do + it 'configures driver' do + r = line_regexp('driver = keystone.identity.backends.sql.Identity') + expect(chef_run).to render_file(path).with_content(r) + end + end + + describe '[assignment] section' do + it 'configures driver' do + r = line_regexp('driver = keystone.assignment.backends.sql.Assignment') + expect(chef_run).to render_file(path).with_content(r) + end + end + + describe '[catalog] section' do + # use let() to access Helpers#line_regexp method + let(:templated) do + str = 'driver = keystone.catalog.backends.templated.TemplatedCatalog' + line_regexp(str) + end + let(:sql) do + line_regexp('driver = keystone.catalog.backends.sql.Catalog') + end + + it 'configures driver' do + expect(chef_run).to render_file(path).with_content(sql) + expect(chef_run).not_to render_file(path).with_content(templated) + end + + it 'configures driver with templated backend' do + node.set['openstack']['identity']['catalog']['backend'] = 'templated' + + expect(chef_run).to render_file(path).with_content(templated) + expect(chef_run).not_to render_file(path).with_content(sql) + end + end + + describe '[token] section' do + it 'configures driver' do + r = line_regexp('driver = keystone.token.backends.sql.Token') + expect(chef_run).to render_file(path).with_content(r) + end + + it 'sets token expiration time' do + r = line_regexp('expiration = 86400') + expect(chef_run).to render_file(path).with_content(r) + end + end + + describe '[policy] section' do + it 'configures driver' do + r = line_regexp('driver = keystone.policy.backends.sql.Policy') + expect(chef_run).to render_file(path).with_content(r) + end + end + + describe '[signing] section' do + opts = { + certfile: '/etc/keystone/ssl/certs/signing_cert.pem', + keyfile: '/etc/keystone/ssl/private/signing_key.pem', + ca_certs: '/etc/keystone/ssl/certs/ca.pem', + key_size: '2048', + valid_days: '3650', + ca_password: nil } - n.set["openstack"]["endpoints"]["identity-admin"] = { - "host" => "127.0.1.1", - "port" => "35357", - "scheme" => "https" - } - end - @chef_run.converge "openstack-identity::server" - end - it "runs logging recipe if node attributes say to" do - expect(@chef_run).to include_recipe "openstack-common::logging" - end - - it "doesn't run logging recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-identity::server" - - expect(chef_run).not_to include_recipe "openstack-common::logging" - end - - it "installs mysql python packages" do - expect(@chef_run).to install_package "python-mysqldb" - end - - it "installs postgresql python packages if explicitly told" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - node = chef_run.node - node.set["openstack"]["db"]["identity"]["db_type"] = "postgresql" - chef_run.converge "openstack-identity::server" - - expect(chef_run).to install_package "python-psycopg2" - end - - it "installs memcache python packages" do - expect(@chef_run).to install_package "python-memcache" - end - - it "installs keystone packages" do - expect(@chef_run).to upgrade_package "keystone" - end - - it "starts keystone on boot" do - expect(@chef_run).to set_service_to_start_on_boot "keystone" - end - - it "sleep on keystone service enable" do - expect(@chef_run.service("keystone")). - to notify "execute[Keystone: sleep]", :run - end - - describe "/etc/keystone" do - before do - @dir = @chef_run.directory "/etc/keystone" - end - - it "has proper owner" do - expect(@dir).to be_owned_by "keystone", "keystone" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" - end - end - - describe "/etc/keystone/ssl" do - before { @dir = "/etc/keystone/ssl" } - - describe "without pki" do - it "doesn't create" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.converge "openstack-identity::server" - - expect(chef_run).not_to create_directory @dir - end - end - - describe "with pki" do - before do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - @chef_run = ::ChefSpec::ChefRunner.new opts do |n| - n.set["openstack"]["auth"]["strategy"] = "pki" - end - @chef_run.converge "openstack-identity::server" - @directory = @chef_run.directory @dir - end - - it "creates" do - expect(@chef_run).to create_directory @directory.name - end - - it "has proper owner" do - expect(@directory).to be_owned_by "keystone", "keystone" - end - - it "has proper modes" do - expect(sprintf("%o", @directory.mode)).to eq "700" - end - end - end - - it "deletes keystone.db" do - expect(@chef_run).to delete_file "/var/lib/keystone/keystone.db" - end - - describe "pki setup" do - before { @cmd = "keystone-manage pki_setup" } - - describe "without pki" do - it "doesn't execute" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - - expect(chef_run).not_to execute_command(@cmd).with( - :user => "keystone" - ) - end - end - - describe "with pki" do - before do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - @chef_run = ::ChefSpec::ChefRunner.new opts do |n| - n.set["openstack"]["auth"]["strategy"] = "pki" + describe 'with pki' do + it 'configures cert options' do + opts.each do |key, val| + r = line_regexp("#{key} = #{val}") + expect(chef_run).to render_file(path).with_content(r) + end end end - it "executes" do - ::FileTest.should_receive(:exists?). - with("/etc/keystone/ssl/private/signing_key.pem"). - and_return(false) - @chef_run.converge "openstack-identity::server" - - expect(@chef_run).to execute_command(@cmd).with( - :user => "keystone" - ) - end - - it "doesn't execute when dir exists" do - ::FileTest.should_receive(:exists?). - with("/etc/keystone/ssl/private/signing_key.pem"). - and_return(true) - @chef_run.converge "openstack-identity::server" - - expect(@chef_run).not_to execute_command(@cmd).with( - :user => "keystone" - ) - end - end - end - - describe "keystone.conf" do - before do - @template = @chef_run.template "/etc/keystone/keystone.conf" - end - - it "has proper owner" do - expect(@template).to be_owned_by "keystone", "keystone" - end - - it "has proper modes" do - expect(sprintf("%o", @template.mode)).to eq "644" - end - - it "has bind host" do - expect(@chef_run).to create_file_with_content @template.name, - "bind_host = 127.0.1.1" - end - - it "has proper public and admin endpoint" do - expect(@chef_run).to create_file_with_content @template.name, - "public_endpoint = https://127.0.1.1:5000/" - expect(@chef_run).to create_file_with_content @template.name, - "admin_endpoint = https://127.0.1.1:35357/" - end - - it "notifies keystone restart" do - expect(@template).to notify "service[keystone]", :restart - end - end - - describe "default_catalog.templates" do - before { @file = "/etc/keystone/default_catalog.templates" } - - describe "without templated" do - it "doesn't create" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.converge "openstack-identity::server" - - expect(chef_run).not_to create_file @file - end - end - - describe "with templated" do - before do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - @chef_run = ::ChefSpec::ChefRunner.new opts do |n| - n.set["openstack"]["identity"]["catalog"]["backend"] = "templated" + describe 'without pki' do + before { node.set['openstack']['auth']['strategy'] = 'uuid' } + it 'does not configure cert options' do + opts.each do |key, val| + expect(chef_run).not_to render_file(path).with_content( + /^#{key} = /) + end end - @chef_run.converge "openstack-identity::server" - @template = @chef_run.template @file - end - - it "creates" do - expect(@chef_run).to create_file @file - end - - it "has proper owner" do - expect(@template).to be_owned_by "keystone", "keystone" - end - - it "has proper modes" do - expect(sprintf("%o", @template.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - - it "notifies keystone restart" do - expect(@template).to notify "service[keystone]", :restart end end end - describe "db_sync" do - before do - @cmd = "keystone-manage db_sync" - end + describe 'default_catalog.templates' do + let(:file) { '/etc/keystone/default_catalog.templates' } - it "runs migrations" do - expect(@chef_run).to execute_command @cmd - end - - it "doesn't run migrations" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new(opts) do |n| - n.set["openstack"]["identity"]["db"]["migrate"] = false + describe 'without templated backend' do + it 'does not create' do + expect(chef_run).not_to render_file(file) end - chef_run.converge "openstack-identity::server" + end - expect(chef_run).not_to execute_command @cmd + describe 'with templated backend' do + before do + node.set['openstack']['identity']['catalog']['backend'] = 'templated' + end + let(:template) { chef_run.template(file) } + + it 'creates' do + expect(chef_run).to render_file(file) + end + + it 'has proper owner' do + expect(template.owner).to eq('keystone') + expect(template.group).to eq('keystone') + end + + it 'has proper modes' do + expect(sprintf('%o', template.mode)).to eq('644') + end + + it 'notifies keystone restart' do + expect(template).to notify('service[keystone]').to(:restart) + end end end + + describe 'db_sync' do + let(:cmd) { 'keystone-manage db_sync' } + + it 'runs migrations' do + expect(chef_run).to run_execute(cmd).with( + user: 'keystone', + group: 'keystone' + ) + end + + it 'does not run migrations' do + node.set['openstack']['db']['identity']['migrate'] = false + expect(chef_run).not_to run_execute(cmd).with( + user: 'keystone', + group: 'keystone' + ) + end + end + + describe 'keystone-paste.ini' do + + it 'does not manage keystone-paste unless specified' do + expect(chef_run).not_to create_remote_file('/etc/keystone/keystone-paste.ini') + end + + describe 'keystone-paste remote specified' do + + before { node.set['openstack']['identity']['pastefile_url'] = 'http://server/mykeystone-paste.ini' } + let(:remote_paste) { chef_run.remote_file('/etc/keystone/keystone-paste.ini') } + + it 'does manage keystone-paste from remote file if specified' do + expect(chef_run).to create_remote_file('/etc/keystone/keystone-paste.ini').with( + user: 'keystone', + group: 'keystone', + mode: 00644) + expect(remote_paste).to notify('service[keystone]').to(:restart) + end + end + + end + end end diff --git a/chef/cookbooks/openstack-identity/spec/spec_helper.rb b/chef/cookbooks/openstack-identity/spec/spec_helper.rb index b90096a..d50025a 100644 --- a/chef/cookbooks/openstack-identity/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-identity/spec/spec_helper.rb @@ -1,27 +1,52 @@ -require "chefspec" +# Encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' -::LOG_LEVEL = :fatal -::OPENSUSE_OPTS = { - :platform => "opensuse", - :version => "12.3", - :log_level => ::LOG_LEVEL +ChefSpec::Coverage.start! { add_filter 'openstack-identity' } + +LOG_LEVEL = :fatal +SUSE_OPTS = { + platform: 'suse', + version: '11.03', + log_level: LOG_LEVEL } -::REDHAT_OPTS = { - :platform => "redhat", - :version => "6.3", - :log_level => ::LOG_LEVEL +REDHAT_OPTS = { + platform: 'redhat', + version: '6.3', + log_level: LOG_LEVEL } -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } -def identity_stubs - ::Chef::Recipe.any_instance.stub(:address_for). - with("lo"). - and_return "127.0.1.1" - ::Chef::Recipe.any_instance.stub(:memcached_servers).and_return [] - ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:secret).and_return String.new +# Helper methods +module Helpers + # Create an anchored regex to exactly match the entire line + # (name borrowed from grep --line-regexp) + # + # @param [String] str The whole line to match + # @return [Regexp] The anchored/escaped regular expression + def line_regexp(str) + /^#{Regexp.quote(str)}$/ + end +end + +shared_context 'identity_stubs' do + before do + ::Chef::Recipe.any_instance.stub(:memcached_servers).and_return [] + ::Chef::Recipe.any_instance.stub(:get_password) + .with('db', anything) + .and_return('') + ::Chef::Recipe.any_instance.stub(:get_password) + .with('user', anything) + .and_return('') + ::Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'user1') + .and_return('secret1') + ::Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_identity_bootstrap_token') + .and_return('bootstrap-token') + end end diff --git a/chef/cookbooks/openstack-identity/templates/default/keystone.conf.erb b/chef/cookbooks/openstack-identity/templates/default/keystone.conf.erb index 73f2ff9..d0268d9 100644 --- a/chef/cookbooks/openstack-identity/templates/default/keystone.conf.erb +++ b/chef/cookbooks/openstack-identity/templates/default/keystone.conf.erb @@ -1,10 +1,10 @@ <%= node["openstack"]["identity"]["custom_template_banner"] %> [DEFAULT] -public_port = <%= node["openstack"]["identity"]["service_port"] %> -admin_port = <%= node["openstack"]["identity"]["admin_port"] %> +public_port = <%= @public_port %> +admin_port = <%= @admin_port %> admin_token = <%= @bootstrap_token %> -bind_host = <%= @ip_address %> +bind_host = <%= @bind_address %> compute_port = 8774 verbose = <%= node["openstack"]["identity"]["verbose"] %> debug = <%= node["openstack"]["identity"]["debug"] %> @@ -16,7 +16,21 @@ log_file = /var/log/keystone/keystone.log public_endpoint = <%= @public_endpoint %> admin_endpoint = <%= @admin_endpoint %> -<% if @memcache_servers -%> +# oslo.messaging properties +control_exchange=<%= node["openstack"]["identity"]["control_exchange"] %> +rpc_thread_pool_size=<%= node["openstack"]["identity"]["rpc_thread_pool_size"] %> +rpc_conn_pool_size=<%= node["openstack"]["identity"]["rpc_conn_pool_size"] %> +rpc_response_timeout=<%= node["openstack"]["identity"]["rpc_response_timeout"] %> +rpc_backend=<%= node["openstack"]["identity"]["rpc_backend"] %> + +# Misc options +<% if node["openstack"]["identity"]["misc_keystone"] %> +<% node["openstack"]["identity"]["misc_keystone"].each do |m| %> +<%= m %> +<% end %> +<% end %> + +<% if node['openstack']['identity']['token']['backend'].eql?('memcache') -%> [memcache] servers = <%= @memcache_servers %> @@ -25,22 +39,162 @@ servers = <%= @memcache_servers %> connection = <%= @sql_connection %> idle_timeout = 200 min_pool_size = 5 -max_pool_size = 10 +max_pool_size = 100 pool_timeout = 200 [ldap] -#url = ldap://localhost -#tree_dn = dc=example,dc=com -#user_tree_dn = ou=Users,dc=example,dc=com -#role_tree_dn = ou=Roles,dc=example,dc=com -#tenant_tree_dn = ou=Groups,dc=example,dc=com -#user = dc=Manager,dc=example,dc=com -#password = freeipa4all -#suffix = cn=example,cn=com +url = <%= @ldap["url"] %> +user = <%= @ldap["user"] %> +<% if @ldap["password"] -%> +password = <%= @ldap["password"] %> +<% else -%> +# password = None +<% end -%> +<% if @ldap["use_tls"] -%> +# use_tls = True +<% if @ldap["tls_cacertfile"] -%> +tls_cacertfile = <%= @ldap["tls_cacertfile"] %> +<% elsif @ldap["tls_cacertdir"] -%> +tls_cacertdir = <%= @ldap["tls_cacertdir"] %> +<% else -%> +# tls_cacertfile = +# tls_cacertdir = +<% end -%> +<% if @ldap["tls_req_cert"] -%> +tls_req_cert = <%= @ldap["tls_req_cert"] %> +<% else -%> +# tls_req_cert = +<% end -%> +<% else -%> +# use_tls = +<% end -%> +suffix = <%= @ldap["suffix"] %> +use_dumb_member = <%= @ldap["use_dumb_member"] %> +allow_subtree_delete = <%= @ldap["allow_subtree_delete"] %> +# dumb_member = <%= @ldap["dumb_member"] %> +# page_size = <%= @ldap["page_size"] %> +# alias_dereferencing = <%= @ldap["alias_dereferencing"] %> +# query_scope = <%= @ldap["query_scope"] %> + +<% if @ldap["user_tree_dn"] -%> +user_tree_dn = <%= @ldap["user_tree_dn"] %> +<% else -%> +# user_tree_dn = +<% end -%> +<% if @ldap["user_filter"] -%> +user_filter = <%= @ldap["user_filter"] %> +<% else -%> +# user_filter = +<% end -%> +user_objectclass = <%= @ldap["user_objectclass"] %> +user_id_attribute = <%= @ldap["user_id_attribute"] %> +user_name_attribute = <%= @ldap["user_name_attribute"] %> +user_mail_attribute = <%= @ldap["user_mail_attribute"] %> +user_pass_attribute = <%= @ldap["user_pass_attribute"] %> +# user_enabled_attribute = <%= @ldap["user_enabled_attribute"] %> +user_domain_id_attribute = <%= @ldap["user_domain_id_attribute"] %> +user_enabled_mask = <%= @ldap["user_enabled_mask"] %> +user_enabled_default = <%= @ldap["user_enabled_default"] %> +user_attribute_ignore = <%= @ldap["user_attribute_ignore"] %> +user_allow_create = <%= @ldap["user_allow_create"] %> +user_allow_update = <%= @ldap["user_allow_update"] %> +user_allow_delete = <%= @ldap["user_allow_delete"] %> +user_enabled_emulation = <%= @ldap["user_enabled_emulation"] %> +<% if @ldap["user_enabled_emulation_dn"] -%> +user_enabled_emulation_dn = <%= @ldap["user_enabled_emulation_dn"] %> +<% else -%> +# user_enabled_emulation_dn = +<% end -%> + + + +<% if @ldap["tenant_tree_dn"] -%> +tenant_tree_dn = <%= @ldap["tenant_tree_dn"] %> +<% else -%> +# tenant_tree_dn = +<% end -%> +<% if @ldap["tenant_filter"] -%> +tenant_filter = <%= @ldap["tenant_filter"] %> +<% else -%> +# tenant_filter = +<% end -%> +#tenant_objectclass = <%= @ldap["tenant_objectclass"] %> +#tenant_id_attribute = <%= @ldap["tenant_id_attribute"] %> +#tenant_member_attribute = <%= @ldap["tenant_member_attribute"] %> +#tenant_name_attribute = <%= @ldap["tenant_name_attribute"] %> +#tenant_desc_attribute = <%= @ldap["tenant_desc_attribute"] %> +#tenant_enabled_attribute = <%= @ldap["tenant_enabled_attribute"] %> +#tenant_domain_id_attribute = <%= @ldap["tenant_domain_id_attribute"] %> +<% if @ldap["tenant_attribute_ignore"] -%> +tenant_attribute_ignore = <%= @ldap["tenant_attribute_ignore"] %> +<% else -%> +# tenant_attribute_ignore = +<% end -%> +#tenant_allow_create = <%= @ldap["tenant_allow_create"] %> +#tenant_allow_update = <%= @ldap["tenant_allow_update"] %> +#tenant_allow_delete = <%= @ldap["tenant_allow_delete"] %> +#tenant_enabled_emulation = <%= @ldap["tenant_enabled_emulation"] %> +<% if @ldap["tenant_enabled_emulation_dn"] -%> +tenant_enabled_emulation_dn = <%= @ldap["tenant_enabled_emulation_dn"] %> +<% else -%> +# tenant_enabled_emulation_dn = +<% end -%> + +<% if @ldap["role_tree_dn"] -%> +role_tree_dn = <%= @ldap["role_tree_dn"] %> +<% else -%> +# role_tree_dn = +<% end -%> +<% if @ldap["role_filter"] -%> +role_filter = <%= @ldap["role_filter"] %> +<% else -%> +# role_filter = +<% end -%> +#role_objectclass = <%= @ldap["role_objectclass"] %> +#role_id_attribute = <%= @ldap["role_id_attribute"] %> +#role_name_attribute = <%= @ldap["role_name_attribute"] %> +#role_member_attribute = <%= @ldap["role_member_attribute"] %> +<% if @ldap["role_attribute_ignore"] -%> +role_attribute_ignore = <%= @ldap["role_attribute_ignore"] %> +<% else -%> +# role_attribute_ignore = +<% end -%> +#role_allow_create = <%= @ldap["role_allow_create"] %> +#role_allow_update = <%= @ldap["role_allow_update"] %> +#role_allow_delete = <%= @ldap["role_allow_delete"] %> + +<% if @ldap["group_tree_dn"] -%> +group_tree_dn = <%= @ldap["group_tree_dn"] %> +<% else -%> +# group_tree_dn = +<% end -%> +<% if @ldap["group_filter"] -%> +group_filter = <%= @ldap["group_filter"] %> +<% else -%> +# group_filter = +<% end -%> +#group_objectclass = <%= @ldap["group_objectclass"] %> +#group_id_attribute = <%= @ldap["group_id_attribute"] %> +group_name_attribute = <%= @ldap["group_name_attribute"] %> +group_member_attribute = <%= @ldap["group_member_attribute"] %> +group_desc_attribute = <%= @ldap["group_desc_attribute"] %> +group_domain_id_attribute = <%= @ldap["group_domain_id_attribute"] %> +<% if @ldap["group_attribute_ignore"] -%> +group_attribute_ignore = <%= @ldap["group_attribute_ignore"] %> +<% else -%> +# group_attribute_ignore = +<% end -%> +group_allow_create = <%= @ldap["group_allow_create"] %> +group_allow_update = <%= @ldap["group_allow_update"] %> +group_allow_delete = <%= @ldap["group_allow_delete"] %> + [identity] driver = keystone.identity.backends.<%= node["openstack"]["identity"]["identity"]["backend"] %>.Identity +[assignment] +driver = keystone.assignment.backends.<%= node["openstack"]["identity"]["assignment"]["backend"] %>.Assignment + [catalog] <% if node["openstack"]["identity"]["catalog"]["backend"] == "templated" -%> # templated driver uses different class name :( @@ -52,12 +206,12 @@ template_file = /etc/keystone/default_catalog.templates [token] driver = keystone.token.backends.<%= node["openstack"]["identity"]["token"]["backend"] %>.Token - +provider = keystone.token.providers.<%= node["openstack"]["auth"]["strategy"] %>.Provider # Amount of time a token should remain valid (in seconds) -expiration = 86400 +expiration = <%= node["openstack"]["identity"]["token"]["expiration"] %> [policy] -driver = keystone.policy.backends.rules.Policy +driver = keystone.policy.backends.<%= node["openstack"]["identity"]["policy"]["backend"] %>.Policy [ec2] driver = keystone.contrib.ec2.backends.sql.Ec2 @@ -71,15 +225,12 @@ driver = keystone.contrib.ec2.backends.sql.Ec2 [signing] <% if node["openstack"]["auth"]["strategy"] == "pki" -%> -token_format = PKI certfile = <%= node["openstack"]["identity"]["signing"]["certfile"] %> keyfile = <%= node["openstack"]["identity"]["signing"]["keyfile"] %> ca_certs = <%= node["openstack"]["identity"]["signing"]["ca_certs"] %> key_size = <%= node["openstack"]["identity"]["signing"]["key_size"] %> valid_days = <%= node["openstack"]["identity"]["signing"]["valid_days"] %> ca_password = <%= node["openstack"]["identity"]["signing"]["ca_password"] %> -<% else -%> -token_format = UUID <% end -%> [auth] @@ -87,86 +238,6 @@ methods = password,token password = keystone.auth.plugins.password.Password token = keystone.auth.plugins.token.Token -[filter:debug] -paste.filter_factory = keystone.common.wsgi:Debug.factory - -[filter:token_auth] -paste.filter_factory = keystone.middleware:TokenAuthMiddleware.factory - -[filter:admin_token_auth] -paste.filter_factory = keystone.middleware:AdminTokenAuthMiddleware.factory - -[filter:xml_body] -paste.filter_factory = keystone.middleware:XmlBodyMiddleware.factory - -[filter:json_body] -paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory - -[filter:user_crud_extension] -paste.filter_factory = keystone.contrib.user_crud:CrudExtension.factory - -[filter:crud_extension] -paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory - -[filter:ec2_extension] -paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory - -[filter:s3_extension] -paste.filter_factory = keystone.contrib.s3:S3Extension.factory - -[filter:url_normalize] -paste.filter_factory = keystone.middleware:NormalizingFilter.factory - -[filter:sizelimit] -paste.filter_factory = keystone.middleware:RequestBodySizeLimiter.factory - -[filter:stats_monitoring] -paste.filter_factory = keystone.contrib.stats:StatsMiddleware.factory - -[filter:stats_reporting] -paste.filter_factory = keystone.contrib.stats:StatsExtension.factory - -[filter:access_log] -paste.filter_factory = keystone.contrib.access:AccessLogMiddleware.factory - -[app:public_service] -paste.app_factory = keystone.service:public_app_factory - -[app:service_v3] -paste.app_factory = keystone.service:v3_app_factory - -[app:admin_service] -paste.app_factory = keystone.service:admin_app_factory - -[pipeline:public_api] -pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug ec2_extension user_crud_extension public_service - -[pipeline:admin_api] -pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension crud_extension admin_service - -[pipeline:api_v3] -pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension service_v3 - -[app:public_version_service] -paste.app_factory = keystone.service:public_version_app_factory - -[app:admin_version_service] -paste.app_factory = keystone.service:admin_version_app_factory - -[pipeline:public_version_api] -pipeline = access_log sizelimit stats_monitoring url_normalize xml_body public_version_service - -[pipeline:admin_version_api] -pipeline = access_log sizelimit stats_monitoring url_normalize xml_body admin_version_service - -[composite:main] -use = egg:Paste#urlmap -/v2.0 = public_api -/v3 = api_v3 -/ = public_version_api - -[composite:admin] -use = egg:Paste#urlmap -/v2.0 = admin_api -/v3 = api_v3 -/ = admin_version_api +[paste_deploy] +# Name of the paste configuration file that defines the available pipelines +#config_file = keystone-paste.ini diff --git a/chef/cookbooks/openstack-image/.rubocop.yml b/chef/cookbooks/openstack-image/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-image/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-image/.tailor b/chef/cookbooks/openstack-image/.tailor deleted file mode 100644 index 99f0dcf..0000000 --- a/chef/cookbooks/openstack-image/.tailor +++ /dev/null @@ -1,25 +0,0 @@ -Tailor.config do |config| - config.formatters "text" - config.file_set '**/*.rb' do |style| - style.max_line_length 80, level: :off - style.allow_camel_case_methods false, level: :error - style.allow_hard_tabs false, level: :error - style.allow_screaming_snake_case_classes false, level: :error - style.allow_trailing_line_spaces false, level: :error - style.allow_invalid_ruby false, level: :warn - style.indentation_spaces 2, level: :error - style.max_code_lines_in_class 300, level: :error - style.max_code_lines_in_method 30, level: :error - style.spaces_after_comma 1, level: :error - style.spaces_after_lbrace 1, level: :error - style.spaces_after_lbracket 0, level: :error - style.spaces_after_lparen 0, level: :error - style.spaces_before_comma 0, level: :error - style.spaces_before_lbrace 1, level: :error - style.spaces_before_rbrace 1, level: :error - style.spaces_before_rbracket 0, level: :error - style.spaces_before_rparen 0, level: :error - style.spaces_in_empty_braces 0, level: :error - style.trailing_newlines 1, level: :error - end -end diff --git a/chef/cookbooks/openstack-image/Berksfile.lock b/chef/cookbooks/openstack-image/Berksfile.lock deleted file mode 100644 index 9b12c3c..0000000 --- a/chef/cookbooks/openstack-image/Berksfile.lock +++ /dev/null @@ -1,41 +0,0 @@ -{ - "sources": { - "openstack-image": { - "path": "." - }, - "openstack-identity": { - "locked_version": "7.0.0", - "git": "git://github.com/stackforge/cookbook-openstack-identity.git", - "ref": "b881af26095cfa869a6970067c49597a0ee63586" - }, - "openstack-common": { - "locked_version": "0.4.2", - "git": "git://github.com/stackforge/cookbook-openstack-common.git", - "ref": "6354e0280ac91b86f244923287380d66ff2f06c6" - }, - "apt": { - "locked_version": "2.0.0" - }, - "database": { - "locked_version": "1.4.0" - }, - "mysql": { - "locked_version": "3.0.2" - }, - "openssl": { - "locked_version": "1.0.2" - }, - "build-essential": { - "locked_version": "1.4.0" - }, - "postgresql": { - "locked_version": "3.0.2" - }, - "aws": { - "locked_version": "0.101.2" - }, - "xfs": { - "locked_version": "1.1.0" - } - } -} diff --git a/chef/cookbooks/openstack-image/CHANGELOG.md b/chef/cookbooks/openstack-image/CHANGELOG.md new file mode 100644 index 0000000..a3f6069 --- /dev/null +++ b/chef/cookbooks/openstack-image/CHANGELOG.md @@ -0,0 +1,76 @@ +# CHANGELOG for cookbook-openstack-image + +This file is used to list changes made in each version of cookbook-openstack-image. + +## 9.1.1 +### Bug +* Fix data bag item id issue in recipes/api.rb + +## 9.1.0 +### Blue print +* Get VMware vCenter password from databag + +## 9.0.3 +* Fix package reference, need keystone client not keystone + +## 9.0.2 +* Fix package action to allow updates + +## 9.0.1 +* Remove policy template + +## 9.0.0 +* Upgrade to Icehouse + +## 8.2.1 +### Bug +* Fix the DB2 ODBC driver issue + +## 8.2.0 +### Blue print +* Use the common auth uri tranformation function and add the auth version to configuration files. + +## 8.1.0 +* Add client recipe + +## 8.0.0 +* Updating to Havana Release + +## 7.1.1 +### Bug +* Relax the dependency on openstack-identity to the 7.x series + +## 7.1.0 +### Blue print +* Add qpid support to glance. Default is rabbit + +## 7.0.6 +### Bug +* Do not delete the sqlite database layed down by the glance packages when node.openstack.db.image.db_type is set to sqlite. + +## 7.0.5: +* Allow swift packages to be optionally installed. + +## 7.0.4: +### Bug +* Fixed _python_packages issue when setting node.openstack.db.image.db_type to sqlite. +* Added `converges when configured to use sqlite db backend` test case for this scenario. + +## 7.0.3: +* Use the image-api endpoint within the image_image LWRP to enable non-localhost + uploads. +* Use non-deprecated parameters within the image_image LWRP use of the glance CLI. + +## 7.0.2: +* Added cron redirection attribute. + +## 7.0.1: +* Corrected inconsistent keystone middleware auth_token for glance-registry.conf.erb. + +## 7.0.0: +* Initial release of cookbook-openstack-image. + +- - - +Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. + +The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. diff --git a/chef/cookbooks/openstack-image/Gemfile b/chef/cookbooks/openstack-image/Gemfile index 04ef97e..58934c6 100644 --- a/chef/cookbooks/openstack-image/Gemfile +++ b/chef/cookbooks/openstack-image/Gemfile @@ -1,9 +1,10 @@ -source "https://rubygems.org" +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 2.0.3" -gem "chefspec", "~> 1.3.0" -gem "foodcritic" -gem "strainer" -gem "tailor" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' +gem 'fauxhai', '>= 2.1.0' diff --git a/chef/cookbooks/openstack-image/Gemfile.lock b/chef/cookbooks/openstack-image/Gemfile.lock index dcf7d6c..bbe41ab 100644 --- a/chef/cookbooks/openstack-image/Gemfile.lock +++ b/chef/cookbooks/openstack-image/Gemfile.lock @@ -1,30 +1,36 @@ GEM remote: https://rubygems.org/ specs: - activesupport (3.2.14) + activesupport (3.2.17) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.3.5) - akami (1.2.0) + addressable (2.3.6) + akami (1.2.1) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - berkshelf (2.0.8) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) activesupport (~> 3.2.0) addressable (~> 2.3.4) buff-shell_out (~> 0.1) - celluloid (>= 0.14.0) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) minitar (~> 0.5.4) rbzip2 (~> 0.2.0) retryable (~> 1.3.3) - ridley (~> 1.2.1) + ridley (~> 1.5.0) solve (>= 0.5.0) thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) buff-ruby_engine (0.1.0) - buff-shell_out (0.1.0) + buff-shell_out (0.1.1) buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) @@ -32,111 +38,127 @@ GEM celluloid-io (0.14.1) celluloid (>= 0.14.1) nio4r (>= 0.4.5) - chef (11.4.4) - erubis - highline (>= 1.6.9) - json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) net-ssh (~> 2.6) - net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (1.3.1) - chef (>= 10.0) - erubis - fauxhai (>= 0.1.1, < 2.0) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.9.0) - builder (>= 2.1.2) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.8) + faraday (0.8.9) multipart-post (~> 1.2.0) - fauxhai (1.1.1) - httparty + fauxhai (2.1.0) net-ssh ohai - ffi (1.9.0) - foodcritic (2.2.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.1.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.4) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - mime-types (1.23) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.5) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.2.0) - multi_json (1.7.7) - multi_xml (0.5.4) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) multipart-post (1.2.0) - net-http-persistent (2.9) - net-ssh (2.6.8) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - net-ssh-multi (1.1) - net-ssh (>= 2.1.4) - net-ssh-gateway (>= 0.99.0) - nio4r (0.4.6) - nokogiri (1.5.10) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.18.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) + rainbow (2.0.0) + rake (10.2.2) rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (1.2.5) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) buff-extensions (~> 0.3) + buff-ignore (~> 1.1) buff-shell_out (~> 0.1) celluloid (~> 0.14.0) celluloid-io (~> 0.14.0) @@ -147,6 +169,7 @@ GEM mixlib-authentication (>= 1.3.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) varia_model (~> 0.1) @@ -155,10 +178,15 @@ GEM rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - rspec-core (2.14.4) - rspec-expectations (2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.2) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) savon (0.9.5) akami (~> 1.0) @@ -168,34 +196,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.8.0) - strainer (3.1.1) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - text-table (1.2.3) thor (0.18.1) - timers (1.1.0) - tins (0.8.3) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) uuidtools (2.1.4) - varia_model (0.1.1) + varia_model (0.3.2) buff-extensions (~> 0.2) hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -205,10 +228,11 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 2.0.3) - chef (~> 11.4.4) - chefspec (~> 1.3.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + fauxhai (>= 2.1.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - tailor diff --git a/chef/cookbooks/openstack-image/README.md b/chef/cookbooks/openstack-image/README.md index 92b7c23..bedbce4 100644 --- a/chef/cookbooks/openstack-image/README.md +++ b/chef/cookbooks/openstack-image/README.md @@ -23,16 +23,24 @@ Usage api ------ -- Installs the image-api server +- Installs the glance-api server + +client +---- +- Install the glance client packages registry -------- -- Installs the image-registry server +- Installs the glance-registry server keystone-registration --------------------- - Registers the API endpoint and glance service Keystone user +image-upload +------------ +- Upload image to glance. If you want to upload image during openstack installation, you need to add this recipe or the os-image role to the run list in a certain role and ensure before this recipe or the os-image role glance api and glance registry recipes have been executed. + The Glance cookbook currently supports file, swift, and Rackspace Cloud Files (swift API compliant) backing stores. NOTE: changing the storage location from cloudfiles to swift (and vice versa) requires that you manually export and import your stored images. To enable these features set the following in the default attributes section in your environment: @@ -48,8 +56,7 @@ Files }, "upload_images": [ "cirros" - ], - "image_upload": true + ] } } ``` @@ -65,8 +72,7 @@ Swift }, "upload_images": [ "cirros" - ], - "image_upload": true + ] } } ``` @@ -104,33 +110,86 @@ Attributes for the Image service are in the ['openstack']['image'] namespace. * `openstack['image']['service_tenant_name']` - Tenant name used by glance when interacting with keystone - used in the API and registry paste.ini files * `openstack['image']['service_user']` - User name used by glance when interacting with keystone - used in the API and registry paste.ini files * `openstack['image']['service_role']` - User role used by glance when interacting with keystone - used in the API and registry paste.ini files +* `openstack['image']['notification_driver']` - Notification driver, default noop. +* `openstack['image']['api']['workers']` - Set the number of glance api workers. +* `openstack['image']['api']['show_image_direct_url']` - Allow glance to return URL referencing where data is stored on the backend. Default 'False' * `openstack['image']['api']['auth']['cache_dir']` - Defaults to `/var/cache/glance/api`. Directory where `auth_token` middleware writes certificates for glance-api * `openstack['image']['registry']['auth']['cache_dir']` - Defaults to `/var/cache/glance/registry`. Directory where `auth_token` middleware writes certificates for glance-registry -* `openstack['image']['image_upload']` - Toggles whether to automatically upload images in the `openstack['image']['upload_images']` array * `openstack['image']['upload_images']` - Default list of images to upload to the glance repository as part of the install * `openstack['image']['upload_image']['']` - URL location of the `` image. There can be multiple instances of this line to define multiple imagess (eg natty, maverick, fedora17 etc) --- example `openstack['image']['upload_image']['natty']` - "http://c250663.r63.cf1.rackcdn.com/ubuntu-11.04-server-uec-amd64-multinic.tar.gz" -* `openstack['image']['api']['default_store']` - Toggles the backend storage type. Currently supported is "file" and "swift" +* `openstack['image']['api']['default_store']` - Toggles the backend storage type. Currently supported is "file", "swift" and "rbd". * `openstack['image']['api']['swift']['store_container']` - Set the container used by glance to store images and snapshots. Defaults to "glance" * `openstack['image']['api']['swift']['store_large_object_size']` - Set the size at which glance starts to chunnk files. Defaults to "200" MB * `openstack['image']['api']['swift']['store_large_object_chunk_size']` - Set the chunk size for glance. Defaults to "200" MB +* `openstack['image']['api']['swift']['enable_snet']` - Set whether to use ServiceNET to communicate with the Swift Storage servers. (Rackspace specific option) +* `openstack['image']['api']['swift']['store_region']` - The region of the swift endpoint to be used for single tenant. This setting is only necessary if the tenant has multiple swift endpoints. * `openstack['image']['api']['rbd']['rbd_store_ceph_conf']` - Default location of ceph.conf * `openstack['image']['api']['rbd']['rbd_store_user']` - User for connecting to ceph store * `openstack['image']['api']['rbd']['rbd_store_pool']` - RADOS pool for images * `openstack['image']['api']['rbd']['rbd_store_chunk_size']` - Size in MB of chunks for RADOS Store, should be a power of 2 +* `openstack['image']['api']['rbd']['key_name']` - The data bag item name used for the Cephx key of the rbd_store_user. +* `openstack['image']['cron']['redirection']` - Redirection of cron output +TODO: Add DB2 support on other platforms +* `openstack['image']['platform']['db2_python_packages']` - Array of DB2 python packages, only available on redhat platform + +VMWare attributes +----------------- + +* `openstack['image']['api']['vmware']['secret_name']` - VMware databag secret name +* `openstack['image']['api']['vmware']['vmware_server_host']` - ESX/ESXi or vCenter Server target system. e.g. 127.0.0.1, 127.0.0.1:443, www.vmware-infra.com +* `openstack['image']['api']['vmware']['vmware_server_username']` - Server username (string value) +* `openstack['image']['api']['vmware']['vmware_datacenter_path']` - Inventory path to a datacenter (string value) +* `openstack['image']['api']['vmware']['vmware_datastore_name']` - Datastore associated with the datacenter (string value) +* `openstack['image']['api']['vmware']['vmware_api_retry_count']` - The number of times we retry on failures (integer value) +* `openstack['image']['api']['vmware']['vmware_task_poll_interval']` - The interval used for polling remote tasks invoked on VMware ESX/VC server in seconds (integer value) +* `openstack['image']['api']['vmware']['vmware_store_image_dir']` - Absolute path of the folder containing the images in the datastore (string value) +* `openstack['image']['api']['vmware']['vmware_api_insecure']` - Allow to perform insecure SSL requests to the target system (boolean value) + +MQ attributes +------------- +* `openstack['image']['mq']['service_type']` - Select qpid or rabbitmq. default rabbitmq +* `openstack['image']['mq']['qpid']['host']` - The qpid host to use +* `openstack['image']['mq']['qpid']['port']` - The qpid port to use +* `openstack['image']['mq']['qpid']['qpid_hosts']` - Qpid hosts. TODO. use only when ha is specified. +* `openstack['image']['mq']['qpid']['username']` - Username for qpid connection +* `openstack['image']['mq']['qpid']['password']` - Password for qpid connection +* `openstack['image']['mq']['qpid']['sasl_mechanisms']` - Space separated list of SASL mechanisms to use for auth +* `openstack['image']['mq']['qpid']['reconnect_timeout']` - The number of seconds to wait before deciding that a reconnect attempt has failed. +* `openstack['image']['mq']['qpid']['reconnect_limit']` - The limit for the number of times to reconnect before considering the connection to be failed. +* `openstack['image']['mq']['qpid']['reconnect_interval_min']` - Minimum number of seconds between connection attempts. +* `openstack['image']['mq']['qpid']['reconnect_interval_max']` - Maximum number of seconds between connection attempts. +* `openstack['image']['mq']['qpid']['reconnect_interval']` - Equivalent to setting qpid_reconnect_interval_min and qpid_reconnect_interval_max to the same value. +* `openstack['image']['mq']['qpid']['heartbeat']` - Seconds between heartbeat messages sent to ensure that the connection is still alive. +* `openstack['image']['mq']['qpid']['protocol']` - Protocol to use. Default tcp. +* `openstack['image']['mq']['qpid']['tcp_nodelay']` - Disable the Nagle algorithm. default disabled. + +The following attributes are defined in attributes/default.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack['endpoints']['image-api-bind']['host']` - The IP address to bind the api service to +* `openstack['endpoints']['image-api-bind']['port']` - The port to bind the api service to +* `openstack['endpoints']['image-api-bind']['bind_interface']` - The interface name to bind the api service to + +* `openstack['endpoints']['image-registry-bind']['host']` - The IP address to bind the registry service to +* `openstack['endpoints']['image-registry-bind']['port']` - The port to bind the registry service to +* `openstack['endpoints']['image-registry-bind']['bind_interface']` - The interface name to bind the registry service to + +If the value of the 'bind_interface' attribute is non-nil, then the image service will be bound to the first IP address on that interface. If the value of the 'bind_interface' attribute is nil, then the image service will be bound to the IP address specified in the host attribute. Testing ===== -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. -Tests are defined in Strainerfile. +Berkshelf +===== -To run tests: - - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -147,11 +206,17 @@ Author:: Jay Pipes () Author:: John Dewey () Author:: Craig Tracey () Author:: Sean Gallagher () +Author:: Mark Vanderwiel () +Author:: Salman Baset () +Author:: Chen Zhiwei (zhiwchen@cn.ibm.com) +Author:: Eric Zhou (zyouzhou@cn.ibm.com) +Author:: Jian Hua Geng (gengjh@cn.ibm.com) Copyright 2012, Rackspace US, Inc. Copyright 2012-2013, Opscode, Inc. Copyright 2012-2013, AT&T Services, Inc. Copyright 2013, Craig Tracey +Copyright 2013-2014, IBM Corp. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/openstack-image/Strainerfile b/chef/cookbooks/openstack-image/Strainerfile index 7e292b4..44e3e14 100644 --- a/chef/cookbooks/openstack-image/Strainerfile +++ b/chef/cookbooks/openstack-image/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-image/TESTING.md b/chef/cookbooks/openstack-image/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-image/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-image/attributes/default.rb b/chef/cookbooks/openstack-image/attributes/default.rb index 231e0f7..63125c4 100644 --- a/chef/cookbooks/openstack-image/attributes/default.rb +++ b/chef/cookbooks/openstack-image/attributes/default.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-image # Attributes:: default @@ -21,125 +22,152 @@ # Set to some text value if you want templated config files # to contain a custom banner at the top of the written file -default["openstack"]["image"]["custom_template_banner"] = " +default['openstack']['image']['custom_template_banner'] = ' # This file autogenerated by Chef # Do not edit, changes will be overwritten -" +' -default["openstack"]["image"]["verbose"] = "False" -default["openstack"]["image"]["debug"] = "False" +default['openstack']['image']['verbose'] = 'False' +default['openstack']['image']['debug'] = 'False' # This is the name of the Chef role that will install the Keystone Service API -default["openstack"]["image"]["identity_service_chef_role"] = "os-identity" +default['openstack']['image']['identity_service_chef_role'] = 'os-identity' # Gets set in the Image Endpoint when registering with Keystone -default["openstack"]["image"]["region"] = "RegionOne" +default['openstack']['image']['region'] = node['openstack']['region'] # The name of the Chef role that knows about the message queue server # that Glance uses -default["openstack"]["image"]["rabbit_server_chef_role"] = "os-ops-messaging" +default['openstack']['image']['rabbit_server_chef_role'] = 'os-ops-messaging' -default["openstack"]["image"]["db"]["username"] = "glance" -# Execute database migrations. There are cases where migrations should not be -# executed. For example when upgrading a zone, and the image database is -# replicated across many zones. -default["openstack"]["image"]["db"]["migrate"] = true +default['openstack']['image']['service_tenant_name'] = 'service' +default['openstack']['image']['service_user'] = 'glance' +default['openstack']['image']['service_role'] = 'admin' -# This user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# user_password routine. You are expected to create -# the user, pass, vhost in a wrapper rabbitmq cookbook. -default["openstack"]["image"]["rabbit"]["username"] = "guest" -default["openstack"]["image"]["rabbit"]["vhost"] = "/" -default["openstack"]["image"]["rabbit"]["port"] = 5672 -default["openstack"]["image"]["rabbit"]["host"] = "127.0.0.1" +default['openstack']['image']['notification_driver'] = 'noop' -default["openstack"]["image"]["service_tenant_name"] = "service" -default["openstack"]["image"]["service_user"] = "glance" -default["openstack"]["image"]["service_role"] = "admin" +# Set the number of api workers +default['openstack']['image']['api']['workers'] = [8, node['cpu']['total'].to_i].min -# Setting this to v2.0. See discussion on -# https://bugs.launchpad.net/openstack-chef/+bug/1207504 -default["openstack"]["image"]["api"]["auth"]["version"] = "v2.0" +# Return the URL that references where the data is stored on the backend. +default['openstack']['image']['api']['show_image_direct_url'] = 'False' + +default['openstack']['image']['api']['auth']['version'] = node['openstack']['api']['auth']['version'] +default['openstack']['image']['registry']['auth']['version'] = node['openstack']['api']['auth']['version'] # Keystone PKI signing directories # XXX keystoneclient wants these dirs to exist even if it doesn't use them -default["openstack"]["image"]["api"]["auth"]["cache_dir"] = "/var/cache/glance/api" -default["openstack"]["image"]["registry"]["auth"]["cache_dir"] = "/var/cache/glance/registry" +default['openstack']['image']['api']['auth']['cache_dir'] = '/var/cache/glance/api' +default['openstack']['image']['registry']['auth']['cache_dir'] = '/var/cache/glance/registry' -default["openstack"]["image"]["api"]["default_store"] = "file" -# If set, glance API service will bind to the address on this interface, -# otherwise it will bind to the API endpoint's host. -default["openstack"]["image"]["api"]["bind_interface"] = nil -default["openstack"]["image"]["api"]["swift"]["container"] = "glance" -default["openstack"]["image"]["api"]["swift"]["large_object_size"] = "200" -default["openstack"]["image"]["api"]["swift"]["large_object_chunk_size"] = "200" -default["openstack"]["image"]["api"]["cache"]["image_cache_max_size"] = "10737418240" +# Whether to use any of the default caching pipelines from the paste configuration file +default['openstack']['image']['api']['caching'] = false +default['openstack']['image']['api']['cache_management'] = false + +default['openstack']['image']['api']['default_store'] = 'file' + +default['openstack']['image']['filesystem_store_datadir'] = '/var/lib/glance/images' + +default['openstack']['image']['api']['swift']['container'] = 'glance' +default['openstack']['image']['api']['swift']['large_object_size'] = '200' +default['openstack']['image']['api']['swift']['large_object_chunk_size'] = '200' +default['openstack']['image']['api']['swift']['enable_snet'] = 'False' +default['openstack']['image']['api']['swift']['store_region'] = nil +default['openstack']['image']['api']['cache']['image_cache_max_size'] = '10737418240' + +# Directory for the Image Cache +default['openstack']['image']['cache']['dir'] = '/var/lib/glance/image-cache/' +# Number of seconds until an incomplete image is considered stalled an +# eligible for reaping +default['openstack']['image']['cache']['stall_time'] = 86400 +# Number of seconds to leave invalid images around before they are eligible to be reaped +default['openstack']['image']['cache']['grace_period'] = 3600 # Ceph Options -default["openstack"]["image"]["api"]["rbd"]["rbd_store_ceph_conf"] = "/etc/ceph/ceph.conf" -default["openstack"]["image"]["api"]["rbd"]["rbd_store_user"] = "glance" -default["openstack"]["image"]["api"]["rbd"]["rbd_store_pool"] = "images" -default["openstack"]["image"]["api"]["rbd"]["rbd_store_chunk_size"] = "8" - -# If set, glance registry service will bind to the address on this interface, -# otherwise it will bind to the API endpoint's host. -default["openstack"]["image"]["registry"]["bind_interface"] = nil +default['openstack']['image']['api']['rbd']['rbd_store_ceph_conf'] = '/etc/ceph/ceph.conf' +default['openstack']['image']['api']['rbd']['rbd_store_user'] = 'glance' +default['openstack']['image']['api']['rbd']['rbd_store_pool'] = 'images' +default['openstack']['image']['api']['rbd']['rbd_store_chunk_size'] = '8' +# The name used for the data bag item containing the Cephx user's password +default['openstack']['image']['api']['rbd']['key_name'] = 'rbd-image' # API to use for accessing data. Default value points to sqlalchemy # package. -default["openstack"]["image"]["data_api"] = "glance.db.sqlalchemy.api" +default['openstack']['image']['data_api'] = 'glance.db.sqlalchemy.api' # Default Image Locations -default["openstack"]["image"]["image_upload"] = true -default["openstack"]["image"]["upload_images"] = [ "cirros-0.3.1-x86_64" ] -default["openstack"]["image"]["upload_image"]["precise"] = "http://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img" -default["openstack"]["image"]["upload_image"]["oneiric"] = "http://cloud-images.ubuntu.com/oneiric/current/oneiric-server-cloudimg-amd64-disk1.img" -default["openstack"]["image"]["upload_image"]["natty"] = "http://cloud-images.ubuntu.com/natty/current/natty-server-cloudimg-amd64-disk1.img" -default["openstack"]["image"]["upload_image"]["cirros-0.3.1-x86_64"] = "http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-disk.img" +default['openstack']['image']['upload_images'] = ['cirros'] +default['openstack']['image']['upload_image']['precise'] = 'http://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img' +default['openstack']['image']['upload_image']['oneiric'] = 'http://cloud-images.ubuntu.com/oneiric/current/oneiric-server-cloudimg-amd64-disk1.img' +default['openstack']['image']['upload_image']['natty'] = 'http://cloud-images.ubuntu.com/natty/current/natty-server-cloudimg-amd64-disk1.img' +default['openstack']['image']['upload_image']['cirros'] = 'http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-disk.img' # more images available at https://github.com/rackerjoe/oz-image-build -default["openstack"]["image"]["upload_image"]["centos"] = "http://c250663.r63.cf1.rackcdn.com/centos60_x86_64.qcow2" +default['openstack']['image']['upload_image']['centos'] = 'http://c250663.r63.cf1.rackcdn.com/centos60_x86_64.qcow2' # logging attribute -default["openstack"]["image"]["syslog"]["use"] = false -default["openstack"]["image"]["syslog"]["facility"] = "LOG_LOCAL2" -default["openstack"]["image"]["syslog"]["config_facility"] = "local2" +default['openstack']['image']['syslog']['use'] = false +default['openstack']['image']['syslog']['facility'] = 'LOG_LOCAL2' +default['openstack']['image']['syslog']['config_facility'] = 'local2' + +# vmware attributes +default['openstack']['image']['api']['vmware']['secret_name'] = 'openstack_vmware_secret_name' +default['openstack']['image']['api']['vmware']['vmware_server_host'] = '' +default['openstack']['image']['api']['vmware']['vmware_server_username'] = '' +default['openstack']['image']['api']['vmware']['vmware_datacenter_path'] = '' +default['openstack']['image']['api']['vmware']['vmware_datastore_name'] = '' +default['openstack']['image']['api']['vmware']['vmware_api_retry_count'] = 10 +default['openstack']['image']['api']['vmware']['vmware_task_poll_interval'] = 5 +default['openstack']['image']['api']['vmware']['vmware_store_image_dir'] = '/openstack_glance' +default['openstack']['image']['api']['vmware']['vmware_api_insecure'] = false + +# cron output redirection +default['openstack']['image']['cron']['redirection'] = '> /dev/null 2>&1' # platform-specific settings -case platform -when "fedora", "redhat", "centos" # :pragma-foodcritic: ~FC024 - won't fix this - default["openstack"]["image"]["user"] = "glance" - default["openstack"]["image"]["group"] = "glance" - default["openstack"]["image"]["platform"] = { - "postgresql_python_packages" => [ "python-psycopg2" ], - "mysql_python_packages" => [ "MySQL-python" ], - "image_packages" => [ "openstack-glance", "openstack-swift", "cronie" ], - "image_api_service" => "openstack-glance-api", - "image_registry_service" => "openstack-glance-registry", - "image_api_process_name" => "glance-api", - "package_overrides" => "" +case platform_family +when 'fedora', 'rhel' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['image']['user'] = 'glance' + default['openstack']['image']['group'] = 'glance' + default['openstack']['image']['platform'] = { + 'postgresql_python_packages' => ['python-psycopg2'], + 'mysql_python_packages' => ['MySQL-python'], + 'db2_python_packages' => ['python-ibm-db', 'python-ibm-db-sa'], + 'image_packages' => %w{openstack-glance cronie python-glanceclient}, + 'image_client_packages' => ['python-glanceclient'], + 'ceph_packages' => ['python-ceph'], + 'swift_packages' => ['openstack-swift'], + 'image_api_service' => 'openstack-glance-api', + 'image_registry_service' => 'openstack-glance-registry', + 'image_api_process_name' => 'glance-api', + 'package_overrides' => '' } -when "suse" - default["openstack"]["image"]["user"] = "openstack-glance" - default["openstack"]["image"]["group"] = "openstack-glance" - default["openstack"]["image"]["platform"] = { - "postgresql_python_packages" => [ "python-psycopg2" ], - "mysql_python_packages" => [ "python-mysql" ], - "image_packages" => [ "openstack-glance", "openstack-swift", "python-glanceclient" ], - "image_api_service" => "openstack-glance-api", - "image_registry_service" => "openstack-glance-registry", - "image_api_process_name" => "glance-api", - "package_overrides" => "" +when 'suse' + default['openstack']['image']['user'] = 'openstack-glance' + default['openstack']['image']['group'] = 'openstack-glance' + default['openstack']['image']['platform'] = { + 'postgresql_python_packages' => ['python-psycopg2'], + 'mysql_python_packages' => ['python-mysql'], + 'image_packages' => ['openstack-glance', 'python-glanceclient'], + 'image_client_packages' => ['python-glanceclient'], + 'ceph_packages' => [], + 'swift_packages' => ['openstack-swift'], + 'image_api_service' => 'openstack-glance-api', + 'image_registry_service' => 'openstack-glance-registry', + 'image_api_process_name' => 'glance-api', + 'package_overrides' => '' } -when "ubuntu" - default["openstack"]["image"]["user"] = "glance" - default["openstack"]["image"]["group"] = "glance" - default["openstack"]["image"]["platform"] = { - "postgresql_python_packages" => [ "python-psycopg2" ], - "mysql_python_packages" => [ "python-mysqldb" ], - "image_packages" => [ "glance", "python-swift" ], - "image_api_service" => "glance-api", - "image_registry_service" => "glance-registry", - "image_registry_process_name" => "glance-registry", - "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" +when 'debian' + default['openstack']['image']['user'] = 'glance' + default['openstack']['image']['group'] = 'glance' + default['openstack']['image']['platform'] = { + 'postgresql_python_packages' => ['python-psycopg2'], + 'mysql_python_packages' => ['python-mysqldb'], + 'image_packages' => ['glance'], + 'image_client_packages' => ['python-glanceclient'], + 'ceph_packages' => ['python-ceph'], + 'swift_packages' => ['python-swift'], + 'image_api_service' => 'glance-api', + 'image_registry_service' => 'glance-registry', + 'image_registry_process_name' => 'glance-registry', + 'package_overrides' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" } end diff --git a/chef/cookbooks/openstack-image/metadata.rb b/chef/cookbooks/openstack-image/metadata.rb index c8f841b..eda6d7e 100644 --- a/chef/cookbooks/openstack-image/metadata.rb +++ b/chef/cookbooks/openstack-image/metadata.rb @@ -1,16 +1,19 @@ -name "openstack-image" -maintainer "Opscode, Inc." -license "Apache 2.0" -description "Installs and configures the Glance Image Registry and Delivery Service" +name 'openstack-image' +maintainer 'Opscode, Inc.' +license 'Apache 2.0' +description 'Installs and configures the Glance Image Registry and Delivery Service' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "7.0.0" -recipe "openstack-image::api", "Installs packages required for a glance api server" -recipe "openstack-image::registry", "Installs packages required for a glance registry server" -recipe "openstack-image::identity_registration", "Registers Glance endpoints and service with Keystone" +version '9.1.1' +recipe 'openstack-image::api', 'Installs packages required for a glance api server' +recipe 'openstack-image::client', 'Install packages required for glance client' +recipe 'openstack-image::registry', 'Installs packages required for a glance registry server' +recipe 'openstack-image::identity_registration', 'Registers Glance endpoints and service with Keystone' +recipe 'openstack-image::image_upload', 'Upload image using glance image-create command' %w{ ubuntu fedora redhat centos suse }.each do |os| supports os end -depends "openstack-common", "~> 0.4.0" -depends "openstack-identity", "~> 7.0.0" +depends 'openstack-common', '~> 9.0' +depends 'openstack-identity', '~> 9.0' +depends 'ceph', '>= 0.2.1' diff --git a/chef/cookbooks/openstack-image/providers/image.rb b/chef/cookbooks/openstack-image/providers/image.rb index 14eec3d..69f319d 100644 --- a/chef/cookbooks/openstack-image/providers/image.rb +++ b/chef/cookbooks/openstack-image/providers/image.rb @@ -1,8 +1,10 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-image # Provider:: image # # Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +19,8 @@ # limitations under the License. # +include ::Openstack + action :upload do @user = new_resource.identity_user @pass = new_resource.identity_pass @@ -25,45 +29,47 @@ action :upload do name = new_resource.image_name url = new_resource.image_url + + ep = endpoint 'image-api' + api = ep.to_s.gsub(ep.path, '') # remove trailing /v2 + type = new_resource.image_type - if type == "unknown" - type = _determine_type(url) - end - _upload_image(type, name, url) + type = _determine_type(url) if type == 'unknown' + _upload_image(type, name, api, url) new_resource.updated_by_last_action(true) end private + def _determine_type(url) # Lets do our best to determine the type from the file extension case ::File.extname(url) - when ".gz", ".tgz" - return "ami" - when ".qcow2", ".img" - return "qcow" + when '.gz', '.tgz' + return 'ami' + when '.qcow2', '.img' + return 'qcow' end end -private -def _upload_image(type, name, url) +def _upload_image(type, name, api, url) case type when 'ami' - _upload_ami(name, url) + _upload_ami(name, api, url) when 'qcow' - _upload_qcow(name, url) + _upload_qcow(name, api, url) end end -private -def _upload_qcow(name, url) - glance_cmd = "glance --insecure -I #{@user} -K #{@pass} -T #{@tenant} -N #{@ks_uri}" - c_fmt = "--container-format bare" - d_fmt = "--disk-format qcow2" +def _upload_qcow(name, api, url) + glance_cmd = "glance --insecure --os-username #{@user} --os-password #{@pass} --os-tenant-name #{@tenant} --os-image-url #{api} --os-auth-url #{@ks_uri}" + c_fmt = '--container-format bare' + d_fmt = '--disk-format qcow2' img_file_name = ::File.basename(url) remote_file "#{Chef::Config[:file_cache_path]}/#{img_file_name}" do source "#{url}" action :create_if_missing end + execute "Uploading QCOW2 image #{name}" do cwd Chef::Config[:file_cache_path] command "#{glance_cmd} image-create --name #{name} \ @@ -72,16 +78,16 @@ def _upload_qcow(name, url) end end -private -def _upload_ami(name, url) - glance_cmd = "glance --insecure -I #{@user} -K #{@pass} -T #{@tenant} -N #{@ks_uri}" - aki_fmt = "--container-format aki --disk-format aki" - ari_fmt = "--container-format ari --disk-format ari" - ami_fmt = "--container-format ami --disk-format ami" +# TODO(chrislaco) This refactor is in the works via Craig Tracey +def _upload_ami(name, api, url) # rubocop:disable MethodLength + glance_cmd = "glance --insecure --os-username #{@user} --os-password #{@pass} --os-tenant-name #{@tenant} --os-image-url #{api} --os-auth-url #{@ks_uri}" + aki_fmt = '--container-format aki --disk-format aki' + ari_fmt = '--container-format ari --disk-format ari' + ami_fmt = '--container-format ami --disk-format ami' bash "Uploading AMI image #{name}" do - cwd "/tmp" - user "root" + cwd '/tmp' + user 'root' code <<-EOH set -x mkdir -p images/#{name} diff --git a/chef/cookbooks/openstack-image/recipes/api.rb b/chef/cookbooks/openstack-image/recipes/api.rb index b6ac6be..1e19db4 100644 --- a/chef/cookbooks/openstack-image/recipes/api.rb +++ b/chef/cookbooks/openstack-image/recipes/api.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-image # Recipe:: api @@ -6,6 +7,7 @@ # Copyright 2012-2013, Opscode, Inc. # Copyright 2012-2013, AT&T Services, Inc. # Copyright 2013, Craig Tracey +# Copyright 2013, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,96 +22,112 @@ # limitations under the License. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -if node["openstack"]["image"]["syslog"]["use"] - include_recipe "openstack-common::logging" +if node['openstack']['image']['syslog']['use'] + include_recipe 'openstack-common::logging' end -platform_options = node["openstack"]["image"]["platform"] +platform_options = node['openstack']['image']['platform'] -package "python-keystone" do - action :install +package 'python-keystoneclient' do + options platform_options['package_overrides'] + action :upgrade end -package "curl" do - action :install +package 'curl' do + options platform_options['package_overrides'] + action :upgrade end -platform_options["image_packages"].each do |pkg| +platform_options['image_packages'].each do |pkg| package pkg do action :upgrade + options platform_options['package_overrides'] end end -service "image-api" do - service_name platform_options["image_api_service"] - supports :status => true, :restart => true +if node['openstack']['image']['api']['default_store'] == 'swift' + platform_options['swift_packages'].each do |pkg| + package pkg do + action :upgrade + options platform_options['package_overrides'] + end + end - action [:enable,:start] +elsif node['openstack']['image']['api']['default_store'] == 'rbd' + # rbd_user = node['openstack']['image']['api']['rbd']['rbd_store_user'] + # rbd_key = get_password 'service', node['openstack']['image']['api']['rbd']['key_name'] + # + # include_recipe 'openstack-common::ceph_client' + # + # platform_options['ceph_packages'].each do |pkg| + # package pkg do + # options platform_options['package_overrides'] + # action :upgrade + # end + # end + # + # template "/etc/ceph/ceph.client.#{rbd_user}.keyring" do + # source 'ceph.client.keyring.erb' + # cookbook 'openstack-common' + # owner node['openstack']['image']['user'] + # group node['openstack']['image']['group'] + # mode 00600 + # variables( + # name: rbd_user, + # key: rbd_key + # ) + # end end -directory "/etc/glance" do - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +service 'glance-api' do + service_name platform_options['image_api_service'] + supports status: true, restart: true + + action :enable +end + +directory '/etc/glance' do + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] mode 00700 end -directory "/var/cache/glance/" do - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] - mode 00700 -end - -directory ::File.dirname node["openstack"]["image"]["api"]["auth"]["cache_dir"] do - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +directory ::File.dirname node['openstack']['image']['api']['auth']['cache_dir'] do + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] mode 00700 end -template "/etc/glance/policy.json" do - source "policy.json.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] - mode 00644 +glance = node['openstack']['image'] - notifies :restart, "service[image-api]", :immediately +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' +service_pass = get_password 'service', 'openstack-image' + +auth_uri = auth_uri_transform identity_endpoint.to_s, node['openstack']['image']['api']['auth']['version'] + +db_user = node['openstack']['db']['image']['username'] +db_pass = get_password 'db', 'glance' +sql_connection = db_uri('image', db_user, db_pass) + +mq_service_type = node['openstack']['mq']['image']['service_type'] + +if mq_service_type == 'rabbitmq' + mq_password = get_password('user', \ + node['openstack']['mq']['user'], \ + node['openstack']['mq']['password']) +elsif mq_service_type == 'qpid' + mq_password = get_password 'user', node['openstack']['mq']['image']['qpid']['username'] end -glance = node["openstack"]["image"] - -identity_endpoint = endpoint "identity-api" -identity_admin_endpoint = endpoint "identity-admin" -service_pass = service_password node['openstack']['identity']['image']['password'] - -#TODO(jaypipes): Move this logic and stuff into the openstack-common -# library cookbook. -auth_uri = identity_endpoint.to_s -if node["openstack"]["image"]["api"]["auth"]["version"] != "v2.0" - # The auth_uri should contain /v2.0 in most cases, but if the - # auth_version is v3.0, we leave it off. This is only necessary - # for environments that need to support V3 non-default-domain - # tokens, which is really the only reason to set version to - # something other than v2.0 (the default) - auth_uri = auth_uri.gsub('/v2.0', '') -end - -#db_user = node["openstack"]["image"]["db"]["username"] -#db_pass = db_password "glance" -db_user = node["openstack"]["db"]["image"]["username"] -db_pass = node["openstack"]["db"]["image"]["password"] - -sql_connection = db_uri("image", db_user, db_pass) - -registry_endpoint = endpoint "image-registry" -api_endpoint = endpoint "image-api" -service_pass = service_password node['openstack']['identity']['image']['password'] -service_tenant_name = node['openstack']['identity']['image']['tenant'] -service_user = node['openstack']['identity']['image']['username'] +registry_endpoint = endpoint 'image-registry' +api_bind = endpoint 'image-api-bind' # Possible combinations of options here # - default_store=file @@ -122,146 +140,130 @@ service_user = node['openstack']['identity']['image']['username'] # swift_store_auth_version from the node attributes and use them to connect # to the swift compatible API service running elsewhere - possibly # Rackspace Cloud Files. -if glance["api"]["swift_store_auth_address"].nil? +if glance['api']['swift_store_auth_address'].nil? swift_store_auth_address = auth_uri - swift_store_user="#{service_tenant_name}:#{service_user}" + swift_store_user = "#{glance['service_tenant_name']}_#{glance['service_user']}" swift_user_tenant = nil swift_store_key = service_pass - swift_store_auth_version=2 + swift_store_auth_version = 2 else - swift_store_auth_address=glance["api"]["swift_store_auth_address"] - swift_user_tenant = glance["api"]["swift_user_tenant"] - swift_store_user=glance["api"]["swift_store_user"] - swift_store_key = service_password swift_store_user - swift_store_auth_version=glance["api"]["swift_store_auth_version"] + swift_store_auth_address = glance['api']['swift_store_auth_address'] + swift_user_tenant = glance['api']['swift_user_tenant'] + swift_store_user = glance['api']['swift_store_user'] + swift_store_key = get_password 'service', swift_store_user + swift_store_auth_version = glance['api']['swift_store_auth_version'] end -# Only use the glance image cacher if we aren't using file for our backing store. -if glance["api"]["default_store"]=="file" - glance_flavor="keystone" -else - glance_flavor="keystone+cachemanagement" +glance_flavor = 'keystone' +if glance['api']['cache_management'] + glance_flavor += '+cachemanagement' +elsif glance['api']['caching'] + glance_flavor += '+caching' end -if node["openstack"]["image"]["api"]["bind_interface"].nil? - bind_address = api_endpoint.host -else - bind_address = address_for node["openstack"]["image"]["api"]["bind_interface"] +unless node['openstack']['image']['api']['vmware']['vmware_server_host'].empty? + vmware_server_password = get_secret node['openstack']['image']['api']['vmware']['secret_name'] end -if node["openstack"]["ha"]["status"].eql?('enable') - registry_ip_address = address_for node["openstack"]["image"]["registry"]["bind_interface"] -else - registry_ip_address = registry_endpoint.host -end - -template "/etc/glance/glance-api.conf" do - source "glance-api.conf.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +template '/etc/glance/glance-api.conf' do + source 'glance-api.conf.erb' + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] mode 00644 variables( - :api_bind_address => bind_address, - :api_bind_port => api_endpoint.port, -# :registry_ip_address => registry_endpoint.host, - :registry_ip_address => registry_ip_address, - :registry_port => registry_endpoint.port, - :sql_connection => sql_connection, - :glance_flavor => glance_flavor, - :auth_uri => auth_uri, - :identity_admin_endpoint => identity_admin_endpoint, - :service_tenant_name => service_tenant_name, - :service_user => service_user, - :service_pass => service_pass, - :swift_store_key => swift_store_key, - :swift_user_tenant => swift_user_tenant, - :swift_store_user => swift_store_user, - :swift_store_auth_address => swift_store_auth_address, - :swift_store_auth_version => swift_store_auth_version + api_bind_address: api_bind.host, + api_bind_port: api_bind.port, + registry_ip_address: registry_endpoint.host, + registry_port: registry_endpoint.port, + sql_connection: sql_connection, + glance_flavor: glance_flavor, + auth_uri: auth_uri, + identity_admin_endpoint: identity_admin_endpoint, + service_pass: service_pass, + swift_store_key: swift_store_key, + swift_user_tenant: swift_user_tenant, + swift_store_user: swift_store_user, + swift_store_auth_address: swift_store_auth_address, + swift_store_auth_version: swift_store_auth_version, + notification_driver: node['openstack']['image']['notification_driver'], + mq_service_type: mq_service_type, + mq_password: mq_password, + vmware_server_password: vmware_server_password ) - notifies :restart, "service[image-api]", :immediately + notifies :restart, 'service[glance-api]', :immediately end -template "/etc/glance/glance-api-paste.ini" do - source "glance-api-paste.ini.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +template '/etc/glance/glance-api-paste.ini' do + source 'glance-api-paste.ini.erb' + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] mode 00644 - notifies :restart, "service[image-api]", :immediately + notifies :restart, 'service[glance-api]', :immediately end -template "/etc/glance/glance-cache.conf" do - source "glance-cache.conf.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +template '/etc/glance/glance-cache.conf' do + source 'glance-cache.conf.erb' + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] mode 00644 variables( - :registry_ip_address => registry_endpoint.host, - :registry_port => registry_endpoint.port + registry_ip_address: registry_endpoint.host, + registry_port: registry_endpoint.port, + vmware_server_password: vmware_server_password ) - notifies :restart, "service[image-api]" + notifies :restart, 'service[glance-api]', :immediately end -#TODO(jaypipes) I don't think this even exists or at least isn't +# TODO(jaypipes) I don't think this even exists or at least isn't # used, since the Glance cache middleware goes in the api-paste.ini... -template "/etc/glance/glance-cache-paste.ini" do - source "glance-cache-paste.ini.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +template '/etc/glance/glance-cache-paste.ini' do + source 'glance-cache-paste.ini.erb' + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] mode 00644 - notifies :restart, "service[image-api]" + notifies :restart, 'service[glance-api]', :immediately end -template "/etc/glance/glance-scrubber.conf" do - source "glance-scrubber.conf.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +template '/etc/glance/glance-scrubber.conf' do + source 'glance-scrubber.conf.erb' + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] mode 00644 variables( - :registry_ip_address => registry_endpoint.host, - :registry_port => registry_endpoint.port + registry_ip_address: registry_endpoint.host, + registry_port: registry_endpoint.port ) end # Configure glance-cache-pruner to run every 30 minutes -cron "glance-cache-pruner" do - minute "*/30" - command "/usr/bin/glance-cache-pruner" +cron 'glance-cache-pruner' do + minute '*/30' + command "/usr/bin/glance-cache-pruner #{node['openstack']['image']['cron']['redirection']}" end # Configure glance-cache-cleaner to run at 00:01 everyday -cron "glance-cache-cleaner" do - minute "01" - hour "00" - command "/usr/bin/glance-cache-cleaner" +cron 'glance-cache-cleaner' do + minute '01' + hour '00' + command "/usr/bin/glance-cache-cleaner #{node['openstack']['image']['cron']['redirection']}" end -template "/etc/glance/glance-scrubber-paste.ini" do - source "glance-scrubber-paste.ini.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +template '/etc/glance/glance-scrubber-paste.ini' do + source 'glance-scrubber-paste.ini.erb' + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] mode 00644 end -execute "delay" do - command "sleep 5" - action :run -end - -if node["openstack"]["image"]["image_upload"] - node["openstack"]["image"]["upload_images"].each do |img| - openstack_image_image "Image setup for #{img.to_s}" do - image_url node["openstack"]["image"]["upload_image"][img.to_sym] - image_name img - identity_user service_user - identity_pass service_pass - identity_tenant service_tenant_name - identity_uri auth_uri - action :upload - end +if node['openstack']['image']['api']['default_store'] == 'file' + directory node['openstack']['image']['filesystem_store_datadir'] do + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] + mode 00750 + recursive true end end diff --git a/chef/cookbooks/openstack-image/recipes/client.rb b/chef/cookbooks/openstack-image/recipes/client.rb new file mode 100644 index 0000000..f25607e --- /dev/null +++ b/chef/cookbooks/openstack-image/recipes/client.rb @@ -0,0 +1,32 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-image +# Recipe:: client +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +platform_options = node['openstack']['image']['platform'] +platform_options['image_client_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end diff --git a/chef/cookbooks/openstack-image/recipes/glance-config-ceph.rb b/chef/cookbooks/openstack-image/recipes/glance-config-ceph.rb new file mode 100644 index 0000000..ed3596d --- /dev/null +++ b/chef/cookbooks/openstack-image/recipes/glance-config-ceph.rb @@ -0,0 +1,45 @@ +# attention: +# this recipe should run after the openstack and ceph are working correctly! +# + +if node['openstack']['image']['api']['default_store'] == 'rbd' + + include_recipe 'ceph::_common' + include_recipe 'ceph::mon_install' + include_recipe 'ceph::conf' + platform_options = node['openstack']['image']['platform'] + cluster = 'ceph' + + class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack + end + + rbd_user = node['openstack']['image']['api']['rbd']['rbd_store_user'] + + if mon_nodes.empty? + rbd_key = "" + elsif !mon_master['ceph'].has_key?('glance-secret') + rbd_key = "" + else + rbd_key = mon_master['ceph']['glance-secret'] + end + + template "/etc/ceph/ceph.client.#{rbd_user}.keyring" do + source 'ceph.client.keyring.erb' + cookbook 'openstack-common' + owner node['openstack']['image']['user'] + group node['openstack']['image']['group'] + mode 00600 + variables( + name: rbd_user, + key: rbd_key + ) + end + + service 'glance-api-ceph' do + service_name platform_options['image_api_service'] + supports status: true, restart: true + action :enable + subscribes :restart, resources('template[/etc/ceph/ceph.conf]') + end +end diff --git a/chef/cookbooks/openstack-image/recipes/identity_registration.rb b/chef/cookbooks/openstack-image/recipes/identity_registration.rb index 7c5f992..393280c 100644 --- a/chef/cookbooks/openstack-image/recipes/identity_registration.rb +++ b/chef/cookbooks/openstack-image/recipes/identity_registration.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-image # Recipe:: identity_registration @@ -19,42 +20,41 @@ # limitations under the License. # -require "uri" +require 'uri' -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -identity_admin_endpoint = endpoint "identity-admin" +identity_admin_endpoint = endpoint 'identity-admin' -token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +token = get_secret 'openstack_identity_bootstrap_token' auth_url = ::URI.decode identity_admin_endpoint.to_s -registry_endpoint = endpoint "image-registry" -api_endpoint = endpoint "image-api" +api_endpoint = endpoint 'image-api' -service_pass = service_password node['openstack']['identity']['image']['password'] -service_tenant_name = node["openstack"]["image"]["service_tenant_name"] -service_user = node["openstack"]["image"]["service_user"] -service_role = node["openstack"]["image"]["service_role"] -region = node["openstack"]["image"]["region"] +service_pass = get_password 'service', 'openstack-image' +service_tenant_name = node['openstack']['image']['service_tenant_name'] +service_user = node['openstack']['image']['service_user'] +service_role = node['openstack']['image']['service_role'] +region = node['openstack']['image']['region'] # Register Image Service -openstack_identity_register "Register Image Service" do +openstack_identity_register 'Register Image Service' do auth_uri auth_url bootstrap_token token - service_name "glance" - service_type "image" - service_description "Glance Image Service" + service_name 'glance' + service_type 'image' + service_description 'Glance Image Service' action :create_service end # Register Image Endpoint -openstack_identity_register "Register Image Endpoint" do +openstack_identity_register 'Register Image Endpoint' do auth_uri auth_url bootstrap_token token - service_type "image" + service_type 'image' endpoint_region region endpoint_adminurl api_endpoint.to_s endpoint_internalurl api_endpoint.to_s @@ -64,11 +64,11 @@ openstack_identity_register "Register Image Endpoint" do end # Register Service Tenant -openstack_identity_register "Register Service Tenant" do +openstack_identity_register 'Register Service Tenant' do auth_uri auth_url bootstrap_token token tenant_name service_tenant_name - tenant_description "Service Tenant" + tenant_description 'Service Tenant' tenant_enabled true # Not required as this is the default action :create_tenant diff --git a/chef/cookbooks/openstack-image/recipes/image_upload.rb b/chef/cookbooks/openstack-image/recipes/image_upload.rb new file mode 100644 index 0000000..f816e78 --- /dev/null +++ b/chef/cookbooks/openstack-image/recipes/image_upload.rb @@ -0,0 +1,61 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-image +# Recipe:: image_upload +# +# Copyright 2013, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +if node['openstack']['image']['syslog']['use'] + include_recipe 'openstack-common::logging' +end + +unless node['local_repo'].nil? or node['local_repo'].empty? + node.override['openstack']['image']['upload_image']['cirros'] = "#{node['local_repo']}/cirros-0.3.2-x86_64-disk.img" +end + +platform_options = node['openstack']['image']['platform'] +platform_options['image_client_packages'].each do |pkg| + package pkg do + action :upgrade + end +end + +identity_endpoint = endpoint 'identity-api' + +# For glance client, only identity v2 is supported. See discussion on +# https://bugs.launchpad.net/openstack-chef/+bug/1207504 +# So here auth_uri can not be transformed. +auth_uri = identity_endpoint.to_s + +service_pass = get_password 'service', 'openstack-image' +service_tenant_name = node['openstack']['image']['service_tenant_name'] +service_user = node['openstack']['image']['service_user'] + +node['openstack']['image']['upload_images'].each do |img| + openstack_image_image "Image setup for #{img.to_s}" do + image_url node['openstack']['image']['upload_image'][img.to_sym] + image_name img + identity_user service_user + identity_pass service_pass + identity_tenant service_tenant_name + identity_uri auth_uri + action :upload + end +end diff --git a/chef/cookbooks/openstack-image/recipes/registry.rb b/chef/cookbooks/openstack-image/recipes/registry.rb index 1798039..0c7a54f 100644 --- a/chef/cookbooks/openstack-image/recipes/registry.rb +++ b/chef/cookbooks/openstack-image/recipes/registry.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-image # Recipe:: registry @@ -17,130 +18,119 @@ # See the License for the specific language governing permissions and # limitations under the License. # -require "uri" -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -if node["openstack"]["image"]["syslog"]["use"] - include_recipe "openstack-common::logging" +if node['openstack']['image']['syslog']['use'] + include_recipe 'openstack-common::logging' end -platform_options = node["openstack"]["image"]["platform"] +platform_options = node['openstack']['image']['platform'] -package "python-keystone" do - action :install +package 'python-keystoneclient' do + options platform_options['package_overrides'] + action :upgrade end -#db_user = node['openstack']['db']['image']['username'] -#db_pass = db_password node['openstack']['db']['image']['password'] -db_user = node["openstack"]["db"]["image"]["username"] -db_pass = node["openstack"]["db"]["image"]["password"] +db_user = node['openstack']['db']['image']['username'] +db_pass = get_password 'db', 'glance' +sql_connection = db_uri('image', db_user, db_pass) -sql_connection = db_uri("image", db_user, db_pass) +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' +registry_bind = endpoint 'image-registry-bind' +service_pass = get_password 'service', 'openstack-image' -identity_endpoint = endpoint "identity-admin" -registry_endpoint = endpoint "image-registry" -service_tenant_name = node['openstack']['identity']['image']['tenant'] -service_user = node['openstack']['identity']['image']['username'] -service_pass = service_password node['openstack']['identity']['image']['password'] +auth_uri = auth_uri_transform identity_endpoint.to_s, node['openstack']['image']['registry']['auth']['version'] -package "curl" do - action :install +glance_user = node['openstack']['image']['user'] +glance_group = node['openstack']['image']['group'] + +package 'curl' do + options platform_options['package_overrides'] + action :upgrade end -db_type = node['openstack']['db']['identity']['db_type'] -platform_options["#{db_type}_python_packages"].each do |pkg| - package pkg do - action :install +pkg_key = "#{node['openstack']['db']['image']['service_type']}_python_packages" +if platform_options.key?(pkg_key) + platform_options[pkg_key].each do |pkg| + package pkg do + action :upgrade + options platform_options['package_overrides'] + end end end -platform_options["image_packages"].each do |pkg| +platform_options['image_packages'].each do |pkg| package pkg do action :upgrade + options platform_options['package_overrides'] end end -directory ::File.dirname(node["openstack"]["image"]["registry"]["auth"]["cache_dir"]) do - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +directory ::File.dirname(node['openstack']['image']['registry']['auth']['cache_dir']) do + owner glance_user + group glance_group mode 00700 end -service "image-registry" do - service_name platform_options["image_registry_service"] - supports :status => true, :restart => true +service 'glance-registry' do + service_name platform_options['image_registry_service'] + supports status: true, restart: true action :enable end -# Having to manually version the database because of Ubuntu bug -# https://bugs.launchpad.net/ubuntu/+source/glance/+bug/981111 -execute "glance-manage version_control 0" do - not_if "glance-manage db_version" - only_if { platform?(%w{ubuntu debian}) } -end - -file "/var/lib/glance/glance.sqlite" do +file '/var/lib/glance/glance.sqlite' do action :delete + not_if { node['openstack']['db']['image']['service_type'] == 'sqlite' } end -directory "/etc/glance" do - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +directory '/etc/glance' do + owner glance_user + group glance_group mode 00700 end -directory "/var/log/glance" do - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] - mode 00700 -end - -directory "/var/cache/glance/" do - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] - mode 00700 -end - -if node["openstack"]["image"]["registry"]["bind_interface"].nil? - bind_address = registry_endpoint.host -else - bind_address = address_for node["openstack"]["image"]["registry"]["bind_interface"] -end - -template "/etc/glance/glance-registry.conf" do - source "glance-registry.conf.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +template '/etc/glance/glance-registry.conf' do + source 'glance-registry.conf.erb' + owner 'root' + group 'root' mode 00644 variables( - :registry_bind_address => bind_address, - :registry_port => registry_endpoint.port, + :registry_bind_address => registry_bind.host, + :registry_bind_port => registry_bind.port, :sql_connection => sql_connection, - "identity_endpoint" => identity_endpoint, - "service_pass" => service_pass, - "service_tenant_name" => service_tenant_name, - "service_user" => service_user + :auth_uri => auth_uri, + 'identity_admin_endpoint' => identity_admin_endpoint, + 'service_pass' => service_pass ) - notifies :restart, "service[image-registry]", :immediately + notifies :restart, 'service[glance-registry]', :immediately end -execute "glance-manage db_sync" do - only_if { node["openstack"]["image"]["db"]["migrate"] } +# Having to manually version the database because of Ubuntu bug +# https://bugs.launchpad.net/ubuntu/+source/glance/+bug/981111 +execute 'glance-manage version_control 0' do + user glance_user + group glance_group + not_if 'glance-manage db_version', user: glance_user, group: glance_group + only_if { platform_family?('debian') } end -template "/etc/glance/glance-registry-paste.ini" do - source "glance-registry-paste.ini.erb" - owner node["openstack"]["image"]["user"] - group node["openstack"]["image"]["group"] +execute 'glance-manage db_sync' do + user glance_user + group glance_group + only_if { node['openstack']['db']['image']['migrate'] } +end + +template '/etc/glance/glance-registry-paste.ini' do + source 'glance-registry-paste.ini.erb' + owner 'root' + group 'root' mode 00644 - notifies :restart, "service[image-registry]", :immediately + notifies :restart, 'service[glance-registry]', :immediately end - -identity_endpoint = endpoint "identity-api" -auth_uri = ::URI.decode identity_endpoint.to_s diff --git a/chef/cookbooks/openstack-image/resources/image.rb b/chef/cookbooks/openstack-image/resources/image.rb index 774d431..1998543 100644 --- a/chef/cookbooks/openstack-image/resources/image.rb +++ b/chef/cookbooks/openstack-image/resources/image.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-image # Resource:: image @@ -26,10 +27,10 @@ def initialize(*args) @action = :upload end -attribute :image_url, :kind_of => String -attribute :image_type, :kind_of => String, :default => "unknown", :equal_to => ["unknown", "ami", "qcow"] -attribute :image_name, :kind_of => String, :default => "default" -attribute :identity_user, :kind_of => String -attribute :identity_pass, :kind_of => String -attribute :identity_tenant, :kind_of => String -attribute :identity_uri, :kind_of => String +attribute :image_url, kind_of: String +attribute :image_type, kind_of: String, default: 'unknown', equal_to: ['unknown', 'ami', 'qcow'] +attribute :image_name, kind_of: String, default: 'default' +attribute :identity_user, kind_of: String +attribute :identity_pass, kind_of: String +attribute :identity_tenant, kind_of: String +attribute :identity_uri, kind_of: String diff --git a/chef/cookbooks/openstack-image/spec/api-redhat_spec.rb b/chef/cookbooks/openstack-image/spec/api-redhat_spec.rb index 8656aaa..b334669 100644 --- a/chef/cookbooks/openstack-image/spec/api-redhat_spec.rb +++ b/chef/cookbooks/openstack-image/spec/api-redhat_spec.rb @@ -1,15 +1,32 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-image::api" do - before { image_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-image::api" +describe 'openstack-image::api' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it "starts glance api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-glance-api" + include_context 'image-stubs' + + it 'does upgrade keystoneclient package' do + expect(chef_run).to upgrade_package('python-keystoneclient') + end + + it 'does not upgrade swift packages by default' do + expect(chef_run).not_to upgrade_package('openstack-swift') + end + + it 'upgrades swift package if openstack/image/api/default_store is swift' do + node.set['openstack']['image']['api']['default_store'] = 'swift' + + expect(chef_run).to upgrade_package('openstack-swift') + end + + it 'starts glance api on boot' do + expect(chef_run).to enable_service('openstack-glance-api') end end end diff --git a/chef/cookbooks/openstack-image/spec/api_spec.rb b/chef/cookbooks/openstack-image/spec/api_spec.rb index 9d1f5b8..7721f8a 100644 --- a/chef/cookbooks/openstack-image/spec/api_spec.rb +++ b/chef/cookbooks/openstack-image/spec/api_spec.rb @@ -1,237 +1,451 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-image::api" do - before { image_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["image"]["syslog"]["use"] = true - n.set["cpu"] = { 'total' => '1' } - end - @chef_run.converge "openstack-image::api" +describe 'openstack-image::api' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - expect_runs_openstack_common_logging_recipe + include_context 'image-stubs' + include_examples 'common-logging-recipe' + include_examples 'common-packages' + include_examples 'cache-directory' + include_examples 'glance-directory' - it "doesn't run logging recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-image::api" - - expect(chef_run).not_to include_recipe "openstack-common::logging" + it 'does not upgrade swift package by default' do + expect(chef_run).not_to upgrade_package('python-swift') end - expect_installs_python_keystone - - expect_installs_curl - - expect_installs_ubuntu_glance_packages - - it "starts glance api on boot" do - expect(@chef_run).to set_service_to_start_on_boot "glance-api" + it 'starts glance api on boot' do + expect(chef_run).to enable_service('glance-api') end - expect_creates_glance_dir - - expect_creates_cache_dir - - describe "policy.json" do + describe 'using swift for default_store' do before do - @file = @chef_run.template "/etc/glance/policy.json" + node.set['openstack']['image']['api']['default_store'] = 'swift' end - it "has proper owner" do - expect(@file).to be_owned_by "glance", "glance" + it 'upgrades swift package if openstack/image/api/default_store is swift' do + expect(chef_run).to upgrade_package('python-swift') end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end + it 'honors platform package name and option overrides for swift packages' do + node.set['openstack']['image']['platform']['package_overrides'] = '--override1 --override2' + node.set['openstack']['image']['platform']['swift_packages'] = ['my-swift'] - it "notifies image-api restart" do - expect(@file).to notify "service[image-api]", :restart + expect(chef_run).to upgrade_package('my-swift').with(options: '--override1 --override2') end end - describe "glance-api.conf" do + describe 'using rbd for default_store' do before do - @file = @chef_run.template "/etc/glance/glance-api.conf" + node.set['openstack']['image']['api']['default_store'] = 'rbd' end - it "has proper owner" do - expect(@file).to be_owned_by "glance", "glance" + it 'upgrades python-ceph package' do + expect(chef_run).to upgrade_package('python-ceph') end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'honors platform package name and option overrides for ceph packages' do + node.set['openstack']['image']['platform']['package_overrides'] = '--override1 --override2' + node.set['openstack']['image']['platform']['ceph_packages'] = ['my-ceph'] + + expect(chef_run).to upgrade_package('my-ceph').with(options: '--override1 --override2') end - it "has bind host when bind_interface not specified" do - expect(@chef_run).to create_file_with_content @file.name, - "bind_host = 127.0.0.1" + it 'includes the ceph_client recipe from openstack-common' do + expect(chef_run).to include_recipe('openstack-common::ceph_client') end - it "has bind host when bind_interface specified" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["image"]["api"]["bind_interface"] = "lo" - n.set["cpu"] = { 'total' => '1' } + describe 'cephx client keyring file' do + let(:file) { chef_run.template('/etc/ceph/ceph.client.glance.keyring') } + + it 'creates /etc/ceph/ceph.client.glance.keyring' do + expect(chef_run).to create_template(file.name).with( + user: 'glance', + group: 'glance', + mode: 00600, + cookbook: 'openstack-common' + ) end - chef_run.converge "openstack-image::api" - expect(chef_run).to create_file_with_content @file.name, - "bind_host = 127.0.1.1" - end - - it "notifies image-api restart" do - expect(@file).to notify "service[image-api]", :restart + it 'has the proper content' do + [/^\[client\.glance\]$/, + /^ key = rbd-pass$/].each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + end end end - describe "glance-api-paste.ini" do - before do - @file = @chef_run.template "/etc/glance/glance-api-paste.ini" + it 'starts glance api on boot' do + expect(chef_run).to enable_service('glance-api') + end + + describe 'glance-api.conf' do + let(:file) { chef_run.template('/etc/glance/glance-api.conf') } + + it 'creates glance-api.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'glance', + group: 'glance', + mode: 00644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "glance", "glance" + include_examples 'vmware config', '/etc/glance/glance-api.conf' + + it 'has bind host when bind_interface not specified' do + match = 'bind_host = 127.0.0.1' + expect(chef_run).to render_file(file.name).with_content(match) end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'has bind host when bind_interface specified' do + node.set['openstack']['endpoints']['image-api-bind']['bind_interface'] = 'lo' + + match = 'bind_host = 127.0.1.1' + expect(chef_run).to render_file(file.name).with_content(match) end - it "template contents" do - pending "TODO: implement" + it 'has default filesystem_store_datadir setting' do + match = 'filesystem_store_datadir = /var/lib/glance/images' + expect(chef_run).to render_file(file.name).with_content(match) end - it "notifies image-api restart" do - expect(@file).to notify "service[image-api]", :restart + it 'has configurable filesystem_store_datadir setting' do + node.set['openstack']['image']['filesystem_store_datadir'] = 'foo' + + expect(chef_run).to render_file(file.name).with_content( + /^filesystem_store_datadir = foo$/) + end + + it 'notifies glance-api restart' do + expect(file).to notify('service[glance-api]').to(:restart) + end + + it 'does not have caching enabled by default' do + expect(chef_run).to render_file(file.name).with_content( + /^flavor = keystone$/) + end + + it 'enables caching when attribute is set' do + node.set['openstack']['image']['api']['caching'] = true + + expect(chef_run).to render_file(file.name).with_content( + /^flavor = keystone\+caching$/) + end + + it 'enables cache_management when attribute is set' do + node.set['openstack']['image']['api']['cache_management'] = true + + expect(chef_run).to render_file(file.name).with_content( + /^flavor = keystone\+cachemanagement$/) + end + + it 'enables only cache_management when it and the caching attributes are set' do + node.set['openstack']['image']['api']['cache_management'] = true + node.set['openstack']['image']['api']['caching'] = true + + expect(chef_run).to render_file(file.name).with_content( + /^flavor = keystone\+cachemanagement$/) + end + + it 'has configurable api workers setting' do + node.set['openstack']['image']['api']['workers'] = 10 + expect(chef_run).to render_file(file.name).with_content( + /^workers = 10$/) + end + + it 'confirms default min value is set' do + node.automatic['cpu']['total'] = 10 + expect(chef_run).to render_file(file.name).with_content( + /^workers = 8$/) + end + + it 'sets show_image_direct_url appropriately' do + node.set['openstack']['image']['api']['show_image_direct_url'] = 'True' + expect(chef_run).to render_file(file.name).with_content( + /^show_image_direct_url = True$/) + end + + it 'sets swift_enable_snet as specified' do + node.set['openstack']['image']['api']['swift']['enable_snet'] = 'True' + expect(chef_run).to render_file(file.name).with_content( + /^swift_enable_snet = True$/) + end + + it 'doesnt set swift_store_region if nil' do + node.set['openstack']['image']['api']['swift']['store_region'] = nil + expect(chef_run).to_not render_file(file.name).with_content( + /^swift_store_region/) + end + + it 'does set swift_store_region if not nil' do + node.set['openstack']['image']['api']['swift']['store_region'] = 'test_region' + expect(chef_run).to render_file(file.name).with_content( + /^swift_store_region = test_region$/) + end + + it 'does set the default rbd_store settings' do + [%r|^rbd_store_ceph_conf = /etc/ceph/ceph\.conf$|, + /^rbd_store_user = glance$/, + /^rbd_store_pool = images$/, + /^rbd_store_chunk_size = 8$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'does set the rbd_store settings when overridden' do + node.set['openstack']['image']['api']['rbd']['rbd_store_ceph_conf'] = '/etc/ceph.conf' + node.set['openstack']['image']['api']['rbd']['rbd_store_user'] = 'openstack-image' + node.set['openstack']['image']['api']['rbd']['rbd_store_pool'] = 'bootimages' + node.set['openstack']['image']['api']['rbd']['rbd_store_chunk_size'] = 4 + + [%r|^rbd_store_ceph_conf = /etc/ceph\.conf$|, + /^rbd_store_user = openstack-image$/, + /^rbd_store_pool = bootimages$/, + /^rbd_store_chunk_size = 4$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'has default_store setting' do + expect(chef_run).to render_file(file.name).with_content( + /^default_store = file$/) + end + + context 'keystone_authtoken' do + it 'has correct authtoken settings' do + [ + 'auth_uri = http://127.0.0.1:5000/v2.0', + 'auth_host = 127.0.0.1', + 'auth_port = 35357', + 'auth_protocol = http', + 'admin_tenant_name = service', + 'admin_user = glance', + 'admin_tenant_name = service', + 'admin_password = glance-pass', + 'signing_dir = /var/cache/glance/api' + ].each do |line| + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote(line)}$/) + end + end + + it 'has no auth_version' do + expect(chef_run).not_to render_file(file.name).with_content( + /^auth_version = v2.0$/) + end + + it 'has auth_version when auth version is set to v3.0' do + chef_run.node.set['openstack']['image']['api']['auth']['version'] = 'v3.0' + expect(chef_run).to render_file(file.name).with_content( + /^auth_version = v3.0$/) + end + end + + context 'rabbitmq' do + before do + node.set['openstack']['image']['notification_driver'] = 'messaging' + node.set['openstack']['mq']['image']['service_type'] = 'rabbitmq' + node.set['openstack']['mq']['image']['notification_topic'] = 'rabbit_topic' + end + + it 'has default rabbit settings' do + [ + 'transport_url = rabbit://', + 'rabbit_host = 127.0.0.1', + 'rabbit_port = 5672', + 'rabbit_userid = guest', + 'rabbit_password = mq-pass', + 'rabbit_virtual_host = /', + 'rabbit_notification_topic = rabbit_topic' + ].each do |line| + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote(line)}$/) + end + end + end + + context 'qpid' do + before do + node.set['openstack']['image']['notification_driver'] = 'messaging' + node.set['openstack']['mq']['image']['service_type'] = 'qpid' + node.set['openstack']['mq']['image']['notification_topic'] = 'qpid_topic' + node.set['openstack']['mq']['image']['qpid']['username'] = 'guest' + end + + it 'has default qpid settings' do + [ + 'transport_url = qpid://', + 'qpid_hostname=127.0.0.1', + 'qpid_port=5672', + 'qpid_username=guest', + 'qpid_password=mq-pass', + 'qpid_sasl_mechanisms=', + 'qpid_reconnect=true', + 'qpid_reconnect_timeout=0', + 'qpid_reconnect_limit=0', + 'qpid_reconnect_interval_min=0', + 'qpid_reconnect_interval_max=0', + 'qpid_reconnect_interval=0', + 'qpid_heartbeat=60', + 'qpid_protocol=tcp', + 'qpid_tcp_nodelay=true', + 'qpid_notification_topic = qpid_topic' + ].each do |line| + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote(line)}$/) + end + end end end - describe "glance-cache.conf" do - before do - @file = @chef_run.template "/etc/glance/glance-cache.conf" + describe 'glance-api-paste.ini' do + let(:file) { chef_run.template('/etc/glance/glance-api-paste.ini') } + + it 'creates glance-api-paste.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'glance', + group: 'glance', + mode: 00644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "glance", "glance" + it 'template contents' do + pending 'TODO: implement' end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" - end - - it "notifies image-api restart" do - expect(@file).to notify "service[image-api]", :restart + it 'notifies glance-api restart' do + expect(file).to notify('service[glance-api]').to(:restart) end end - describe "glance-cache-paste.ini" do - before do - @file = @chef_run.template "/etc/glance/glance-cache-paste.ini" + describe 'glance-cache.conf' do + let(:file) { chef_run.template('/etc/glance/glance-cache.conf') } + + include_examples 'vmware config', '/etc/glance/glance-cache.conf' + + it 'creates glance-cache.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'glance', + group: 'glance', + mode: 00644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "glance", "glance" + it 'notifies glance-api restart' do + expect(file).to notify('service[glance-api]').to(:restart) end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'has the default image_cache_dir setting' do + expect(chef_run).to render_file(file.name).with_content( + %r{^image_cache_dir = /var/lib/glance/image-cache/$}) end - it "template contents" do - pending "TODO: implement" + it 'has a configurable image_cache_dir setting' do + node.set['openstack']['image']['cache']['dir'] = 'foo' + + expect(chef_run).to render_file(file.name).with_content( + /^image_cache_dir = foo$/) end - it "notifies image-api restart" do - expect(@file).to notify "service[image-api]", :restart + it 'has the default cache stall_time setting' do + expect(chef_run).to render_file(file.name).with_content( + /^image_cache_stall_time = 86400$/) + end + + it 'has a configurable stall_time setting' do + node.set['openstack']['image']['cache']['stall_time'] = '42' + + expect(chef_run).to render_file(file.name).with_content( + /^image_cache_stall_time = 42$/) + end + + it 'has the default grace_period setting' do + expect(chef_run).to render_file(file.name).with_content( + /^image_cache_invalid_entry_grace_period = 3600$/) + end + + it 'has a configurable grace_period setting' do + node.set['openstack']['image']['cache']['grace_period'] = '42' + + expect(chef_run).to render_file(file.name).with_content( + /^image_cache_invalid_entry_grace_period = 42$/) end end - describe "glance-scrubber.conf" do - before do - @file = @chef_run.template "/etc/glance/glance-scrubber.conf" + describe 'glance-cache-paste.ini' do + let(:file) { chef_run.template('/etc/glance/glance-cache-paste.ini') } + + it 'creates glance-cache-paste.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'glance', + group: 'glance', + mode: 00644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "glance", "glance" - end + # README(galstrom21): This file currently has no templated variables + # it 'template contents' do + # pending 'TODO: implement' + # end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" + it 'notifies glance-api restart' do + expect(file).to notify('service[glance-api]').to(:restart) end end - it "has glance-cache-pruner cronjob running every 30 minutes" do - cron = @chef_run.cron "glance-cache-pruner" + describe 'glance-scrubber.conf' do + let(:file) { chef_run.template('/etc/glance/glance-scrubber.conf') } - expect(cron.command).to eq "/usr/bin/glance-cache-pruner" - expect(cron.minute).to eq "*/30" - end - - it "has glance-cache-cleaner to run at 00:01 each day" do - cron = @chef_run.cron "glance-cache-cleaner" - - expect(cron.command).to eq "/usr/bin/glance-cache-cleaner" - expect(cron.minute).to eq "01" - expect(cron.hour).to eq "00" - end - - describe "glance-scrubber-paste.ini" do - before do - @file = @chef_run.template "/etc/glance/glance-scrubber-paste.ini" + it 'creates glance-cache-paste.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'glance', + group: 'glance', + mode: 00644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "glance", "glance" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "template contents" do - pending "TODO: implement" + it 'template contents' do + pending 'TODO: implement' end end - it "uploads qcow images" do - opts = { - :step_into => ["openstack-image_image"] - } - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS.merge(opts) do |n| - n.set["openstack"]["image"] = { - "image_upload" => true, - "upload_images" => [ - "image1" - ], - "upload_image" => { - "image1" => "http://example.com/image.qcow2" - } - } - end - chef_run.converge "openstack-image::api" - cmd = "glance --insecure " \ - "-I glance " \ - "-K glance-pass " \ - "-T service " \ - "-N http://127.0.0.1:5000/v2.0 " \ - "image-create " \ - "--name image1 " \ - "--is-public true " \ - "--container-format bare "\ - "--disk-format qcow2 " \ - "--location http://example.com/image.qcow2" + it 'has glance-cache-pruner cronjob running every 30 minutes' do + cron = chef_run.cron('glance-cache-pruner') - expect(chef_run).to execute_command cmd + expect(cron.command).to eq '/usr/bin/glance-cache-pruner > /dev/null 2>&1' + expect(cron.minute).to eq '*/30' + end + + it 'has glance-cache-cleaner to run at 00:01 each day' do + cron = chef_run.cron('glance-cache-cleaner') + + expect(cron.command).to eq '/usr/bin/glance-cache-cleaner > /dev/null 2>&1' + expect(cron.minute).to eq '01' + expect(cron.hour).to eq '00' + end + + describe 'glance-scrubber-paste.ini' do + let(:file) { chef_run.template('/etc/glance/glance-scrubber-paste.ini') } + + it 'creates glance-scrubber-paste.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'glance', + group: 'glance', + mode: 00644 + ) + end + + it 'template contents' do + pending 'TODO: implement' + end end end end diff --git a/chef/cookbooks/openstack-image/spec/client-redhat_spec.rb b/chef/cookbooks/openstack-image/spec/client-redhat_spec.rb new file mode 100644 index 0000000..0f6aed0 --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/client-redhat_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-image::client' do + + describe 'redhat' do + + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades python glance client package' do + expect(chef_run).to upgrade_package('python-glanceclient') + end + end +end diff --git a/chef/cookbooks/openstack-image/spec/client_spec.rb b/chef/cookbooks/openstack-image/spec/client_spec.rb new file mode 100644 index 0000000..bcb9db3 --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/client_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-image::client' do + + describe 'ubuntu' do + + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades python glance client package' do + expect(chef_run).to upgrade_package('python-glanceclient') + end + end +end diff --git a/chef/cookbooks/openstack-image/spec/default_spec.rb b/chef/cookbooks/openstack-image/spec/default_spec.rb deleted file mode 100644 index d9376b5..0000000 --- a/chef/cookbooks/openstack-image/spec/default_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-dashboard::default" do -end diff --git a/chef/cookbooks/openstack-image/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-image/spec/identity_registration_spec.rb index 5b957f8..00d5a1f 100644 --- a/chef/cookbooks/openstack-image/spec/identity_registration_spec.rb +++ b/chef/cookbooks/openstack-image/spec/identity_registration_spec.rb @@ -1,91 +1,110 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-image::identity_registration" do - before do - image_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-image::identity_registration" +describe 'openstack-image::identity_registration' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it "registers image service" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Image Service" + include_context 'image-stubs' + + it 'registers image service' do + resource = chef_run.find_resource( + 'openstack-identity_register', + 'Register Image Service' ).to_hash expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_type => "image", - :service_description => "Glance Image Service", - :action => [:create_service] + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'image', + service_description: 'Glance Image Service', + action: [:create_service] ) end - it "registers image endpoint" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Image Endpoint" + context 'registers compute endpoint' do + it 'with default values' do + resource = chef_run.find_resource( + 'openstack-identity_register', + 'Register Image Endpoint' + ).to_hash + + expect(resource).to include( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'image', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:9292', + endpoint_internalurl: 'http://127.0.0.1:9292', + endpoint_publicurl: 'http://127.0.0.1:9292', + action: [:create_endpoint] + ) + end + + it 'with custom region override' do + node.set['openstack']['image']['region'] = 'imageRegion' + + resource = chef_run.find_resource( + 'openstack-identity_register', + 'Register Image Endpoint' + ).to_hash + + expect(resource).to include( + endpoint_region: 'imageRegion', + action: [:create_endpoint] + ) + end + end + + it 'registers service tenant' do + resource = chef_run.find_resource( + 'openstack-identity_register', + 'Register Service Tenant' ).to_hash expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_type => "image", - :endpoint_region => "RegionOne", - :endpoint_adminurl => "http://127.0.0.1:9292/v2", - :endpoint_internalurl => "http://127.0.0.1:9292/v2", - :endpoint_publicurl => "http://127.0.0.1:9292/v2", - :action => [:create_endpoint] + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + tenant_description: 'Service Tenant', + tenant_enabled: true, + action: [:create_tenant] ) end - it "registers service tenant" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Service Tenant" + it 'registers service user' do + resource = chef_run.find_resource( + 'openstack-identity_register', + 'Register glance User' ).to_hash expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :tenant_description => "Service Tenant", - :tenant_enabled => true, - :action => [:create_tenant] + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'glance', + user_pass: 'glance-pass', + user_enabled: true, + action: [:create_user] ) end - it "registers service user" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register glance User" - ).to_hash - - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :user_name => "glance", - :user_pass => "glance-pass", - :user_enabled => true, - :action => [:create_user] - ) - end - - it "grants admin role to service user for service tenant" do - resource = @chef_run.find_resource( - "openstack-identity_register", + it 'grants admin role to service user for service tenant' do + resource = chef_run.find_resource( + 'openstack-identity_register', "Grant 'admin' Role to glance User for service Tenant" ).to_hash expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :role_name => "admin", - :user_name => "glance", - :action => [:grant_role] + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + role_name: 'admin', + user_name: 'glance', + action: [:grant_role] ) end end diff --git a/chef/cookbooks/openstack-image/spec/image_upload_spec.rb b/chef/cookbooks/openstack-image/spec/image_upload_spec.rb new file mode 100644 index 0000000..324081c --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/image_upload_spec.rb @@ -0,0 +1,57 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-image::image_upload' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(options) } + let(:options) { UBUNTU_OPTS.merge(step_into: 'openstack_image_image') } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + include_context 'image-stubs' + + it 'uploads qcow image when one does not exist' do + node.set['openstack']['image'] = { + 'upload_images' => ['image1'], + 'upload_image' => { + 'image1' => 'http://example.com/image.qcow2' + } + } + + list_cmd = 'glance --insecure ' \ + '--os-username glance ' \ + '--os-password glance-pass ' \ + '--os-tenant-name service '\ + '--os-image-url http://127.0.0.1:9292 ' \ + '--os-auth-url http://127.0.0.1:5000/v2.0 ' \ + 'image-list | grep image1' + + stub_command(list_cmd).and_return(false) + + expect(chef_run).to run_execute('Uploading QCOW2 image image1') + end + + it 'does not upload qcow image if it already exists' do + node.set['openstack']['image'] = { + 'upload_images' => ['image1'], + 'upload_image' => { + 'image1' => 'http://example.com/image.qcow2' + } + } + + list_cmd = "glance --insecure " \ + "--os-username glance " \ + "--os-password glance-pass " \ + "--os-tenant-name service "\ + "--os-image-url http://127.0.0.1:9292 " \ + "--os-auth-url http://127.0.0.1:5000/v2.0 " \ + "image-list | grep image1" + + stub_command(list_cmd).and_return(true) + + expect(chef_run).to_not run_execute('Uploading QCOW2 image image1') + end + end +end diff --git a/chef/cookbooks/openstack-image/spec/registry-redhat_spec.rb b/chef/cookbooks/openstack-image/spec/registry-redhat_spec.rb index e855fc9..3f3c6ed 100644 --- a/chef/cookbooks/openstack-image/spec/registry-redhat_spec.rb +++ b/chef/cookbooks/openstack-image/spec/registry-redhat_spec.rb @@ -1,36 +1,52 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-image::registry" do - before { image_stubs } - describe "redhat" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @chef_run.converge "openstack-image::registry" +describe 'openstack-image::registry' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it "installs mysql python packages" do - expect(@chef_run).to install_package "MySQL-python" + include_context 'image-stubs' + + it 'converges when configured to use sqlite' do + node.set['openstack']['db']['image']['service_type'] = 'sqlite' + + expect { chef_run }.to_not raise_error end - it "installs glance packages" do - expect(@chef_run).to upgrade_package "openstack-glance" - expect(@chef_run).to upgrade_package "openstack-swift" - expect(@chef_run).to upgrade_package "cronie" + it 'does upgrades keystoneclient package' do + expect(chef_run).to upgrade_package('python-keystoneclient') end - it "starts glance registry on boot" do - expected = "openstack-glance-registry" - expect(@chef_run).to set_service_to_start_on_boot expected + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package('MySQL-python') end - it "doesn't version the database" do - opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command("glance-manage db_version", false) - chef_run.converge "openstack-image::registry" - cmd = "glance-manage version_control 0" + it 'upgrades db2 python packages if explicitly told' do + node.set['openstack']['db']['image']['service_type'] = 'db2' - expect(chef_run).not_to execute_command cmd + ['python-ibm-db', 'python-ibm-db-sa'].each do |pkg| + expect(chef_run).to upgrade_package(pkg) + end + end + + it 'upgrades glance packages' do + expect(chef_run).to upgrade_package('openstack-glance') + expect(chef_run).to upgrade_package('cronie') + end + + it 'starts glance registry on boot' do + expect(chef_run).to enable_service('openstack-glance-registry') + end + + it 'does not version the database' do + stub_command('glance-manage db_version').and_return(false) + cmd = 'glance-manage version_control 0' + + expect(chef_run).not_to run_execute(cmd) end end end diff --git a/chef/cookbooks/openstack-image/spec/registry_spec.rb b/chef/cookbooks/openstack-image/spec/registry_spec.rb index 7e9678c..59afa38 100644 --- a/chef/cookbooks/openstack-image/spec/registry_spec.rb +++ b/chef/cookbooks/openstack-image/spec/registry_spec.rb @@ -1,143 +1,177 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-image::registry" do - before { image_stubs } - describe "ubuntu" do +describe 'openstack-image::registry' do + describe 'ubuntu' do before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["image"]["syslog"]["use"] = true - end - @chef_run.converge "openstack-image::registry" + # Lame we must still stub this, since the recipe contains shell + # guards. Need to work on a way to resolve this. + stub_command('glance-manage db_version').and_return(true) end - expect_runs_openstack_common_logging_recipe - - it "doesn't run logging recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-image::registry" - - expect(chef_run).not_to include_recipe "openstack-common::logging" + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - expect_installs_python_keystone + include_context 'image-stubs' + include_examples 'common-logging-recipe' + include_examples 'common-packages' + include_examples 'cache-directory' + include_examples 'glance-directory' - expect_installs_curl + it 'converges when configured to use sqlite' do + node.set['openstack']['db']['image']['service_type'] = 'sqlite' - it "installs mysql python packages" do - expect(@chef_run).to install_package "python-mysqldb" + expect { chef_run }.to_not raise_error end - expect_installs_ubuntu_glance_packages - - expect_creates_cache_dir - - it "starts glance registry on boot" do - expect(@chef_run).to set_service_to_start_on_boot "glance-registry" + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package('python-mysqldb') end - describe "version_control" do - before { @cmd = "glance-manage version_control 0" } + it 'honors package name and option overrides for mysql python packages' do + node.set['openstack']['image']['platform']['package_overrides'] = '-o Dpkg::Options::=\'--force-confold\' -o Dpkg::Options::=\'--force-confdef\' --force-yes' + node.set['openstack']['image']['platform']['mysql_python_packages'] = ['my-mysql-py'] - it "versions the database" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command("glance-manage db_version", false) - chef_run.converge "openstack-image::registry" + expect(chef_run).to upgrade_package('my-mysql-py').with(options: '-o Dpkg::Options::=\'--force-confold\' -o Dpkg::Options::=\'--force-confdef\' --force-yes') + end - expect(chef_run).to execute_command @cmd - end + %w{db2 postgresql}.each do |service_type| + it "upgrades #{service_type} python packages if chosen" do + node.set['openstack']['db']['image']['service_type'] = service_type + node.set['openstack']['image']['platform']["#{service_type}_python_packages"] = ["my-#{service_type}-py"] - it "doesn't version when glance-manage db_version false" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.stub_command("glance-manage db_version", true) - chef_run.converge "openstack-image::registry" - - expect(chef_run).not_to execute_command @cmd + expect(chef_run).to upgrade_package("my-#{service_type}-py") end end - it "deletes glance.sqlite" do - expect(@chef_run).to delete_file "/var/lib/glance/glance.sqlite" + it 'starts glance registry on boot' do + expect(chef_run).to enable_service('glance-registry') end - expect_creates_glance_dir + describe 'version_control' do + let(:cmd) { 'glance-manage version_control 0' } - describe "glance-registry.conf" do - before do - @file = @chef_run.template "/etc/glance/glance-registry.conf" + it 'versions the database' do + stub_command('glance-manage db_version').and_return(false) + + expect(chef_run).to run_execute(cmd).with(user: 'glance', group: 'glance') end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" + it 'does not version when glance-manage db_version false' do + stub_command('glance-manage db_version').and_return(true) + + expect(chef_run).not_to run_execute(cmd) + end + end + + it 'deletes glance.sqlite' do + expect(chef_run).to delete_file('/var/lib/glance/glance.sqlite') + end + + it 'does not delete glance.sqlite when configured to use sqlite' do + node.set['openstack']['db']['image']['service_type'] = 'sqlite' + + expect(chef_run).not_to delete_file('/var/lib/glance/glance.sqlite') + end + + describe 'glance-registry.conf' do + let(:file) { chef_run.template('/etc/glance/glance-registry.conf') } + + it 'creates glance-registry.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 00644 + ) end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'has bind host when bind_interface not specified' do + match = 'bind_host = 127.0.0.1' + expect(chef_run).to render_file(file.name).with_content(match) end - it "has bind host when bind_interface not specified" do - expect(@chef_run).to create_file_with_content @file.name, - "bind_host = 127.0.0.1" + it 'has bind host when bind_interface specified' do + node.set['openstack']['endpoints']['image-registry-bind']['bind_interface'] = 'lo' + + match = 'bind_host = 127.0.1.1' + expect(chef_run).to render_file(file.name).with_content(match) end - it "has bind host when bind_interface specified" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["image"]["registry"]["bind_interface"] = "lo" + it 'notifies glance-registry restart' do + expect(file).to notify('service[glance-registry]').to(:restart) + end + + context 'keystone_authtoken' do + it 'has correct authtoken settings' do + [ + 'auth_uri = http://127.0.0.1:5000/v2.0', + 'auth_host = 127.0.0.1', + 'auth_port = 35357', + 'auth_protocol = http', + 'admin_tenant_name = service', + 'admin_user = glance', + 'admin_tenant_name = service', + 'admin_password = glance-pass', + 'signing_dir = /var/cache/glance/registry' + ].each do |line| + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote(line)}$/) + end end - chef_run.converge "openstack-image::registry" - expect(chef_run).to create_file_with_content @file.name, - "bind_host = 127.0.1.1" - end - - it "notifies image-registry restart" do - expect(@file).to notify "service[image-registry]", :restart - end - end - - describe "db_sync" do - before do - @cmd = "glance-manage db_sync" - end - - it "runs migrations" do - expect(@chef_run).to execute_command @cmd - end - - it "doesn't run migrations" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new(opts) do |n| - n.set["openstack"]["image"]["db"]["migrate"] = false + it 'has no auth_version' do + expect(chef_run).not_to render_file(file.name).with_content( + /^auth_version = v2.0$/) end - # Lame we must still stub this, since the recipe contains shell - # guards. Need to work on a way to resolve this. - chef_run.stub_command("glance-manage db_version", false) - chef_run.converge "openstack-image::registry" - expect(chef_run).not_to execute_command @cmd + it 'has signing_dir' do + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote('signing_dir = /var/cache/glance/registry')}$/) + end + + it 'has auth_version when auth version is set to v3.0' do + chef_run.node.set['openstack']['image']['registry']['auth']['version'] = 'v3.0' + expect(chef_run).to render_file(file.name).with_content( + /^auth_version = v3.0$/) + end end end - describe "glance-registry-paste.ini" do - before do - @file = @chef_run.template "/etc/glance/glance-registry-paste.ini" + describe 'db_sync' do + let(:cmd) { 'glance-manage db_sync' } + + it 'runs migrations' do + expect(chef_run).to run_execute(cmd).with(user: 'glance', group: 'glance') end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" + it 'does not run migrations when openstack/image/db/migrate is false' do + node.set['openstack']['db']['image']['migrate'] = false + stub_command('glance-manage db_version').and_return(false) + + expect(chef_run).not_to run_execute(cmd) + end + end + + describe 'glance-registry-paste.ini' do + let(:file) { chef_run.template('/etc/glance/glance-registry-paste.ini') } + + it 'creates glance-registry-paste.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 00644 + ) end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'template contents' do + pending 'TODO: implement' end - it "template contents" do - pending "TODO: implement" - end - - it "notifies image-registry restart" do - expect(@file).to notify "service[image-registry]", :restart + it 'notifies glance-registry restart' do + expect(file).to notify('service[glance-registry]').to(:restart) end end end diff --git a/chef/cookbooks/openstack-image/spec/spec_helper.rb b/chef/cookbooks/openstack-image/spec/spec_helper.rb index 2edf9c0..f9a6f3c 100644 --- a/chef/cookbooks/openstack-image/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-image/spec/spec_helper.rb @@ -1,87 +1,164 @@ -require "chefspec" +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' -::LOG_LEVEL = :fatal -::REDHAT_OPTS = { - :platform => "redhat", - :version => "6.3", - :log_level => ::LOG_LEVEL +ChefSpec::Coverage.start! { add_filter 'openstack-image' } + +require 'chef/application' + +LOG_LEVEL = :fatal +REDHAT_OPTS = { + platform: 'redhat', + version: '6.3', + log_level: LOG_LEVEL } -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } -def image_stubs - ::Chef::Recipe.any_instance.stub(:address_for). - with("lo"). - and_return "127.0.1.1" - ::Chef::Recipe.any_instance.stub(:config_by_role). - with("rabbitmq-server", "queue").and_return( - {'host' => 'rabbit-host', 'port' => 'rabbit-port'} - ) - ::Chef::Recipe.any_instance.stub(:secret). - with("secrets", "openstack_identity_bootstrap_token"). - and_return "bootstrap-token" - ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:service_password).with("openstack-image"). - and_return "glance-pass" -end +shared_context 'image-stubs' do + before do + Chef::Recipe.any_instance.stub(:address_for) + .with('lo') + .and_return('127.0.1.1') -def expect_runs_openstack_common_logging_recipe - it "runs logging recipe if node attributes say to" do - expect(@chef_run).to include_recipe "openstack-common::logging" + Chef::Recipe.any_instance.stub(:config_by_role) + .with('rabbitmq-server', 'queue') + .and_return( + 'host' => 'rabbit-host', 'port' => 'rabbit-port' + ) + + Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_identity_bootstrap_token') + .and_return('bootstrap-token') + Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_vmware_secret_name') + .and_return 'vmware_secret_name' + + Chef::Recipe.any_instance.stub(:get_password) + .with('db', anything) + .and_return('') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack-image') + .and_return('glance-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'guest') + .and_return('mq-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'rbd-image') + .and_return('rbd-pass') + + Chef::Application.stub(:fatal!) end end -def expect_creates_cache_dir - describe "/var/cache/glance" do - before do - @dir = @chef_run.directory "/var/cache/glance" +shared_examples 'common-logging-recipe' do + it 'does not include logging recipe by default' do + expect(chef_run).not_to include_recipe('openstack-common::logging') + end + + it 'includes logging recipe if openstack/image/syslog/use attribute is true' do + node.set['openstack']['image']['syslog']['use'] = true + + expect(chef_run).to include_recipe('openstack-common::logging') + end +end + +shared_examples 'common-packages' do + it 'upgrades python-keystoneclient package' do + expect(chef_run).to upgrade_package 'python-keystoneclient' + end + + it 'upgrades curl package' do + expect(chef_run).to upgrade_package 'curl' + end + + it 'upgrades glance package' do + expect(chef_run).to upgrade_package 'glance' + end + + it 'honors the platform name and option package overrides' do + node.set['openstack']['image']['platform']['package_overrides'] = '-o Dpkg::Options::=\'--force-confold\' -o Dpkg::Options::=\'--force-confdef\' --force-yes' + node.set['openstack']['image']['platform']['image_packages'] = ['my-glance'] + + expect(chef_run).to upgrade_package('my-glance').with(options: '-o Dpkg::Options::=\'--force-confold\' -o Dpkg::Options::=\'--force-confdef\' --force-yes') + end +end + +shared_examples 'cache-directory' do + describe '/var/cache/glance' do + let(:dir) { chef_run.directory('/var/cache/glance') } + + it 'has proper owner' do + expect(dir.owner).to eq('glance') + expect(dir.group).to eq('glance') end - it "has proper owner" do - expect(@dir).to be_owned_by "glance", "glance" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" + it 'has proper modes' do + expect(sprintf('%o', dir.mode)).to eq '700' end end end -def expect_installs_python_keystone - it "installs python-keystone package" do - expect(@chef_run).to install_package "python-keystone" - end -end +shared_examples 'glance-directory' do + describe '/etc/glance' do + let(:dir) { chef_run.directory('/etc/glance') } -def expect_installs_curl - it "installs curl package" do - expect(@chef_run).to install_package "curl" - end -end - -def expect_installs_ubuntu_glance_packages - it "installs glance packages" do - expect(@chef_run).to upgrade_package "glance" - expect(@chef_run).to upgrade_package "python-swift" - end -end - -def expect_creates_glance_dir - describe "/etc/glance" do - before do - @dir = @chef_run.directory "/etc/glance" + it 'has proper owner' do + expect(dir.owner).to eq('glance') + expect(dir.group).to eq('glance') end - it "has proper owner" do - expect(@dir).to be_owned_by "glance", "glance" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" + it 'has proper modes' do + expect(sprintf('%o', dir.mode)).to eq '700' + end + end +end + +shared_examples 'vmware config' do |file_name| + describe 'vmware settings' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + let(:file) { chef_run.template(file_name) } + + it 'has default vmware_* settings' do + [ + /^vmware_server_host = $/, + /^vmware_server_username = $/, + /^vmware_server_password = $/, + /^vmware_datacenter_path = $/, + /^vmware_datastore_name = $/, + /^vmware_api_retry_count = 10/, + /^vmware_task_poll_interval = 5$/, + /^vmware_store_image_dir = \/openstack_glance$/, + /^vmware_api_insecure = false$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'looks up the vmware_server_password when vmware_server_host is set' do + node.set['openstack']['image']['api']['vmware']['vmware_server_host'] = 'my-vmware' + expect(chef_run).to render_file(file.name).with_content(/^vmware_server_password = vmware_secret_name$/) + end + + it 'does set vmware settings when overridden' do + %w( + vmware_server_host + vmware_server_username + vmware_datacenter_path + vmware_datastore_name + vmware_api_retry_count + vmware_task_poll_interval + vmware_store_image_dir + vmware_api_insecure + ).each { |key| node.set['openstack']['image']['api']['vmware'][key] = "my_#{key}" } + .each do |key| + expect(chef_run).to render_file(file.name).with_content(/^#{key} = my_#{key}$/) + end end end end diff --git a/chef/cookbooks/openstack-image/templates/default/glance-api.conf.erb b/chef/cookbooks/openstack-image/templates/default/glance-api.conf.erb index 57a7651..df2be4c 100644 --- a/chef/cookbooks/openstack-image/templates/default/glance-api.conf.erb +++ b/chef/cookbooks/openstack-image/templates/default/glance-api.conf.erb @@ -9,7 +9,8 @@ debug = <%= node["openstack"]["image"]["debug"] %> # Which backend store should Glance use by default is not specified # in a request to add a new image to Glance? Default: 'file' -# Available choices are 'file', 'swift', and 's3' +# Available choices are 'file', 'swift', 's3', 'cinder', 'gridfs', +# 'http', 'sheepdog' and 'vsphere' default_store = <%= node["openstack"]["image"]["api"]["default_store"] %> # Address to bind the API server @@ -26,7 +27,7 @@ backlog = 4096 # may improve performance (especially if using SSL with # compression turned on). It is typically recommended to set # this value to the number of CPUs present on your machine. -workers = <%= node["cpu"]["total"] %> +workers = <%= node["openstack"]["image"]["api"]["workers"] %> # SQLAlchemy connection string for the reference implementation # registry server. Any valid SQLAlchemy connection string is fine. @@ -35,6 +36,13 @@ sql_connection = <%= @sql_connection %> # Role used to identify an authenticated user as administrator #admin_role = admin +# +# Return the URL that references where the data is stored on +# the backend storage system. For example, if using the +# file system store a URL of 'file:///path/to/image' will +# be returned to the user in the 'direct_url' meta-data field. +# The default value is false. +show_image_direct_url = <%= node["openstack"]["image"]["api"]["show_image_direct_url"] %> # ================= Syslog Options ============================ @@ -95,43 +103,49 @@ registry_client_protocol = http # There are three methods of sending notifications, logging (via the # log_file directive), rabbit (via a rabbitmq queue), qpid (via a Qpid # message queue), or noop (no notifications sent, the default) -notifier_strategy = noop +notifier_strategy = default +notification_driver = <%= @notification_driver %> -# Configuration options if sending notifications via rabbitmq (these are -# the defaults) -rabbit_host = <%= node['openstack']['mq']['bind_address'] %> -rabbit_port = <%= node['openstack']['mq']['port'] %> +<% if @notification_driver == "messaging" and @mq_service_type == "rabbitmq" %> +#### RABBITMQ ##### +transport_url = rabbit:// +rabbit_host = <%= node["openstack"]["mq"]["image"]["rabbit"]["host"] %> +rabbit_port = <%= node["openstack"]["mq"]["image"]["rabbit"]["port"] %> rabbit_use_ssl = false -rabbit_userid = <%= node['openstack']['mq']['user'] %> -rabbit_password = <%= node['openstack']['mq']['password'] %> -rabbit_virtual_host = / +rabbit_userid = <%= node["openstack"]["mq"]["image"]["rabbit"]["userid"] %> +rabbit_password = <%= @mq_password %> +rabbit_virtual_host = <%= node["openstack"]["mq"]["image"]["rabbit"]["vhost"] %> rabbit_notification_exchange = glance -rabbit_notification_topic = glance_notifications +rabbit_notification_topic = <%= node["openstack"]["mq"]["image"]["rabbit"]["notification_topic"] %> +<% elsif @notification_driver == "messaging" and @mq_service_type == "qpid" %> +##### QPID ##### +transport_url = qpid:// +qpid_notification_exchange = glance +qpid_notification_topic = <%= node["openstack"]["mq"]["image"]["qpid"]["notification_topic"] %> -# Configuration options if sending notifications via Qpid (these are -# the defaults) -# qpid_notification_exchange = glance -# qpid_notification_topic = glance_notifications -# qpid_host = localhost -# qpid_port = 5672 -# qpid_username = -# qpid_password = -# qpid_sasl_mechanisms = -# qpid_reconnect_timeout = 0 -# qpid_reconnect_limit = 0 -# qpid_reconnect_interval_min = 0 -# qpid_reconnect_interval_max = 0 -# qpid_reconnect_interval = 0 -# qpid_heartbeat = 5 -# Set to 'ssl' to enable SSL -# qpid_protocol = tcp -# qpid_tcp_nodelay = True +qpid_hostname=<%= node["openstack"]["mq"]["image"]["qpid"]["host"] %> +qpid_port=<%= node["openstack"]["mq"]["image"]["qpid"]["port"] %> + +qpid_password=<%= @mq_password %> +qpid_username=<%= node["openstack"]["mq"]["image"]["qpid"]["username"] %> +qpid_sasl_mechanisms=<%= node["openstack"]["mq"]["image"]["qpid"]["sasl_mechanisms"] %> +qpid_reconnect=<%= node["openstack"]["mq"]["image"]["qpid"]["reconnect"] %> +qpid_reconnect_timeout=<%= node["openstack"]["mq"]["image"]["qpid"]["reconnect_timeout"] %> +qpid_reconnect_limit=<%= node["openstack"]["mq"]["image"]["qpid"]["reconnect_limit"] %> +qpid_reconnect_interval_min=<%= node["openstack"]["mq"]["image"]["qpid"]["reconnect_interval_min"] %> +qpid_reconnect_interval_max=<%= node["openstack"]["mq"]["image"]["qpid"]["reconnect_interval_max"] %> +qpid_reconnect_interval=<%= node["openstack"]["mq"]["image"]["qpid"]["reconnect_interval"] %> +qpid_heartbeat=<%= node["openstack"]["mq"]["image"]["qpid"]["heartbeat"] %> +# qpid protocol. default 'tcp'. set to 'ssl' to enable SSL +qpid_protocol=<%= node["openstack"]["mq"]["image"]["qpid"]["protocol"] %> +qpid_tcp_nodelay=<%= node["openstack"]["mq"]["image"]["qpid"]["tcp_nodelay"] %> +<% end %> # ============ Filesystem Store Options ======================== # Directory that the Filesystem backend store # writes image data to -filesystem_store_datadir = /var/lib/glance/images/ +filesystem_store_datadir = <%= node["openstack"]["image"]["filesystem_store_datadir"] %> # ============ Swift Store Options ============================= @@ -139,11 +153,11 @@ filesystem_store_datadir = /var/lib/glance/images/ # Address where the Swift authentication service lives # Valid schemes are 'http://' and 'https://' # If no scheme specified, default to 'https://' -#swift_store_auth_address = <%= @swift_store_auth_address %> +swift_store_auth_address = <%= @swift_store_auth_address %> # Authentication version to use. Current Rackspace CloudFiles supports # Version 1 while swift backed with keystone supports Version 2. -#swift_store_auth_version = <%= @swift_store_auth_version %> +swift_store_auth_version = <%= @swift_store_auth_version %> # User to authenticate against the Swift authentication service # If you use Swift authentication service, set it to 'account':'user' @@ -153,25 +167,25 @@ swift_store_user = <%= @swift_user_tenant %>:<%= @swift_store_user %> # Auth key for the user authenticating against the # Swift authentication service -#swift_store_key = <%= @swift_store_key %> +swift_store_key = <%= @swift_store_key %> # Container within the account that the account should use # for storing images in Swift -#swift_store_container = <%= node["openstack"]["image"]["api"]["swift"]["container"] %> +swift_store_container = <%= node["openstack"]["image"]["api"]["swift"]["container"] %> # Do we create the container if it does not exist? -#swift_store_create_container_on_put = True +swift_store_create_container_on_put = True # What size, in MB, should Glance start chunking image files # and do a large object manifest in Swift? By default, this is # the maximum object size in Swift, which is 5GB -#swift_store_large_object_size = <%= node["openstack"]["image"]["api"]["swift"]["large_object_size"] %> +swift_store_large_object_size = <%= node["openstack"]["image"]["api"]["swift"]["large_object_size"] %> # When doing a large object manifest, what size, in MB, should # Glance write chunks to Swift? This amount of data is written # to a temporary disk buffer during the process of chunking # the image file, and the default is 200MB -#swift_store_large_object_chunk_size = <%= node["openstack"]["image"]["api"]["swift"]["large_object_chunk_size"] %> +swift_store_large_object_chunk_size = <%= node["openstack"]["image"]["api"]["swift"]["large_object_chunk_size"] %> # Whether to use ServiceNET to communicate with the Swift storage servers. # (If you aren't RACKSPACE, leave this False!) @@ -179,21 +193,24 @@ swift_store_user = <%= @swift_user_tenant %>:<%= @swift_store_user %> # To use ServiceNET for authentication, prefix hostname of # `swift_store_auth_address` with 'snet-'. # Ex. https://example.com/v1.0/ -> https://snet-example.com/v1.0/ -#swift_enable_snet = False +swift_enable_snet = <%= node["openstack"]["image"]["api"]["swift"]["enable_snet"] %> +<% unless node["openstack"]["image"]["api"]["swift"]["store_region"].nil? %> +swift_store_region = <%= node["openstack"]["image"]["api"]["swift"]["store_region"] %> +<% end %> # ============ S3 Store Options ============================= # Address where the S3 authentication service lives # Valid schemes are 'http://' and 'https://' # If no scheme specified, default to 'http://' -#s3_store_host = 127.0.0.1:8080/v1.0/ +s3_store_host = 127.0.0.1:8080/v1.0/ # User to authenticate against the S3 authentication service -#s3_store_access_key = <20-char AWS access key> +s3_store_access_key = <20-char AWS access key> # Auth key for the user authenticating against the # S3 authentication service -#s3_store_secret_key = <40-char AWS secret key> +s3_store_secret_key = <40-char AWS secret key> # Container within the account that the account should use # for storing images in S3. Note that S3 has a flat namespace, @@ -201,10 +218,10 @@ swift_store_user = <%= @swift_user_tenant %>:<%= @swift_store_user %> # easy way to do this is append your AWS access key to "glance". # S3 buckets in AWS *must* be lowercased, so remember to lowercase # your AWS access key if you use it in your bucket name below! -#s3_store_bucket = glance +s3_store_bucket = glance # Do we create the bucket if it does not exist? -#s3_store_create_bucket_on_put = False +s3_store_create_bucket_on_put = False # When sending images to S3, the data will first be written to a # temporary buffer on disk. By default the platform's temporary directory @@ -217,17 +234,53 @@ swift_store_user = <%= @swift_user_tenant %>:<%= @swift_store_user %> # If using cephx authentication, this file should # include a reference to the right keyring # in a client. section -#rbd_store_ceph_conf = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_ceph_conf"] %> +rbd_store_ceph_conf = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_ceph_conf"] %> # RADOS user to authenticate as (only applicable if using cephx) -#rbd_store_user = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_user"] %> +rbd_store_user = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_user"] %> # RADOS pool in which images are stored -#rbd_store_pool = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_pool"] %> +rbd_store_pool = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_pool"] %> # Images will be chunked into objects of this size (in megabytes). # For best performance, this should be a power of two -#rbd_store_chunk_size = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_chunk_size"] %> +rbd_store_chunk_size = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_chunk_size"] %> + +# ============ VMware Datastore Store Options ===================== + +# ESX/ESXi or vCenter Server target system. +# The server value can be an IP address or a DNS name +# e.g. 127.0.0.1, 127.0.0.1:443, www.vmware-infra.com +vmware_server_host = <%= node['openstack']['image']['api']['vmware']['vmware_server_host'] %> + +# Server username (string value) +vmware_server_username = <%= node['openstack']['image']['api']['vmware']['vmware_server_username'] %> + +# Server password (string value) +vmware_server_password = <%= @vmware_server_password %> + +# Inventory path to a datacenter (string value) +# Value optional when vmware_server_ip is an ESX/ESXi host: if specified +# should be `ha-datacenter`. +vmware_datacenter_path = <%= node['openstack']['image']['api']['vmware']['vmware_datacenter_path'] %> + +# Datastore associated with the datacenter (string value) +vmware_datastore_name = <%= node['openstack']['image']['api']['vmware']['vmware_datastore_name'] %> + +# The number of times we retry on failures +# e.g., socket error, etc (integer value) +vmware_api_retry_count = <%= node['openstack']['image']['api']['vmware']['vmware_api_retry_count'] %> + +# The interval used for polling remote tasks +# invoked on VMware ESX/VC server in seconds (integer value) +vmware_task_poll_interval = <%= node['openstack']['image']['api']['vmware']['vmware_task_poll_interval'] %> + +# Absolute path of the folder containing the images in the datastore +# (string value) +vmware_store_image_dir = <%= node['openstack']['image']['api']['vmware']['vmware_store_image_dir'] %> + +# Allow to perform insecure SSL requests to the target system (boolean value) +vmware_api_insecure = <%= node['openstack']['image']['api']['vmware']['vmware_api_insecure'] %> # ============ Delayed Delete Options ============================= @@ -254,10 +307,14 @@ auth_protocol = <%= @identity_admin_endpoint.scheme %> <% if node["openstack"]["image"]["api"]["auth"]["version"] != "v2.0" %> auth_version = <%= node["openstack"]["image"]["api"]["auth"]["version"] %> <% end %> -admin_tenant_name = <%= @service_tenant_name %> -admin_user = <%= @service_user %> +admin_tenant_name = <%= node["openstack"]["image"]["service_tenant_name"] %> +admin_user = <%= node["openstack"]["image"]["service_user"] %> admin_password = <%= @service_pass %> signing_dir = <%= node["openstack"]["image"]["api"]["auth"]["cache_dir"] %> [paste_deploy] +# Partial name of a pipeline in your paste configuration file with the +# service name removed. For example, if your paste section name is +# [pipeline:glance-api-keystone], you would configure the flavor below +# as 'keystone'. flavor = <%= @glance_flavor %> diff --git a/chef/cookbooks/openstack-image/templates/default/glance-cache.conf.erb b/chef/cookbooks/openstack-image/templates/default/glance-cache.conf.erb index 0b24667..96d0922 100644 --- a/chef/cookbooks/openstack-image/templates/default/glance-cache.conf.erb +++ b/chef/cookbooks/openstack-image/templates/default/glance-cache.conf.erb @@ -10,11 +10,11 @@ debug = <%= node["openstack"]["image"]["debug"] %> # =============== Image Cache Options ============================= # Directory that the Image Cache writes data to -image_cache_dir = /var/lib/glance/image-cache/ +image_cache_dir = <%= node["openstack"]["image"]["cache"]["dir"] %> # Number of seconds after which we should consider an incomplete image to be # stalled and eligible for reaping -image_cache_stall_time = 86400 +image_cache_stall_time = <%= node["openstack"]["image"]["cache"]["stall_time"] %> # image_cache_invalid_entry_grace_period - seconds # @@ -24,7 +24,7 @@ image_cache_stall_time = 86400 # # This is number of seconds to leave these invalid images around before they # are elibible to be reaped. -image_cache_invalid_entry_grace_period = 3600 +image_cache_invalid_entry_grace_period = <%= node["openstack"]["image"]["cache"]["grace_period"] %> # Max cache size in bytes image_cache_max_size = <%= node["openstack"]["image"]["api"]["cache"]["image_cache_max_size"] %> @@ -51,6 +51,42 @@ registry_port = <%= @registry_port %> # admin_user = %SERVICE_USER% # admin_password = %SERVICE_PASSWORD% +# ============ VMware Datastore Store Options ===================== + +# ESX/ESXi or vCenter Server target system. +# The server value can be an IP address or a DNS name +# e.g. 127.0.0.1, 127.0.0.1:443, www.vmware-infra.com +vmware_server_host = <%= node['openstack']['image']['api']['vmware']['vmware_server_host'] %> + +# Server username (string value) +vmware_server_username = <%= node['openstack']['image']['api']['vmware']['vmware_server_username'] %> + +# Server password (string value) +vmware_server_password = <%= @vmware_server_password %> + +# Inventory path to a datacenter (string value) +# Value optional when vmware_server_ip is an ESX/ESXi host: if specified +# should be `ha-datacenter`. +vmware_datacenter_path = <%= node['openstack']['image']['api']['vmware']['vmware_datacenter_path'] %> + +# Datastore associated with the datacenter (string value) +vmware_datastore_name = <%= node['openstack']['image']['api']['vmware']['vmware_datastore_name'] %> + +# The number of times we retry on failures +# e.g., socket error, etc (integer value) +vmware_api_retry_count = <%= node['openstack']['image']['api']['vmware']['vmware_api_retry_count'] %> + +# The interval used for polling remote tasks +# invoked on VMware ESX/VC server in seconds (integer value) +vmware_task_poll_interval = <%= node['openstack']['image']['api']['vmware']['vmware_task_poll_interval'] %> + +# Absolute path of the folder containing the images in the datastore +# (string value) +vmware_store_image_dir = <%= node['openstack']['image']['api']['vmware']['vmware_store_image_dir'] %> + +# Allow to perform insecure SSL requests to the target system (boolean value) +vmware_api_insecure = <%= node['openstack']['image']['api']['vmware']['vmware_api_insecure'] %> + # ================= Security Options ========================== # AES key for encrypting store 'location' metadata, including diff --git a/chef/cookbooks/openstack-image/templates/default/glance-registry.conf.erb b/chef/cookbooks/openstack-image/templates/default/glance-registry.conf.erb index b2e911b..e45bf0d 100644 --- a/chef/cookbooks/openstack-image/templates/default/glance-registry.conf.erb +++ b/chef/cookbooks/openstack-image/templates/default/glance-registry.conf.erb @@ -11,7 +11,7 @@ debug = <%= node["openstack"]["image"]["debug"] %> bind_host = <%= @registry_bind_address %> # Port the bind the registry server to -bind_port = <%= @registry_port %> +bind_port = <%= @registry_bind_port %> # Backlog requests when creating socket backlog = 4096 @@ -62,11 +62,15 @@ log_file = /var/log/glance/registry.log # ================= Keystone authtoken =============================== [keystone_authtoken] -auth_host = <%= @identity_endpoint.host %> -auth_port = <%= @identity_endpoint.port %> -auth_protocol = <%= @identity_endpoint.scheme %> -admin_tenant_name = <%= @service_tenant_name %> -admin_user = <%= @service_user %> +auth_uri = <%= @auth_uri %> +auth_host = <%= @identity_admin_endpoint.host %> +auth_port = <%= @identity_admin_endpoint.port %> +auth_protocol = <%= @identity_admin_endpoint.scheme %> +<% if node['openstack']['image']['registry']['auth']['version'] != 'v2.0' %> +auth_version = <%= node['openstack']['image']['registry']['auth']['version'] %> +<% end %> +admin_tenant_name = <%= node["openstack"]["image"]["service_tenant_name"] %> +admin_user = <%= node["openstack"]["image"]["service_user"] %> admin_password = <%= @service_pass %> signing_dir = <%= node["openstack"]["image"]["registry"]["auth"]["cache_dir"] %> diff --git a/chef/cookbooks/openstack-image/templates/default/policy.json.erb b/chef/cookbooks/openstack-image/templates/default/policy.json.erb deleted file mode 100644 index 9e373ab..0000000 --- a/chef/cookbooks/openstack-image/templates/default/policy.json.erb +++ /dev/null @@ -1,4 +0,0 @@ -{ - "default": [], - "manage_image_cache": [["role:admin"]] -} diff --git a/chef/cookbooks/openstack-image/templates/default/tinyimage.sh.erb b/chef/cookbooks/openstack-image/templates/default/tinyimage.sh.erb deleted file mode 100644 index 3274c67..0000000 --- a/chef/cookbooks/openstack-image/templates/default/tinyimage.sh.erb +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -OS_USERNAME=<%= @os_username %> -OS_PASSWORD=<%= @os_password %> -OS_TENANT_NAME=<%= @os_tenant_name %> -OS_AUTH_URL=<%= @os_auth_url %> - -IMAGEDIR=/tmp/images - -glance --os-username=<%= @os_username %> \ - --os-password=<%= @os_password %> \ - --os-tenant-name=<%= @os_tenant_name %> \ - --os-auth-url=<%= @os_auth_url %> \ - image-list |grep "cirros-0.3.1" - -if [ $? -ne 0 ]; then - if [ -d "$IMAGEDIR" ]; then - cd $IMAGEDIR - glance --os-username=<%= @os_username %> \ - --os-password=<%= @os_password %> \ - --os-tenant-name=<%= @os_tenant_name %> \ - --os-auth-url=<%= @os_auth_url %> \ - image-create \ - --name="cirros-0.3.1-x86_64" \ - --disk-format=qcow2 \ - --container-format bare < $IMAGEDIR/cirros-0.3.1-x86_64-disk.img - - glance --os-username=<%= @os_username %> \ - --os-password=<%= @os_password %> \ - --os-tenant-name=<%= @os_tenant_name %> \ - --os-auth-url=<%= @os_auth_url %> \ - image-create \ - --name="cirros-0.3.1-i386" \ - --disk-format=qcow2 \ - --container-format bare < $IMAGEDIR/cirros-0.3.1-i386-disk.img - fi -fi - diff --git a/chef/cookbooks/openstack-metering/Berksfile.lock b/chef/cookbooks/openstack-metering/Berksfile.lock deleted file mode 100644 index 086f87c..0000000 --- a/chef/cookbooks/openstack-metering/Berksfile.lock +++ /dev/null @@ -1,41 +0,0 @@ -{ - "sources": { - "openstack-metering": { - "path": "." - }, - "openstack-common": { - "locked_version": "0.4.3", - "git": "git://github.com/stackforge/cookbook-openstack-common.git", - "ref": "eb5eed7126b6a6efbaf803e8a594d610cf661e97" - }, - "openstack-identity": { - "locked_version": "7.0.0", - "git": "git://github.com/stackforge/cookbook-openstack-identity.git", - "ref": "b881af26095cfa869a6970067c49597a0ee63586" - }, - "apt": { - "locked_version": "2.0.0" - }, - "database": { - "locked_version": "1.4.0" - }, - "mysql": { - "locked_version": "3.0.2" - }, - "openssl": { - "locked_version": "1.0.2" - }, - "build-essential": { - "locked_version": "1.4.0" - }, - "postgresql": { - "locked_version": "3.0.2" - }, - "aws": { - "locked_version": "0.101.2" - }, - "xfs": { - "locked_version": "1.1.0" - } - } -} diff --git a/chef/cookbooks/openstack-metering/CHANGELOG.md b/chef/cookbooks/openstack-metering/CHANGELOG.md deleted file mode 100644 index 119e1c7..0000000 --- a/chef/cookbooks/openstack-metering/CHANGELOG.md +++ /dev/null @@ -1,19 +0,0 @@ -openstack-metering Cookbook CHANGELOG -============================== -This file is used to list changes made in each version of the openstack-metering cookbook. - - -v7.0.3 ------- -### Bug -- Ubuntu cloud archive dpkg failing to install init script properly for agent-compute - -v7.0.2 ------- -### Improvement -- Add optional host to the ceilometer.conf - -v7.0.1 ------- -### Bug -- Fix naming inconsistency for db password databag. This makes the metering cookbook consistent with all the others. diff --git a/chef/cookbooks/openstack-metering/Gemfile b/chef/cookbooks/openstack-metering/Gemfile deleted file mode 100644 index 04ef97e..0000000 --- a/chef/cookbooks/openstack-metering/Gemfile +++ /dev/null @@ -1,9 +0,0 @@ -source "https://rubygems.org" - -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 2.0.3" -gem "chefspec", "~> 1.3.0" -gem "foodcritic" -gem "strainer" -gem "tailor" diff --git a/chef/cookbooks/openstack-metering/README.md b/chef/cookbooks/openstack-metering/README.md deleted file mode 100644 index 455e47a..0000000 --- a/chef/cookbooks/openstack-metering/README.md +++ /dev/null @@ -1,81 +0,0 @@ -Description -=========== - -Installs the OpenStack Metering service **Ceilometer** as part of the OpenStack -reference deployment Chef for OpenStack. Ceilometer is currently installed -from packages. - -https://wiki.openstack.org/wiki/Ceilometer - -Requirements -============ - -Cookbooks ---------- - -Usage -===== - -agent-central ----- -- Installs agent central service. - -agent-compute ----- -- Installs agent compute service. - -api ----- -- Installs API service. - -collector ----- -- Installs nova network service. - -common ----- -- Common metering configuration. - -identity_registration ----- -- Registers the endpoints with Keystone. - -Attributes -========== - -Testing -===== - -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. - -Tests are defined in Strainerfile. - -To run tests: - - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests - -License and Author -================== - -| | | -|:---------------------|:---------------------------------------------------| -| **Author** | Matt Ray () | -| **Author** | John Dewey () | -| | | -| **Copyright** | Copyright (c) 2013, Opscode, Inc. | -| **Copyright** | Copyright (c) 2013, AT&T Services, Inc. | - - -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. diff --git a/chef/cookbooks/openstack-metering/attributes/default.rb b/chef/cookbooks/openstack-metering/attributes/default.rb deleted file mode 100644 index 90185c2..0000000 --- a/chef/cookbooks/openstack-metering/attributes/default.rb +++ /dev/null @@ -1,73 +0,0 @@ -# -# Cookbook Name:: openstack-metering -# Recipe:: default -# -# Copyright 2013, AT&T Services, Inc. -# Copyright 2013, SUSE Linux GmbH -# -# 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. -# - -# The name of the Chef role that knows about the message queue server -# that Nova uses -default["openstack"]["metering"]["rabbit_server_chef_role"] = "os-ops-messaging" - -# This user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# user_password routine. You are expected to create -# the user, pass, vhost in a wrapper rabbitmq cookbook. -default["openstack"]["metering"]["rabbit"]["username"] = "guest" -default["openstack"]["metering"]["rabbit"]["vhost"] = "/" -default["openstack"]["metering"]["rabbit"]["port"] = 5672 -default["openstack"]["metering"]["rabbit"]["host"] = "127.0.0.1" -default["openstack"]["metering"]["rabbit"]["ha"] = false - -default["openstack"]["metering"]["conf_dir"] = "/etc/ceilometer" -default["openstack"]["metering"]["conf"] = ::File.join(node["openstack"]["metering"]["conf_dir"], "ceilometer.conf") -default["openstack"]["metering"]["db"]["username"] = "ceilometer" -default["openstack"]["metering"]["periodic_interval"] = 600 -default["openstack"]["metering"]["syslog"]["use"] = false - -default["openstack"]["metering"]["api"]["auth"]["cache_dir"] = "/var/cache/ceilometer/api" - -default["openstack"]["metering"]["user"] = "ceilometer" -default["openstack"]["metering"]["group"] = "ceilometer" - -default["openstack"]["metering"]["region"] = "RegionOne" - -case platform -when "suse" # :pragma-foodcritic: ~FC024 - won't fix this - default["openstack"]["metering"]["platform"] = { - "common_packages" => ["openstack-ceilometer"], - "agent_central_packages" => ["openstack-ceilometer-agent-central"], - "agent_central_service" => "openstack-ceilometer-agent-central", - "agent_compute_packages" => ["openstack-ceilometer-agent-compute"], - "agent_compute_service" => "openstack-ceilometer-agent-compute", - "api_packages" => ["openstack-ceilometer-api"], - "api_service" => "openstack-ceilometer-api", - "collector_packages" => ["openstack-ceilometer-collector"], - "collector_service" => "openstack-ceilometer-collector" - } -when "ubuntu" - default["openstack"]["metering"]["platform"] = { - "common_packages" => ["ceilometer-common"], - "agent_central_packages" => ["ceilometer-agent-central"], - "agent_central_service" => "ceilometer-agent-central", - "agent_compute_packages" => ["ceilometer-agent-compute"], - "agent_compute_service" => "ceilometer-agent-compute", - "api_packages" => ["ceilometer-api"], - "api_service" => "ceilometer-api", - "collector_packages" => ["ceilometer-collector"], - "collector_service" => "ceilometer-collector" - } -end diff --git a/chef/cookbooks/openstack-metering/files/default/policy.json b/chef/cookbooks/openstack-metering/files/default/policy.json deleted file mode 100644 index 373c568..0000000 --- a/chef/cookbooks/openstack-metering/files/default/policy.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "context_is_admin": [["role:admin"]] -} diff --git a/chef/cookbooks/openstack-metering/metadata.rb b/chef/cookbooks/openstack-metering/metadata.rb deleted file mode 100644 index fec5380..0000000 --- a/chef/cookbooks/openstack-metering/metadata.rb +++ /dev/null @@ -1,21 +0,0 @@ -name "openstack-metering" -maintainer "AT&T Services, Inc." -maintainer_email "cookbooks@lists.tfoundry.com" -license "Apache 2.0" -description "The OpenStack Metering service Ceilometer." -long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "7.0.3" - -recipe "openstack-metering::agent-central", "Installs agent central service." -recipe "openstack-metering::agent-compute", "Installs agent compute service." -recipe "openstack-metering::api", "Installs API service." -recipe "openstack-metering::collector", "Installs nova network service." -recipe "openstack-metering::common", "Common metering configuration." -recipe "openstack-metering::identity_registration", "Registers the endpoints with Keystone" - -%w{ ubuntu suse }.each do |os| - supports os -end - -depends "openstack-common", "~> 0.4.0" -depends "openstack-identity", "~> 7.0.0" diff --git a/chef/cookbooks/openstack-metering/recipes/collector.rb b/chef/cookbooks/openstack-metering/recipes/collector.rb deleted file mode 100644 index 7b068e0..0000000 --- a/chef/cookbooks/openstack-metering/recipes/collector.rb +++ /dev/null @@ -1,37 +0,0 @@ -# -# Cookbook Name:: openstack-metering -# Recipe:: collector -# -# Copyright 2013, AT&T Services, Inc. -# Copyright 2013, Craig Tracey -# Copyright 2013, SUSE Linux GmbH -# -# 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. -# - -include_recipe "openstack-metering::common" - -conf_switch = "--config-file #{node["openstack"]["metering"]["conf"]}" - -execute "database migration" do - command "ceilometer-dbsync #{conf_switch}" -end - -platform = node["openstack"]["metering"]["platform"] -platform["collector_packages"].each do |pkg| - package pkg -end - -service platform["collector_service"] do - action :start -end diff --git a/chef/cookbooks/openstack-metering/recipes/common.rb b/chef/cookbooks/openstack-metering/recipes/common.rb deleted file mode 100644 index 3232e50..0000000 --- a/chef/cookbooks/openstack-metering/recipes/common.rb +++ /dev/null @@ -1,89 +0,0 @@ -# -# Cookbook Name:: openstack-metering -# Recipe:: common -# -# Copyright 2013, AT&T Services, Inc. -# Copyright 2013, Craig Tracey -# Copyright 2013, SUSE Linux GmbH -# -# 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. -# - -class ::Chef::Recipe - include ::Openstack -end - -if node["openstack"]["metering"]["syslog"]["use"] - include_recipe "openstack-common::logging" -end - -platform = node["openstack"]["metering"]["platform"] -platform["common_packages"].each do |pkg| - package pkg -end - -rabbit_pass = user_password node["openstack"]["metering"]["rabbit"]["username"] - -db_info = db "metering" -db_user = node["openstack"]["metering"]["db"]["username"] -db_pass = db_password "ceilometer" -db_query = db_info["db_type"] == "mysql" ? "?charset=utf8" : "" -db_uri = db_uri("metering", db_user, db_pass).to_s + db_query - -#service_user = node["openstack"]["metering"]["service_user"] -#service_pass = service_password "openstack-compute" -#service_tenant = node["openstack"]["metering"]["service_tenant_name"] - -service_user = node['openstack']['identity']['metering']['username'] -service_pass = service_password node['openstack']['identity']['metering']['password'] -service_tenant = node['openstack']['identity']['metering']['tenant'] - -identity_endpoint = endpoint "identity-api" -image_endpoint = endpoint "image-api" - -Chef::Log.debug("openstack-metering::common:service_user|#{service_user}") -Chef::Log.debug("openstack-metering::common:service_tenant|#{service_tenant}") -Chef::Log.debug("openstack-metering::common:identity_endpoint|#{identity_endpoint.to_s}") - -directory node["openstack"]["metering"]["conf_dir"] do - owner node["openstack"]["metering"]["user"] - group node["openstack"]["metering"]["group"] - mode 00750 - - action :create -end - -template node["openstack"]["metering"]["conf"] do - source "ceilometer.conf.erb" - owner node["openstack"]["metering"]["user"] - group node["openstack"]["metering"]["group"] - mode 00640 - - variables( - :auth_uri => ::URI.decode(identity_endpoint.to_s), - :database_connection => db_uri, - :image_endpoint => image_endpoint, - :identity_endpoint => identity_endpoint, - :rabbit_pass => rabbit_pass, - :service_pass => service_pass, - :service_tenant_name => service_tenant, - :service_user => service_user - ) -end - -cookbook_file "/etc/ceilometer/policy.json" do - source "policy.json" - mode 00640 - owner node["openstack"]["metering"]["user"] - group node["openstack"]["metering"]["group"] -end diff --git a/chef/cookbooks/openstack-metering/recipes/identity_registration.rb b/chef/cookbooks/openstack-metering/recipes/identity_registration.rb deleted file mode 100644 index e37f4cf..0000000 --- a/chef/cookbooks/openstack-metering/recipes/identity_registration.rb +++ /dev/null @@ -1,51 +0,0 @@ -# -# Cookbook Name:: openstack-metering -# Recipe:: identity_registration -# -# Copyright 2013, AT&T Services, Inc. -# -# 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. -# - -require "uri" - -class ::Chef::Recipe - include ::Openstack -end - -api_endpoint = endpoint "metering-api" -identity_admin_endpoint = endpoint "identity-admin" -bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" -auth_uri = ::URI.decode identity_admin_endpoint.to_s - -openstack_identity_register "Register Metering Service" do - auth_uri auth_uri - bootstrap_token bootstrap_token - service_name "ceilometer" - service_type "metering" - service_description "Ceilometer Service" - - action :create_service -end - -openstack_identity_register "Register Metering Endpoint" do - auth_uri auth_uri - bootstrap_token bootstrap_token - service_type "metering" - endpoint_region node["openstack"]["metering"]["region"] - endpoint_adminurl ::URI.decode api_endpoint.to_s - endpoint_internalurl ::URI.decode api_endpoint.to_s - endpoint_publicurl ::URI.decode api_endpoint.to_s - - action :create_endpoint -end diff --git a/chef/cookbooks/openstack-metering/spec/agent-central-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/agent-central-opensuse_spec.rb deleted file mode 100644 index e8607f4..0000000 --- a/chef/cookbooks/openstack-metering/spec/agent-central-opensuse_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::agent-central" do - before { metering_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-metering::agent-central" - end - - it "installs the agent-central package" do - expect(@chef_run).to install_package "openstack-ceilometer-agent-central" - end - - it "starts the agent-central service" do - expect(@chef_run).to start_service "openstack-ceilometer-agent-central" - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/agent-central_spec.rb b/chef/cookbooks/openstack-metering/spec/agent-central_spec.rb deleted file mode 100644 index 39e995a..0000000 --- a/chef/cookbooks/openstack-metering/spec/agent-central_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::agent-central" do - before { metering_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-metering::agent-central" - end - - expect_runs_common_recipe - - it "installs the agent-central package" do - expect(@chef_run).to install_package "ceilometer-agent-central" - end - - it "starts agent-central service" do - expect(@chef_run).to start_service("ceilometer-agent-central") - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/agent-compute-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/agent-compute-opensuse_spec.rb deleted file mode 100644 index e38ddad..0000000 --- a/chef/cookbooks/openstack-metering/spec/agent-compute-opensuse_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::agent-compute" do - before { metering_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-metering::agent-compute" - end - - it "installs the agent-compute package" do - expect(@chef_run).to install_package "openstack-ceilometer-agent-compute" - end - - it "starts the agent-compute service" do - expect(@chef_run).to start_service "openstack-ceilometer-agent-compute" - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/agent-compute_spec.rb b/chef/cookbooks/openstack-metering/spec/agent-compute_spec.rb deleted file mode 100644 index e6f9a3a..0000000 --- a/chef/cookbooks/openstack-metering/spec/agent-compute_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::agent-compute" do - before { metering_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-metering::agent-compute" - end - - expect_runs_common_recipe - - it "installs the agent-compute package" do - expect(@chef_run).to install_package "ceilometer-agent-compute" - end - - it "starts ceilometer-agent-compute service" do - expect(@chef_run).to start_service("ceilometer-agent-compute") - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/api-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/api-opensuse_spec.rb deleted file mode 100644 index 7f65d70..0000000 --- a/chef/cookbooks/openstack-metering/spec/api-opensuse_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::api" do - before { metering_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-metering::api" - end - - it "installs the api package" do - expect(@chef_run).to install_package("openstack-ceilometer-api") - end - - it "starts api service" do - expect(@chef_run).to start_service("openstack-ceilometer-api") - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/api_spec.rb b/chef/cookbooks/openstack-metering/spec/api_spec.rb deleted file mode 100644 index 9399b12..0000000 --- a/chef/cookbooks/openstack-metering/spec/api_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::api" do - before { metering_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-metering::api" - end - - expect_runs_common_recipe - - describe "/var/cache/ceilometer" do - before do - @dir = @chef_run.directory "/var/cache/ceilometer" - end - - it "has proper owner" do - expect(@dir).to be_owned_by "ceilometer", "ceilometer" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "700" - end - end - - it "starts api service" do - expect(@chef_run).to start_service("ceilometer-api") - end - - it "starts api service" do - expect(@chef_run).to start_service("ceilometer-api") - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/collector-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/collector-opensuse_spec.rb deleted file mode 100644 index 14aac9d..0000000 --- a/chef/cookbooks/openstack-metering/spec/collector-opensuse_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::collector" do - before { metering_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-metering::collector" - end - - it "installs the collector package" do - expect(@chef_run).to install_package "openstack-ceilometer-collector" - end - - it "starts the collector service" do - expect(@chef_run).to start_service "openstack-ceilometer-collector" - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/collector_spec.rb b/chef/cookbooks/openstack-metering/spec/collector_spec.rb deleted file mode 100644 index 1655d42..0000000 --- a/chef/cookbooks/openstack-metering/spec/collector_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::collector" do - before { metering_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-metering::collector" - end - - expect_runs_common_recipe - - it "executes ceilometer dbsync" do - command = "ceilometer-dbsync --config-file /etc/ceilometer/ceilometer.conf" - expect(@chef_run).to execute_command command - end - - it "starts collector service" do - expect(@chef_run).to start_service("ceilometer-collector") - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/common-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/common-opensuse_spec.rb deleted file mode 100644 index c3ae818..0000000 --- a/chef/cookbooks/openstack-metering/spec/common-opensuse_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::common" do - before { metering_stubs } - describe "opensuse" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-metering::common" - end - - it "installs the common package" do - expect(@chef_run).to install_package "openstack-ceilometer" - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/common_spec.rb b/chef/cookbooks/openstack-metering/spec/common_spec.rb deleted file mode 100644 index 4d7c3fe..0000000 --- a/chef/cookbooks/openstack-metering/spec/common_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::common" do - before { metering_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| - n.set["openstack"]["metering"]["syslog"]["use"] = true - end - @chef_run.converge "openstack-metering::common" - end - - it "runs logging recipe" do - expect(@chef_run).to include_recipe "openstack-common::logging" - end - - it "installs the common package" do - expect(@chef_run).to install_package "ceilometer-common" - end - - describe "/etc/ceilometer" do - before do - @dir = @chef_run.directory "/etc/ceilometer" - end - - it "has proper owner" do - expect(@dir).to be_owned_by "ceilometer", "ceilometer" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "750" - end - end - - describe "/etc/ceilometer" do - before do - @file = @chef_run.template "/etc/ceilometer/ceilometer.conf" - end - - it "has proper owner" do - expect(@file).to be_owned_by("ceilometer", "ceilometer") - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq("640") - end - - it "has rabbit_user" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_userid = guest" - end - - it "has rabbit_password" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_password = rabbit-pass" - end - - it "has rabbit_port" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_port = 5672" - end - - it "has rabbit_host" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_host = 127.0.0.1" - end - - it "has rabbit_virtual_host" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_virtual_host = /" - end - end - - describe "/etc/ceilometer/policy.json" do - before do - @dir = @chef_run.cookbook_file "/etc/ceilometer/policy.json" - end - - it "has proper owner" do - expect(@dir).to be_owned_by "ceilometer", "ceilometer" - end - - it "has proper modes" do - expect(sprintf("%o", @dir.mode)).to eq "640" - end - end - end -end diff --git a/chef/cookbooks/openstack-metering/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-metering/spec/identity_registration_spec.rb deleted file mode 100644 index 92dea7b..0000000 --- a/chef/cookbooks/openstack-metering/spec/identity_registration_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -require_relative "spec_helper" - -describe "openstack-metering::identity_registration" do - before do - metering_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-metering::identity_registration" - end - - it "registers metering service" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Metering Service" - ).to_hash - - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_name => "ceilometer", - :service_type => "metering", - :action => [:create_service] - ) - end - - it "registers metering endpoint" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Metering Endpoint" - ).to_hash - - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_type => "metering", - :endpoint_region => "RegionOne", - :endpoint_adminurl => "http://127.0.0.1:8777/v1", - :endpoint_internalurl => "http://127.0.0.1:8777/v1", - :endpoint_publicurl => "http://127.0.0.1:8777/v1", - :action => [:create_endpoint] - ) - end -end diff --git a/chef/cookbooks/openstack-metering/spec/spec_helper.rb b/chef/cookbooks/openstack-metering/spec/spec_helper.rb deleted file mode 100644 index bd0b397..0000000 --- a/chef/cookbooks/openstack-metering/spec/spec_helper.rb +++ /dev/null @@ -1,37 +0,0 @@ -require "chefspec" - -::LOG_LEVEL = :fatal -::OPENSUSE_OPTS = { - :platform => "opensuse", - :version => "12.3", - :log_level => ::LOG_LEVEL -} -::REDHAT_OPTS = { - :platform => "redhat", - :version => "6.3", - :log_level => ::LOG_LEVEL -} -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL -} - -def metering_stubs - ::Chef::Recipe.any_instance.stub(:memcached_servers).and_return [] - ::Chef::Recipe.any_instance.stub(:service_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:user_password). - with("guest"). - and_return "rabbit-pass" - ::Chef::Recipe.any_instance.stub(:secret). - with("secrets", "openstack_identity_bootstrap_token"). - and_return "bootstrap-token" -end - -def expect_runs_common_recipe - it "runs common recipe" do - expect(@chef_run).to include_recipe "openstack-metering::common" - end -end diff --git a/chef/cookbooks/openstack-metering/templates/default/ceilometer.conf.erb b/chef/cookbooks/openstack-metering/templates/default/ceilometer.conf.erb deleted file mode 100644 index 97d0cec..0000000 --- a/chef/cookbooks/openstack-metering/templates/default/ceilometer.conf.erb +++ /dev/null @@ -1,36 +0,0 @@ -[DEFAULT] -<% if node["openstack"]["metering"]["host"] %> -host = <%= node["openstack"]["metering"]["host"] %> -<% end %> -os_auth_url = <%= @auth_uri %> -os_tenant_name = <%= @service_tenant_name %> -os_password = <%= @service_pass %> -os_username = <%= @service_user %> -policy_file = /etc/ceilometer/policy.json -database_connection = <%= @database_connection %> -rabbit_userid = <%= node["openstack"]["metering"]["rabbit"]["username"] %> -rabbit_password = <%= @rabbit_pass %> -rabbit_port = <%= node["openstack"]["metering"]["rabbit"]["port"] %> -rabbit_host = <%= node["openstack"]["metering"]["rabbit"]["host"] %> -rabbit_virtual_host = <%= node["openstack"]["metering"]["rabbit"]["vhost"] %> -verbose = True -notification_topics = notifications,glance_notifications -rpc_backend = ceilometer.openstack.common.rpc.impl_kombu -<% if node["openstack"]["metering"]["syslog"]["use"] %> -log_config = /etc/openstack/logging.conf -<% end %> -<% if node["openstack"]["metering"]["debug"] %> -debug = True -<% end %> -glance_registry_host = <%= @image_endpoint.host %> -periodic_interval = <%= node["openstack"]["metering"]["periodic_interval"] %> - -[keystone_authtoken] -paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory -auth_host = <%= @identity_endpoint.host %> -auth_port = <%= @identity_endpoint.port %> -auth_protocol = <%= @identity_endpoint.scheme %> -admin_tenant_name = <%= @service_tenant_name %> -admin_user = <%= @service_user %> -admin_password = <%= @service_pass %> -signing_dir = <%= node["openstack"]["metering"]["api"]["auth"]["cache_dir"] %> diff --git a/chef/cookbooks/openstack-network/.rubocop.yml b/chef/cookbooks/openstack-network/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-network/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-network/Berksfile b/chef/cookbooks/openstack-network/Berksfile index c00fec3..918525a 100644 --- a/chef/cookbooks/openstack-network/Berksfile +++ b/chef/cookbooks/openstack-network/Berksfile @@ -2,5 +2,3 @@ metadata cookbook 'openstack-identity', :git => 'https://github.com/stackforge/cookbook-openstack-identity.git' cookbook 'openstack-common', :git => 'https://github.com/stackforge/cookbook-openstack-common.git' -cookbook 'database' -cookbook 'mysql' \ No newline at end of file diff --git a/chef/cookbooks/openstack-network/Berksfile.lock b/chef/cookbooks/openstack-network/Berksfile.lock deleted file mode 100644 index 773cd83..0000000 --- a/chef/cookbooks/openstack-network/Berksfile.lock +++ /dev/null @@ -1,41 +0,0 @@ -{ - "sources": { - "openstack-network": { - "path": "." - }, - "openstack-identity": { - "locked_version": "7.0.0", - "git": "https://github.com/stackforge/cookbook-openstack-identity.git", - "ref": "b881af26095cfa869a6970067c49597a0ee63586" - }, - "openstack-common": { - "locked_version": "0.4.3", - "git": "https://github.com/stackforge/cookbook-openstack-common.git", - "ref": "eb5eed7126b6a6efbaf803e8a594d610cf661e97" - }, - "database": { - "locked_version": "1.4.0" - }, - "mysql": { - "locked_version": "3.0.0" - }, - "postgresql": { - "locked_version": "3.0.2" - }, - "apt": { - "locked_version": "2.0.0" - }, - "build-essential": { - "locked_version": "1.4.0" - }, - "openssl": { - "locked_version": "1.0.2" - }, - "aws": { - "locked_version": "0.101.2" - }, - "xfs": { - "locked_version": "1.1.0" - } - } -} diff --git a/chef/cookbooks/openstack-network/CHANGELOG.md b/chef/cookbooks/openstack-network/CHANGELOG.md new file mode 100644 index 0000000..7982401 --- /dev/null +++ b/chef/cookbooks/openstack-network/CHANGELOG.md @@ -0,0 +1,89 @@ +# CHANGELOG for cookbook-openstack-network +This file is used to list changes made in each version of cookbook-openstack-network. +## 9.0.8 +* Add support for miscellaneous options (like in Compute) + +## 9.0.7 +* Revert Switch to using auth_url instead of auth_host et al + +## 9.0.6 +* Fix ovs_use_veth default value + +## 9.0.5 +* Switch to using auth_url instead of auth_host et al + +## 9.0.4 +* Fix to allow build openvswitch spec to work on windows + +## 9.0.3 +* Fix openvswitch and linuxbridge agent + +## 9.0.2 +* Fix to allow data network openvswitch bridge to be created + +## 9.0.1 +* Fix package action to allow updates + +## 9.0.0 +* Upgrade to Icehouse +* The balancer recipe now includes openstack-network::common +* Neutron agents now subscribe to changes in neutron.conf +* Add rpc attributes +* Remove policy file + +## 8.5.1 +### Bug +* Fix the DB2 ODBC driver issue + +## 8.5.0 +### Blue print +* Use the library method auth_uri_transform + +## 8.4.0 +* Add new template for ml2 plugin + +## 8.3.0 +* Add new attributes to support vxlan in linuxbridge plugin template + +## 8.2.0 +* Move the database section into neutron.conf from plugins +* Make the service_provider attribute configurable + +## 8.1.1 +* allow dnsmasq source build to be optional + +## 8.1.0 +* Add client recipe + +## 8.0.1: +* Add network database migration +* Remove unneeded and redundant rhel setup script calls +* Deprecate node['openstack']['network']['neutron_loadbalancer'] in favor of + node['openstack']['network']['service_plugins'] + +## 8.0.0: +* Support neutron deployment by search and replace quantum with neutron + +## 7.1.1 +* fixing rpc_backend for qpid + +## 7.1.0 +* adding qpid support to quantum. default is rabbitmq + +## 7.0.5 +* Parameterize quota default values in quantum.conf.erb (LP #1228623) + +## 7.0.4 +* Set auth_uri and use admin_endpoint in authtoken configuration (LP #1207504) + +## 7.0.3: +* Parameterize agent_down_time and report_interval settings + +## 7.0.2: +* Add delay to quantum-ha-tool.py script to prevent aggressive migrations + +## 7.0.1: +* Allow quota driver to be set dynamically (LP #1234324) + +## 7.0.0: +* Start Grizzly + Neutron deployment diff --git a/chef/cookbooks/openstack-network/Gemfile b/chef/cookbooks/openstack-network/Gemfile index bb25f5c..ccfa456 100644 --- a/chef/cookbooks/openstack-network/Gemfile +++ b/chef/cookbooks/openstack-network/Gemfile @@ -1,11 +1,9 @@ -# A sample Gemfile -source "https://rubygems.org" +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef dependency -gem "berkshelf", "~> 2.0.6" -gem "chefspec", "~> 2.0.0" -gem "foodcritic" -gem "strainer" -gem "webmock", "~> 1.11.0" -gem "tailor" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' diff --git a/chef/cookbooks/openstack-network/Gemfile.lock b/chef/cookbooks/openstack-network/Gemfile.lock index 955443e..ae0fed9 100644 --- a/chef/cookbooks/openstack-network/Gemfile.lock +++ b/chef/cookbooks/openstack-network/Gemfile.lock @@ -1,30 +1,35 @@ GEM remote: https://rubygems.org/ specs: - activesupport (3.2.13) - i18n (= 0.6.1) + activesupport (3.2.16) + i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) addressable (2.3.5) akami (1.2.0) gyoku (>= 0.4.0) nokogiri (>= 1.4.0) - berkshelf (2.0.6) + ast (1.1.0) + berkshelf (2.0.12) activesupport (~> 3.2.0) addressable (~> 2.3.4) buff-shell_out (~> 0.1) - celluloid (>= 0.14.0) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.5) hashie (>= 2.0.2) minitar (~> 0.5.4) rbzip2 (~> 0.2.0) retryable (~> 1.3.3) - ridley (~> 1.2.1) + ridley (~> 1.5.0) solve (>= 0.5.0) thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) buff-ruby_engine (0.1.0) - buff-shell_out (0.1.0) + buff-shell_out (0.1.1) buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) @@ -32,115 +37,127 @@ GEM celluloid-io (0.14.1) celluloid (>= 0.14.1) nio4r (>= 0.4.5) - chef (11.4.4) - erubis - highline (>= 1.6.9) + chef (11.8.2) + chef-zero (~> 1.6, >= 1.6.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.3) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.2) net-ssh (~> 2.6) net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (2.0.0) - chef (>= 10.0) - erubis - fauxhai (~> 1.1) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.2) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.9.0) - builder (>= 2.1.2) - crack (0.4.0) - safe_yaml (~> 0.9.0) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.7) - multipart-post (~> 1.1) - fauxhai (1.1.1) - httparty + faraday (0.8.8) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) net-ssh ohai - ffi (1.9.0) - foodcritic (2.1.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) - rak (~> 1.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.0.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.20) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.1) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.1) little-plugger (>= 1.1.3) - mime-types (1.23) + multi_json (>= 1.3.6) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.5) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.1.0) - multi_json (1.7.7) - multi_xml (0.5.5) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.8.3) multipart-post (1.2.0) - net-http-persistent (2.8) - net-ssh (2.6.7) + net-http-persistent (2.9) + net-ssh (2.7.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) net-ssh-multi (1.1) net-ssh (>= 2.1.4) net-ssh-gateway (>= 0.99.0) - nio4r (0.4.6) - nokogiri (1.5.10) + nio4r (0.5.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.16.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby + parser (2.1.4) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) polyglot (0.3.3) + powerpack (0.0.9) + pry (0.9.12.4) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) - rak (1.4) + rainbow (2.0.0) + rake (10.1.1) rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (1.2.4) + retryable (1.3.4) + ridley (1.5.3) addressable + buff-config (~> 0.2) buff-extensions (~> 0.3) + buff-ignore (~> 1.1) buff-shell_out (~> 0.1) celluloid (~> 0.14.0) celluloid-io (~> 0.14.0) @@ -151,6 +168,7 @@ GEM mixlib-authentication (>= 1.3.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) varia_model (~> 0.1) @@ -159,12 +177,16 @@ GEM rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - rspec-core (2.14.5) - rspec-expectations (2.14.2) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.3) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) - safe_yaml (0.9.3) savon (0.9.5) akami (~> 1.0) builder (>= 2.1.2) @@ -173,37 +195,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.6.0) - strainer (3.0.4) + slop (3.4.7) + solve (0.8.2) + strainer (3.3.0) berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - text-table (1.2.3) thor (0.18.1) - timers (1.1.0) - tins (0.8.2) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) uuidtools (2.1.4) - varia_model (0.1.0) - buff-extensions (~> 0.1) + varia_model (0.2.0) + buff-extensions (~> 0.2) hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - webmock (1.11.0) - addressable (>= 2.2.7) - crack (>= 0.3.2) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -213,11 +227,10 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 2.0.6) - chef (~> 11.4.4) - chefspec (~> 2.0.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - tailor - webmock (~> 1.11.0) diff --git a/chef/cookbooks/openstack-network/README.md b/chef/cookbooks/openstack-network/README.md index a20a2f7..410dfc3 100644 --- a/chef/cookbooks/openstack-network/README.md +++ b/chef/cookbooks/openstack-network/README.md @@ -1,7 +1,7 @@ Description =========== -This cookbook installs the **OpenStack Network** service (formerly project-named Quantum) +This cookbook installs the **OpenStack Network** service (formerly project-named Quantum, current name is Neutron) as part of a Chef reference deployment of OpenStack. More information about the OpenStack Network service is available @@ -24,11 +24,16 @@ Cookbooks The following cookbooks are dependencies: * identity -* openstack-common `>= 2.0.0` +* openstack-common `>= 8.0.0` Recipes ======= +client +------ + +- Install the network client packages + server ------ @@ -52,27 +57,95 @@ Identity-registration Attributes ========== +* `openstack['network']['service_provider']` - Array of service providers (drivers) for advanced services like loadbalancer, VPN, Firewall. +* `openstack['network']['api']['auth']['version']` - Select v2.0 or v3.0. Default v2.0. The auth API version used to interact with identity service. +* `openstack['network']["misc_neutron"]` - Array of strings to be added to neutron.conf + +TODO: Add DB2 support on other platforms +* `openstack["network"]["platform"]["db2_python_packages"]` - Array of DB2 python packages, only available on redhat platform + TODO +* `openstack["network"]["service_plugins"]` - Array of Python classes to be used as `service_plugins` in neutron.conf (default: []). Set it to ['neutron.plugins.services.agent_loadbalancer.plugin.LoadBalancerPlugin'] to include the load balancer plugin. + +MQ attributes +------------- +* `openstack["network"]["mq"]["service_type"]` - Select qpid or rabbitmq. default rabbitmq +TODO: move rabbit parameters under openstack["network"]["mq"] +* `openstack["network"]["rabbit"]["username"]` - Username for nova rabbit access +* `openstack["network"]["rabbit"]["vhost"]` - The rabbit vhost to use +* `openstack["network"]["rabbit"]["port"]` - The rabbit port to use +* `openstack["network"]["rabbit"]["host"]` - The rabbit host to use (must set when `openstack["network"]["rabbit"]["ha"]` false). +* `openstack["network"]["rabbit"]["ha"]` - Whether or not to use rabbit ha + +* `openstack["network"]["mq"]["qpid"]["host"]` - The qpid host to use +* `openstack["network"]["mq"]["qpid"]["port"]` - The qpid port to use +* `openstack["network"]["mq"]["qpid"]["qpid_hosts"]` - Qpid hosts. TODO. use only when ha is specified. +* `openstack["network"]["mq"]["qpid"]["username"]` - Username for qpid connection +* `openstack["network"]["mq"]["qpid"]["password"]` - Password for qpid connection +* `openstack["network"]["mq"]["qpid"]["sasl_mechanisms"]` - Space separated list of SASL mechanisms to use for auth +* `openstack["network"]["mq"]["qpid"]["reconnect_timeout"]` - The number of seconds to wait before deciding that a reconnect attempt has failed. +* `openstack["network"]["mq"]["qpid"]["reconnect_limit"]` - The limit for the number of times to reconnect before considering the connection to be failed. +* `openstack["network"]["mq"]["qpid"]["reconnect_interval_min"]` - Minimum number of seconds between connection attempts. +* `openstack["network"]["mq"]["qpid"]["reconnect_interval_max"]` - Maximum number of seconds between connection attempts. +* `openstack["network"]["mq"]["qpid"]["reconnect_interval"]` - Equivalent to setting qpid_reconnect_interval_min and qpid_reconnect_interval_max to the same value. +* `openstack["network"]["mq"]["qpid"]["heartbeat"]` - Seconds between heartbeat messages sent to ensure that the connection is still alive. +* `openstack["network"]["mq"]["qpid"]["protocol"]` - Protocol to use. Default tcp. +* `openstack["network"]["mq"]["qpid"]["tcp_nodelay"]` - Disable the Nagle algorithm. default disabled. + +Linuxbridge plugin attributes +----------------------------- +* `openstack['openstack']['network']['linuxbridge']['tenant_network_type']` - Type of network to allocate for tenant networks. (default 'local') +* `openstack['openstack']['network']['linuxbridge']['network_vlan_ranges']` - Comma-separated list of [::] tuples enumerating ranges of VLAN IDs +* `openstack['openstack']['network']['linuxbridge']['physical_interface_mappings']` - (ListOpt) Comma-separated list of : tuples mapping physical network names +* `openstack['openstack']['network']['linuxbridge']['enable_vxlan']` - (BoolOpt) enable VXLAN on the agent. (default false) +* `openstack['openstack']['network']['linuxbridge']['ttl']` - (IntOpt) use specific TTL for vxlan interface protocol packets +* `openstack['openstack']['network']['linuxbridge']['tos']` - (IntOpt) use specific TOS for vxlan interface protocol packets +* `openstack['openstack']['network']['linuxbridge']['vxlan_group']` - (StrOpt) multicast group to use for broadcast emulation. (default '224.0.0.1') +* `openstack['openstack']['network']['linuxbridge']['l2_population']` - (BoolOpt) Flag to enable l2population extension. (default false) +* `openstack['openstack']['network']['linuxbridge']['polling_interval']` - Agent polling interval in seconds. (default 2) +* `openstack['openstack']['network']['linuxbridge']['rpc_support_old_agents']` - (BoolOpt) Enable server RPC compatibility with old (pre-havana). (default false) +* `openstack['openstack']['network']['linuxbridge']['firewall_driver']` - Firewall driver for realizing neutron security group function + +Modular Layer 2 Plugin Configuration +------------------------------------ +* `openstack['openstack']['network']['ml2']['type_drivers']` - (ListOpt) List of network type driver entrypoints to be loaded from the neutron.ml2.type_drivers namespace. +* `openstack['openstack']['network']['ml2']['tenant_network_types']` - (ListOpt) Ordered list of net work_types to allocate as tenant networks. (default local) +* `openstack['openstack']['network']['ml2']['mechanism_drivers']` - (ListOpt) Ordered list of networ king mechanism driver entrypoints to be loaded from the neutron.ml2.mechanism_drivers namespace. +* `openstack['openstack']['network']['ml2']['flat_networks']` - (ListOpt) List of physical_network names with which flat networks can be created. +* `openstack['openstack']['network']['ml2']['network_vlan_ranges']` - (ListOpt) List of [::] tuples specifying physical_network names usable for VLAN provider and tenant networks +* `openstack['openstack']['network']['ml2']['tunnel_id_ranges']` - (ListOpt) Comma-separated list of : tuples enumerating ranges of GRE tunnel IDs that are available for tenant network allocation +* `openstack['openstack']['network']['ml2']['vni_ranges']` - (ListOpt) Comma-separated list of : tuples enumerating ranges of VXLAN VNI IDs that are available for tenant network allocation. +* `openstack['openstack']['network']['ml2']['vxlan_group']` - (StrOpt) Multicast group for the VXLAN interface. + +The following attributes are defined in attributes/default.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack['endpoints']['network-api-bind']['host']` - The IP address to bind the api service to +* `openstack['endpoints']['network-api-bind']['port']` - The port to bind the api service to +* `openstack['endpoints']['network-api-bind']['bind_interface']` - The interface name to bind the api service to + +If the value of the 'bind_interface' attribute is non-nil, then the network service will be bound to the first IP address on that interface. If the value of the 'bind_interface' attribute is nil, then the network service will be bound to the IP address specified in the host attribute. + Templates ========= - * `api-paste.ini.erb` - Paste config for OpenStack Network server -* `quantum.conf.erb` - Config file for OpenStack Network server -* `policy.json.erb` - Configuration of ACLs for glance API server +* `neutron.conf.erb` - Config file for OpenStack Network server +* `ml2_conf.ini.erb` - Configuration of Network ML2 Plugins Testing ======= -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. -Tests are defined in Strainerfile. +Berkshelf +===== -To run tests: - - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -82,10 +155,16 @@ License and Author | **Authors** | Alan Meadows () | | | Jay Pipes () | | | Ionut Artarisi () | +| | Salman Baset () | +| | Jian Hua Geng () | +| | Chen Zhiwei () | +| | Mark Vanderwiel() | +| | Eric Zhou() | | | | | **Copyright** | Copyright (c) 2013, AT&T Services, Inc. | -| | Copyright (c) 2013, SUSE Linux GmbH | +| | Copyright (c) 2013-2014, SUSE Linux GmbH | | | Copyright (c) 2012, Rackspace US, Inc. | +| | Copyright (c) 2013-2014, IBM Corp. | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/openstack-network/Strainerfile b/chef/cookbooks/openstack-network/Strainerfile index 7e292b4..44e3e14 100644 --- a/chef/cookbooks/openstack-network/Strainerfile +++ b/chef/cookbooks/openstack-network/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-network/TESTING.md b/chef/cookbooks/openstack-network/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-network/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-network/attributes/default.rb b/chef/cookbooks/openstack-network/attributes/default.rb index db16f1b..e97d0c1 100644 --- a/chef/cookbooks/openstack-network/attributes/default.rb +++ b/chef/cookbooks/openstack-network/attributes/default.rb @@ -1,8 +1,10 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-network # Attributes:: default # # Copyright 2013, AT&T +# Copyright 2014, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,58 +21,99 @@ # Set to some text value if you want templated config files # to contain a custom banner at the top of the written file -default["openstack"]["network"]["custom_template_banner"] = " +default['openstack']['network']['custom_template_banner'] = ' # This file autogenerated by Chef # Do not edit, changes will be overwritten -" +' -default["openstack"]["network"]["verbose"] = "False" -default["openstack"]["network"]["debug"] = "False" +default['openstack']['network']['verbose'] = 'False' +default['openstack']['network']['debug'] = 'False' +default['openstack']['network']['auth_strategy'] = 'keystone' + +default['openstack']['network']['state_path'] = '/var/lib/neutron' +default['openstack']['network']['lock_path'] = '$state_path/lock' + +# Specify policy.json remote file to import +default['openstack']['network']['policyfile_url'] = nil + +# DB Stamp comes late in dev cycle, allow override for development and test +default['openstack']['network']['db_stamp'] = node['openstack']['release'] # Gets set in the Network Endpoint when registering with Keystone -default["openstack"]["network"]["region"] = "RegionOne" -default["openstack"]["network"]["service_user"] = "quantum" -default["openstack"]["network"]["service_role"] = "admin" -default["openstack"]["network"]["service_name"] = "quantum" -default["openstack"]["network"]["service_type"] = "network" -default["openstack"]["network"]["description"] = "OpenStack Networking service" +default['openstack']['network']['region'] = node['openstack']['region'] +default['openstack']['network']['service_user'] = 'neutron' +default['openstack']['network']['service_role'] = 'admin' +default['openstack']['network']['service_name'] = 'neutron' +default['openstack']['network']['service_type'] = 'network' +default['openstack']['network']['description'] = 'OpenStack Networking service' -# The rabbit user's password is stored in an encrypted databag -# and accessed with openstack-common cookbook library's -# user_password routine. You are expected to create -# the user, pass, vhost in a wrapper rabbitmq cookbook. -default["openstack"]["network"]["rabbit_server_chef_role"] = "rabbitmq-server" -default["openstack"]["network"]["rabbit"]["username"] = "guest" -default["openstack"]["network"]["rabbit"]["vhost"] = "/" -default["openstack"]["network"]["rabbit"]["port"] = 5672 -default["openstack"]["network"]["rabbit"]["host"] = "127.0.0.1" -default["openstack"]["network"]["rabbit"]["ha"] = false - -# The database username for the quantum database -default["openstack"]["network"]["db"]["username"] = "quantum" +default['openstack']['network']['rabbit_server_chef_role'] = 'rabbitmq-server' # Used in the Keystone authtoken middleware configuration -default["openstack"]["network"]["service_tenant_name"] = "service" -default["openstack"]["network"]["service_user"] = "quantum" -default["openstack"]["network"]["service_role"] = "admin" +default['openstack']['network']['service_tenant_name'] = 'service' +default['openstack']['network']['service_user'] = 'neutron' +default['openstack']['network']['service_role'] = 'admin' -# The default agent reporting interval -default["openstack"]["network"]["api"]["agent"]["agent_report_interval"] = 4 +# The maximum number of seconds we will wait for an agent to checkin +default['openstack']['network']['api']['agent']['agent_down_time'] = 75 + +# The default agent reporting interval in seconds +default['openstack']['network']['api']['agent']['agent_report_interval'] = 30 # The agent signing directory for api server -default["openstack"]["network"]["api"]["agent"]["signing_dir"] = "/var/lib/quantum/keystone-signing" +default['openstack']['network']['api']['agent']['signing_dir'] = '/var/lib/neutron/keystone-signing' # Keystone PKI signing directory. -default["openstack"]["network"]["api"]["auth"]["cache_dir"] = "/var/cache/quantum/api" +default['openstack']['network']['api']['auth']['cache_dir'] = '/var/cache/neutron/api' -# If bind_interface is set, the quantum API service will bind to the -# address on this interface and use the port in bind_port. Otherwise, -# it will bind to the API endpoint's host. -default["openstack"]["network"]["api"]["bind_interface"] = nil -default["openstack"]["network"]["api"]["bind_port"] = 9696 +# The auth api version used to interact with identity service. +default['openstack']['network']['api']['auth']['version'] = node['openstack']['api']['auth']['version'] + +# Number of separate worker processes to spawn. +default['openstack']['network']['api_workers'] = 8 + +# Number of separate RPC worker processes to spawn. +default['openstack']['network']['rpc_workers'] = 8 # logging attribute -default["openstack"]["network"]["syslog"]["use"] = false +default['openstack']['network']['log_dir'] = '/var/log/neutron' +default['openstack']['network']['syslog']['use'] = false + +# The driver for Quota management in Neutron. Possible values: +# neutron.quota.ConfDriver +# neutron.db.quota_db.DbQuotaDriver (default) +# +# Note: set this to the DbDriver if you want to be able to update +# quotas for networks/subnets/security groups +default['openstack']['network']['quota']['driver'] = 'neutron.db.quota_db.DbQuotaDriver' + +# default quotas will be used when no more specific tenant entry exists +# when using the DBDriver - override them below to adjust the default +# quotas + +# resource name(s) that are supported in quota features +default['openstack']['network']['quota']['items'] = 'network,subnet,port' + +# default number of resource allowed per tenant, minus for unlimited +# however if more specific setting exists for a quota resource (all known +# quota resources are specified below) those numbers will be used instead +# so unless new resources are introduces, this has no effect +default['openstack']['network']['quota']['default'] = -1 + +# number of networks allowed per tenant, and minus means unlimited +default['openstack']['network']['quota']['network'] = 100 + +# number of subnets allowed per tenant, and minus means unlimited +default['openstack']['network']['quota']['subnet'] = 100 + +# number of ports allowed per tenant, and minus means unlimited +default['openstack']['network']['quota']['port'] = 8000 + +# number of security groups allowed per tenant, and minus means unlimited +default['openstack']['network']['quota']['security_group'] = 1000 + +# number of security group rules allowed per tenant, and minus means unlimited +default['openstack']['network']['quota']['security_group_rule'] = 1000 # Whether or not we want to disable offloading # on all the NIC interfaces (currently only supports @@ -78,110 +121,167 @@ default["openstack"]["network"]["syslog"]["use"] = false # or nicira plugins are crashing the sdn routers default['openstack']['network']['disable_offload'] = false -# configure quantum ha tool installation parameters -default["openstack"]["network"]["quantum_ha_cmd_cron"] = false -default["openstack"]["network"]["quantum_ha_cmd"] = "/usr/local/bin/quantum-ha-tool.py" -default["openstack"]["network"]["cron_l3_healthcheck"] = "*/1" -default["openstack"]["network"]["cron_replicate_dhcp"] = "*/1" +# configure neutron ha tool installation parameters +default['openstack']['network']['neutron_ha_cmd_cron'] = false +default['openstack']['network']['neutron_ha_cmd'] = '/usr/local/bin/neutron-ha-tool.py' +default['openstack']['network']['cron_l3_healthcheck'] = '*/1' +default['openstack']['network']['cron_replicate_dhcp'] = '*/1' -# the plugins to install on the server. this will be -# quantum-plugin-%plugin% and the first plugin in the -# list should match the core plugin below -# N.B. this will be ignored on SUSE as all plugins are installed by -# default by the main openstack-quantum package -default["openstack"]["network"]["plugins"] = ['openvswitch', 'openvswitch-agent' ] +# (ListOpt) Specify service providers (drivers) for advanced services like loadbalancer, VPN, Firewall. +# Must be in form: +# service_provider=::[:default] +# List of allowed service type include LOADBALANCER, FIREWALL, VPN +# Combination of and must be unique; must also be unique +# this is multiline option, example for default provider: +# service_provider=LOADBALANCER:name:lbaas_plugin_driver_path:default +# example of non-default provider: +# service_provider=FIREWALL:name2:firewall_driver_path +# --- Reference implementations --- +default['openstack']['network']['service_provider'] = [] -# the core plugin to use for quantum -default["openstack"]["network"]["core_plugin"] = "quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2" +# The core plugin to use for neutron +default['openstack']['network']['core_plugin'] = 'neutron.plugins.ml2.plugin.Ml2Plugin' + +# additional service plugins to use for neutron +# e.g. neutron.plugins.services.agent_loadbalancer.plugin.LoadBalancerPlugin +# for the loadbalancer reference implementation +default['openstack']['network']['service_plugins'] = [] # The bridging interface driver. -# +# This is used by the L3, DHCP and LBaaS agents. # Options are: # -# - quantum.agent.linux.interface.OVSInterfaceDriver -# - quantum.agent.linux.interface.BridgeInterfaceDriver +# - neutron.agent.linux.interface.OVSInterfaceDriver +# - neutron.agent.linux.interface.BridgeInterfaceDriver # +default['openstack']['network']['interface_driver'] = 'neutron.agent.linux.interface.OVSInterfaceDriver' -default["openstack"]["network"]["interface_driver"] = 'quantum.agent.linux.interface.OVSInterfaceDriver' - -# maps the above driver to a plugin name -default["openstack"]["network"]["interface_driver_map"] = { - 'ovsinterfacedriver' => 'openvswitch', - 'bridgeinterfacedriver' => 'linuxbridge' +# Maps the above core plugin driver to a simple name +# This is used in the neutron_plugin_package package name and common recipe case statements +default['openstack']['network']['core_plugin_map'] = { + 'ovsneutronpluginv2' => 'openvswitch', + 'linuxbridgepluginv2' => 'linuxbridge', + 'ml2plugin' => 'ml2' } -default["openstack"]["network"]["plugin_conf_map"] = { - 'ovsinterfacedriver' => 'openvswitch/ovs_quantum_plugin.ini', - 'bridgeinterfacedriver' => 'linuxbridge/linuxbridge_conf.ini' +# This is used by SUSE to setup the sysconfig neutron initfile +default['openstack']['network']['plugin_conf_map'] = { + 'ovsneutronpluginv2' => 'openvswitch/ovs_neutron_plugin.ini', + 'linuxbridgepluginv2' => 'linuxbridge/linuxbridge_conf.ini', + 'ml2plugin' => 'ml2/ml2_conf.ini' } # The agent can use other DHCP drivers. Dnsmasq is the simplest and requires # no additional setup of the DHCP server. -default["openstack"]["network"]["dhcp_driver"] = 'quantum.agent.linux.dhcp.Dnsmasq' +default['openstack']['network']['dhcp_driver'] = 'neutron.agent.linux.dhcp.Dnsmasq' # Use namespaces and optionally allow overlapping IPs. You # must enable namespaces to use overlapping ips. Also, # you must have kernel build with CONFIG_NET_NS=y and # iproute2 package that supports namespaces. -default["openstack"]["network"]["use_namespaces"] = "True" -default["openstack"]["network"]["allow_overlapping_ips"] = "True" +default['openstack']['network']['use_namespaces'] = 'True' +default['openstack']['network']['allow_overlapping_ips'] = 'True' -# use quantum root wrap -default["openstack"]["network"]["use_rootwrap"] = true +# use neutron root wrap +default['openstack']['network']['use_rootwrap'] = true + +# DHCP lease duration +default['openstack']['network']['dhcp_lease_duration'] = 86400 + +# Driver or drivers to handle sending notifications and control exchange +default['openstack']['network']['notification_driver'] = 'neutron.openstack.common.notifier.rpc_notifier' +default['openstack']['network']['control_exchange'] = node['openstack']['mq']['network']['control_exchange'] + +# Common rpc definitions +default['openstack']['network']['rpc_thread_pool_size'] = 240 +default['openstack']['network']['rpc_conn_pool_size'] = 100 +default['openstack']['network']['rpc_response_timeout'] = 300 +default['openstack']['network']['rpc_cast_timeout'] = 300 + +# ======== Neutron Nova interactions ========== +# Send notification to nova when port status is active. +default['openstack']['network']['nova']['notify_nova_on_port_status_changes'] = 'True' + +# Send notifications to nova when port data (fixed_ips/floatingips) change +# so nova can update it's cache. +default['openstack']['network']['nova']['notify_nova_on_port_data_changes'] = 'True' + +# Name of nova region to use. Useful if keystone manages more than one region +default['openstack']['network']['nova']['region_name'] = node['openstack']['region'] + +# Username for connection to nova in admin context +default['openstack']['network']['nova']['admin_username'] = 'nova' + +# Version for connection to nova +# TODO: (MRV) Need to allow for this in Common. +default['openstack']['network']['nova']['url_version'] = '/v2' + +# The uuid of the nova tenant +# Nil will cause the uuid to be queried from keystone. +default['openstack']['network']['nova']['admin_tenant_id'] = nil + +# Number of seconds between sending events to nova if there are any events to send +default['openstack']['network']['nova']['send_events_interval'] = 2 # ============================= DHCP Agent Configuration =================== # The scheduler class to use for scheduling to DHCP agents -default["openstack"]["network"]["dhcp"]["scheduler"] = "quantum.scheduler.dhcp_agent_scheduler.ChanceScheduler" +default['openstack']['network']['dhcp']['scheduler'] = 'neutron.scheduler.dhcp_agent_scheduler.ChanceScheduler' # Override the default mtu setting given to virtual machines # to 1454 to allow for tunnel and other encapsulation overhead. You # can adjust this from 1454 to 1500 if you do not want any lowering # of the default guest MTU. -default["openstack"]["network"]["dhcp"]["dhcp-option"] = "26,1454" +default['openstack']['network']['dhcp']['dhcp-option'] = '26,1454' -# Number of seconds between sync of DHCP agent with Quantum API server -default["openstack"]["network"]["dhcp"]["resync_interval"] = 5 +# Number of seconds between sync of DHCP agent with Neutron API server +default['openstack']['network']['dhcp']['resync_interval'] = 5 # OVS based plugins(Ryu, NEC, NVP, BigSwitch/Floodlight) that use OVS # as OpenFlow switch and check port status -default["openstack"]["network"]["dhcp"]["ovs_use_veth"] = "True" +default['openstack']['network']['dhcp']['ovs_use_veth'] = 'False' # The DHCP server can assist with providing metadata support on isolated # networks. Setting this value to True will cause the DHCP server to append # specific host routes to the DHCP request. The metadata service will only # be activated when the subnet gateway_ip is None. The guest instance must # be configured to request host routes via DHCP (Option 121). -default["openstack"]["network"]["dhcp"]["enable_isolated_metadata"] = "False" +default['openstack']['network']['dhcp']['enable_isolated_metadata'] = 'False' # Allows for serving metadata requests coming from a dedicated metadata # access network whose cidr is 169.254.169.254/16 (or larger prefix), and -# is connected to a Quantum router from which the VMs send metadata +# is connected to a Neutron router from which the VMs send metadata # request. In this case DHCP Option 121 will not be injected in VMs, as # they will be able to reach 169.254.169.254 through a router. # This option requires enable_isolated_metadata = True -default["openstack"]["network"]["dhcp"]["enable_metadata_network"] = "False" +default['openstack']['network']['dhcp']['enable_metadata_network'] = 'False' # On ubuntu precise, we build dnsmasq from source to fetch a more recent # version of dnsmasq since a backport is not available. For any other # platform, dnsmasq will be installed as a package # # See https://lists.launchpad.net/openstack/msg11696.html -default["openstack"]["network"]["dhcp"]["dnsmasq_url"] = "https://github.com/guns/dnsmasq/archive/v2.65.tar.gz" +default['openstack']['network']['dhcp']['dnsmasq_url'] = 'https://codeload.github.com/guns/dnsmasq/tar.gz/v2.65' + +# allow a wrapper to do this another way or use it's own package +default['openstack']['network']['dhcp']['dnsmasq_compile'] = true # The name of the file we will fetch -default["openstack"]["network"]["dhcp"]["dnsmasq_filename"] = "v2.65.tar.gz" +default['openstack']['network']['dhcp']['dnsmasq_filename'] = 'v2.65.tar.gz' # The checksum of the remote file we fetched -default["openstack"]["network"]["dhcp"]["dnsmasq_checksum"] = "f6cab8c64cb612089174f50927a05e2b" +default['openstack']['network']['dhcp']['dnsmasq_checksum'] = 'f6cab8c64cb612089174f50927a05e2b' # The package architecture that will be built which should match the # archecture of the server this cookbook will run on which will be # amd64 or i386 -default["openstack"]["network"]["dhcp"]["dnsmasq_architecture"] = "amd64" +default['openstack']['network']['dhcp']['dnsmasq_architecture'] = 'amd64' # The debian package version that the above tarball will produce -default["openstack"]["network"]["dhcp"]["dnsmasq_dpkgversion"] = "2.65-1" +default['openstack']['network']['dhcp']['dnsmasq_dpkgversion'] = '2.65-1' + +# Limit number of leases to prevent a denial-of-service. +default['openstack']['network']['dhcp']['dnsmasq_lease_max'] = 16777216 # Upstream resolver to use # This will be used by dnsmasq to resolve recursively @@ -195,78 +295,77 @@ default["openstack"]["network"]["dhcp"]["dnsmasq_dpkgversion"] = "2.65-1" # 209.244.0.3 is Level3 # # May be a comma separated list of servers -default["openstack"]["network"]["dhcp"]["upstream_dns_servers"] = ["8.8.8.8", "209.244.0.3"] +default['openstack']['network']['dhcp']['upstream_dns_servers'] = ['8.8.8.8', '209.244.0.3'] # Set the default domain in dnsmasq -default["openstack"]["network"]["dhcp"]["default_domain"] = "openstacklocal" +default['openstack']['network']['dhcp']['default_domain'] = 'openstacklocal' # ============================= L3 Agent Configuration ===================== # The scheduler class to use for scheduling routers to L3 agents -default["openstack"]["network"]["l3"]["scheduler"] = "quantum.scheduler.l3_agent_scheduler.ChanceScheduler" +default['openstack']['network']['l3']['scheduler'] = 'neutron.scheduler.l3_agent_scheduler.ChanceScheduler' # If use_namespaces is set as False then the agent can only configure one router. # This is done by setting the specific router_id. -default["openstack"]["network"]["l3"]["router_id"] = nil +default['openstack']['network']['l3']['router_id'] = nil # Each L3 agent can be associated with at most one external network. This # value should be set to the UUID of that external network. If empty, # the agent will enforce that only a single external networks exists and # use that external network id -default["openstack"]["network"]["l3"]["gateway_external_network_id"] = nil +default['openstack']['network']['l3']['gateway_external_network_id'] = nil # Indicates that this L3 agent should also handle routers that do not have # an external network gateway configured. This option should be True only -# for a single agent in a Quantum deployment, and may be False for all agents +# for a single agent in a Neutron deployment, and may be False for all agents # if all routers must have an external network gateway -default["openstack"]["network"]["l3"]["handle_internal_only_routers"] = "True" +default['openstack']['network']['l3']['handle_internal_only_routers'] = 'True' # Name of bridge used for external network traffic. This should be set to # empty value for the linux bridge -default["openstack"]["network"]["l3"]["external_network_bridge"] = "br-ex" +default['openstack']['network']['l3']['external_network_bridge'] = 'br-ex' # Interface to use for external bridge. -default["openstack"]["network"]["l3"]["external_network_bridge_interface"] = "eth1" +default['openstack']['network']['l3']['external_network_bridge_interface'] = 'eth1' -# TCP Port used by Quantum metadata server -default["openstack"]["network"]["l3"]["metadata_port"] = 9697 +# TCP Port used by Neutron metadata server +default['openstack']['network']['l3']['metadata_port'] = 9697 # Send this many gratuitous ARPs for HA setup. Set it below or equal to 0 # to disable this feature. -default["openstack"]["network"]["l3"]["send_arp_for_ha"] = 3 +default['openstack']['network']['l3']['send_arp_for_ha'] = 3 # seconds between re-sync routers' data if needed -default["openstack"]["network"]["l3"]["periodic_interval"] = 40 +default['openstack']['network']['l3']['periodic_interval'] = 40 # seconds to start to sync routers' data after # starting agent -default["openstack"]["network"]["l3"]["periodic_fuzzy_delay"] = 5 +default['openstack']['network']['l3']['periodic_fuzzy_delay'] = 5 # ============================= Metadata Agent Configuration =============== # The location of the Nova Metadata API service to proxy to (nil uses default) -default["openstack"]["network"]["metadata"]["nova_metadata_ip"] = "127.0.0.1" -default["openstack"]["network"]["metadata"]["nova_metadata_port"] = 8775 +default['openstack']['network']['metadata']['nova_metadata_ip'] = '127.0.0.1' +default['openstack']['network']['metadata']['nova_metadata_port'] = 8775 # The name of the secret databag containing the metadata secret -default["openstack"]["network"]["metadata"]["secret_name"] = "quantum_metadata_secret" - +default['openstack']['network']['metadata']['secret_name'] = 'neutron_metadata_secret' # ============================= LBaaS Agent Configuration ================== -# Enable or disable quantum loadbalancer -default["openstack"]["network"]["quantum_loadbalancer"] = false +# node['openstack']['network']['neutron_loadbalancer'] is deprecated. Use +# node['openstack']['network']['service_plugins'] for the loadbalancer plugin. +# See that attribute for details. -# Plugin configuration path -default["openstack"]["network"]["lbaas_config_path"] = "/etc/quantum/plugins/services/agent_loadbalancer" +default['openstack']['network']['lbaas']['device_driver'] = 'neutron.services.loadbalancer.drivers.haproxy.namespace_driver.HaproxyNSDriver' -# Number of seconds between sync of LBaaS agent with Quantum API server -default["openstack"]["network"]["lbaas"]["periodic_interval"] = 10 +# Number of seconds between sync of LBaaS agent with Neutron API server +default['openstack']['network']['lbaas']['periodic_interval'] = 10 # Set lbaas plugin -# Supported types are: "ovs" (ovs based plugins(OVS, Ryu, NEC, NVP, BigSwitch/Floodlight)) -# and "linuxbridge". -default["openstack"]["network"]["lbaas_plugin"] = "ovs" +# Supported types are: 'ovs' (ovs based plugins(OVS, Ryu, NEC, NVP, BigSwitch/Floodlight)) +# and 'linuxbridge'. +default['openstack']['network']['lbaas_plugin'] = 'ovs' # ============================= OVS Plugin Configuration =================== @@ -276,7 +375,7 @@ default["openstack"]["network"]["lbaas_plugin"] = "ovs" # or change this to 'gre' and configure tunnel_id_ranges below in order for tenant # networks to provide connectivity between hosts. Set to 'none' to disable creation # of tenant networks. -default["openstack"]["network"]["openvswitch"]["tenant_network_type"] = 'gre' +default['openstack']['network']['openvswitch']['tenant_network_type'] = 'local' # Comma-separated list of [::] tuples enumerating # ranges of VLAN IDs on named physical networks that are available for allocation. @@ -286,44 +385,46 @@ default["openstack"]["network"]["openvswitch"]["tenant_network_type"] = 'gre' # networks may be created # # Example: network_vlan_ranges = physnet1:1000:2999 -default["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] = nil +default['openstack']['network']['openvswitch']['network_vlan_ranges'] = nil # Set to True in the server and the agents to enable support -# for GRE networks. Requires kernel support for OVS patch ports and -# GRE tunneling. -default["openstack"]["network"]["openvswitch"]["enable_tunneling"] = "True" +# for GRE or VXLAN networks. Requires kernel support for OVS patch ports and +# GRE or VXLAN tunneling. +# +# WARNING: This option will be deprecated in the Icehouse release, at which +# point setting tunnel_type below will be required to enable +# tunneling. +default['openstack']['network']['openvswitch']['enable_tunneling'] = 'False' + +# The type of tunnel network, if any, supported by the plugin. If +# this is set, it will cause tunneling to be enabled. If this is not set and +# the option enable_tunneling is set, this will default to 'gre'. +# 'gre' or 'vxlan' +default['openstack']['network']['openvswitch']['tunnel_type'] = '' # Comma-separated list of : tuples # enumerating ranges of GRE tunnel IDs that are available for tenant # network allocation if tenant_network_type is 'gre'. # # Example: tunnel_id_ranges = 1:1000 -default["openstack"]["network"]["openvswitch"]["tunnel_id_ranges"] = "1:10000" +default['openstack']['network']['openvswitch']['tunnel_id_ranges'] = nil # Do not change this parameter unless you have a good reason to. # This is the name of the OVS integration bridge. There is one per hypervisor. -# The integration bridge acts as a virtual "patch bay". All VM VIFs are -# attached to this bridge and then "patched" according to their network +# The integration bridge acts as a virtual 'patch bay'. All VM VIFs are +# attached to this bridge and then 'patched' according to their network # connectivity -default["openstack"]["network"]["openvswitch"]["integration_bridge"] = 'br-int' +default['openstack']['network']['openvswitch']['integration_bridge'] = 'br-int' # Only used for the agent if tunnel_id_ranges (above) is not empty for # the server. In most cases, the default value should be fine -default["openstack"]["network"]["openvswitch"]["tunnel_bridge"] = "br-tun" +default['openstack']['network']['openvswitch']['tunnel_bridge'] = 'br-tun' # Peer patch port in integration bridge for tunnel bridge (nil uses default) -default["openstack"]["network"]["openvswitch"]["int_peer_patch_port"] = nil +default['openstack']['network']['openvswitch']['int_peer_patch_port'] = nil # Peer patch port in tunnel bridge for integration bridge (nil uses default) -default["openstack"]["network"]["openvswitch"]["tun_peer_patch_port"] = nil - -# Uncomment this line for the agent if tunnel_id_ranges (above) is not -# empty for the server. Set local_ip to be the local IP address of -# this hypervisor or set the local_ip_interface parameter to use the IP -# address of the specified interface. If local_ip_interface is set -# it will take precedence. -default["openstack"]["network"]["openvswitch"]["local_ip"] = "127.0.0.1" -default["openstack"]["network"]["openvswitch"]["local_ip_interface"] = nil +default['openstack']['network']['openvswitch']['tun_peer_patch_port'] = nil # Comma-separated list of : tuples # mapping physical network names to the agent's node-specific OVS @@ -334,10 +435,56 @@ default["openstack"]["network"]["openvswitch"]["local_ip_interface"] = nil # server should have mappings to appropriate bridges on each agent. # # Example: bridge_mappings = physnet1:br-eth1 -default["openstack"]["network"]["openvswitch"]["bridge_mappings"] = nil +default['openstack']['network']['openvswitch']['bridge_mappings'] = nil -# Firewall driver for realizing quantum security group function -default["openstack"]["network"]["openvswitch"]["fw_driver"] = "quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver" +# Create OVS data network bridge for the physical network and configure it +# with the specified port. If nil or empty string is specified, the data +# network bridge will not be created. +# Format: : +# +# Example: bridge_mapping_interface = br-eth1:eth1 +default['openstack']['network']['openvswitch']['bridge_mapping_interface'] = nil + +# Agent's polling interval in seconds +default['openstack']['network']['openvswitch']['polling_interval'] = 2 + +# Firewall driver for realizing neutron security group function +default['openstack']['network']['openvswitch']['fw_driver'] = 'neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver' + +# Controls if neutron security group is enabled or not. +# It should be false when you use nova security group. +default['openstack']['network']['openvswitch']['enable_security_group'] = 'True' + +# OVS host for GRE tunnel. If bind_interface is set, it will set the host IP of +# this interface, otherwise use default host. +default['openstack']['network']['openvswitch']['host'] = '127.0.0.1' +default['openstack']['network']['openvswitch']['bind_interface'] = nil + + +# The newest version of OVS which comes with 12.04 Precise is 1.4.0 +# Which is legacy. Should we compile a newer version from source? +# If so, set ['openstack']['network']['openvswitch']['use_source_version'] +# to true, and configure the packages, versions, checksums, etc. you wish +# to use +default['openstack']['network']['openvswitch']['use_source_version'] = false + +# Url of the OVS source tarball +default['openstack']['network']['openvswitch']['openvswitch_url'] = 'http://git.openvswitch.org/cgi-bin/gitweb.cgi?p=openvswitch;a=snapshot;h=88dbaa9dc554d0ace1867bf23144788bd6f700bb;sf=tgz' + +# Filename the above URL actually downloads. +default['openstack']['network']['openvswitch']['openvswitch_filename'] = 'openvswitch-88dbaa9.tar.gz' + +# What is the base filename (essentially, the above without the .tar.gz) +default['openstack']['network']['openvswitch']['openvswitch_base_filename'] = 'openvswitch-88dbaa9' + +# Checksum of the file for verification +default['openstack']['network']['openvswitch']['openvswitch_checksum'] = '22df718eb81fcfe93228e9bba8575e50' + +# What version of the package does this install +default['openstack']['network']['openvswitch']['openvswitch_dpkgversion'] = '1.10.2-1' + +# What arch are we building for +default['openstack']['network']['openvswitch']['openvswitch_architecture'] = 'amd64' # ============================= LinuxBridge Plugin Configuration =========== @@ -347,7 +494,7 @@ default["openstack"]["network"]["openvswitch"]["fw_driver"] = "quantum.agent.lin # 'vlan' and configure network_vlan_ranges below in order for tenant # networks to provide connectivity between hosts. Set to 'none' to # disable creation of tenant networks. -default["openstack"]["network"]["linuxbridge"]["tenant_network_type"] = 'local' +default['openstack']['network']['linuxbridge']['tenant_network_type'] = 'local' # Comma-separated list of [::] tuples enumerating # ranges of VLAN IDs on named physical networks that are available for allocation. @@ -357,7 +504,7 @@ default["openstack"]["network"]["linuxbridge"]["tenant_network_type"] = 'local' # networks may be created. # # Example: network_vlan_ranges = physnet1:1000:2999 -default["openstack"]["network"]["linuxbridge"]["network_vlan_ranges"] = "" +default['openstack']['network']['linuxbridge']['network_vlan_ranges'] = '' # (ListOpt) Comma-separated list of # : tuples mapping physical @@ -367,32 +514,74 @@ default["openstack"]["network"]["linuxbridge"]["network_vlan_ranges"] = "" # mappings to appropriate interfaces on each agent. # # Example: physical_interface_mappings = physnet1:eth1 -default["openstack"]["network"]["linuxbridge"]["physical_interface_mappings"] = "" +default['openstack']['network']['linuxbridge']['physical_interface_mappings'] = '' + +# (BoolOpt) enable VXLAN on the agent +# VXLAN support can be enabled when agent is managed by ml2 plugin using +# linuxbridge mechanism driver. Useless if set while using linuxbridge plugin. +default['openstack']['network']['linuxbridge']['enable_vxlan'] = false + +# (IntOpt) use specific TTL for vxlan interface protocol packets +default['openstack']['network']['linuxbridge']['ttl'] = '' + +# (IntOpt) use specific TOS for vxlan interface protocol packets +default['openstack']['network']['linuxbridge']['tos'] = '' + +# (StrOpt) multicast group to use for broadcast emulation. +# This group must be the same on all the agents. +default['openstack']['network']['linuxbridge']['vxlan_group'] = '224.0.0.1' + +# (BoolOpt) Flag to enable l2population extension. This option should be used +# in conjunction with ml2 plugin l2population mechanism driver (in that case, +# both linuxbridge and l2population mechanism drivers should be loaded). +# It enables plugin to populate VXLAN forwarding table, in order to limit +# the use of broadcast emulation (multicast will be turned off if kernel and +# iproute2 supports unicast flooding - requires 3.11 kernel and iproute2 3.10) +default['openstack']['network']['linuxbridge']['l2_population'] = false + +# Agent's polling interval in seconds +default['openstack']['network']['linuxbridge']['polling_interval'] = 2 + +# (BoolOpt) Enable server RPC compatibility with old (pre-havana) +# agents. +# +# rpc_support_old_agents = False +# Example: rpc_support_old_agents = True +default['openstack']['network']['linuxbridge']['rpc_support_old_agents'] = false + +# Firewall driver for realizing neutron security group function +# firewall_driver = neutron.agent.firewall.NoopFirewallDriver +# Example: firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver +default['openstack']['network']['linuxbridge']['firewall_driver'] = 'neutron.agent.firewall.NoopFirewallDriver' + +# Controls if neutron security group is enabled or not. +# It should be false when you use nova security group. +default['openstack']['network']['linuxbridge']['enable_security_group'] = 'True' # ============================= BigSwitch Plugin Configuration ============= # Not really sure what this is... -default["openstack"]["network"]["bigswitch"]["servers"] = "localhost:8080" +default['openstack']['network']['bigswitch']['servers'] = 'localhost:8080' # ============================= Brocade Plugin Configuration =============== # username = -default["openstack"]["network"]["brocade"]["switch_username"] = "admin" +default['openstack']['network']['brocade']['switch_username'] = 'admin' # password = -default["openstack"]["network"]["brocade"]["switch_password"] = "admin" +default['openstack']['network']['brocade']['switch_password'] = 'admin' # address = -default["openstack"]["network"]["brocade"]["switch_address"] = "127.0.0.1" +default['openstack']['network']['brocade']['switch_address'] = '127.0.0.1' # ostype = NOS -default["openstack"]["network"]["brocade"]["switch_ostype"] = "NOS" +default['openstack']['network']['brocade']['switch_ostype'] = 'NOS' # physical_interface = # # Example: # physical_interface = physnet1 -default["openstack"]["network"]["brocade"]["physical_interface"] = "physnet1" +default['openstack']['network']['brocade']['physical_interface'] = 'physnet1' # (ListOpt) Comma-separated list of # [::] tuples enumerating ranges @@ -402,7 +591,7 @@ default["openstack"]["network"]["brocade"]["physical_interface"] = "physnet1" # # Default: network_vlan_ranges = # Example: network_vlan_ranges = physnet1:1000:2999 -default["openstack"]["network"]["brocade"]["network_vlan_ranges"] = "" +default['openstack']['network']['brocade']['network_vlan_ranges'] = '' # (ListOpt) Comma-separated list of # : tuples mapping physical @@ -412,44 +601,44 @@ default["openstack"]["network"]["brocade"]["network_vlan_ranges"] = "" # mappings to appropriate interfaces on each agent. # # Example: physical_interface_mappings = physnet1:eth1 -default["openstack"]["network"]["brocade"]["physical_interface_mappings"] = "" +default['openstack']['network']['brocade']['physical_interface_mappings'] = '' # ============================= Cisco Plugin Configuration ================= # The module and class name path for the nexus plugin -default["openstack"]["network"]["cisco"]["nexus_plugin"] = "quantum.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin" +default['openstack']['network']['cisco']['nexus_plugin'] = 'neutron.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin' # The module and class name path for the vswitch plugin -default["openstack"]["network"]["cisco"]["vswitch_plugin"] = "quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2" +default['openstack']['network']['cisco']['vswitch_plugin'] = 'neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2' # Start of the tenant VLAN range -default["openstack"]["network"]["cisco"]["vlan_start"] = 100 +default['openstack']['network']['cisco']['vlan_start'] = 100 # End of the tenant VLAN range -default["openstack"]["network"]["cisco"]["vlan_end"] = 3000 +default['openstack']['network']['cisco']['vlan_end'] = 3000 # Prefix for tenant VLANs -default["openstack"]["network"]["cisco"]["vlan_name_prefix"] = "q-" +default['openstack']['network']['cisco']['vlan_name_prefix'] = 'q-' # Maximum number of ports -default["openstack"]["network"]["cisco"]["max_ports"] = 100 +default['openstack']['network']['cisco']['max_ports'] = 100 # Max number of port profiles -default["openstack"]["network"]["cisco"]["max_port_profiles"] = 65568 +default['openstack']['network']['cisco']['max_port_profiles'] = 65568 # Maximum number of networks -default["openstack"]["network"]["cisco"]["max_networks"] = 65568 +default['openstack']['network']['cisco']['max_networks'] = 65568 # Module and class path for switch model -default["openstack"]["network"]["cisco"]["model_class"] = "quantum.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchModelV2" +default['openstack']['network']['cisco']['model_class'] = 'neutron.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchModelV2' # Module and class path for VLAN network manager -default["openstack"]["network"]["cisco"]["manager_class"] = "quantum.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr" +default['openstack']['network']['cisco']['manager_class'] = 'neutron.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr' # Module and class path for the Nexus driver -default["openstack"]["network"]["cisco"]["nexus_driver"] = "quantum.plugins.cisco.tests.unit.v2.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver" +default['openstack']['network']['cisco']['nexus_driver'] = 'neutron.plugins.cisco.tests.unit.v2.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver' # For each Nexus switch, add a hash to the -# node["openstack"]["network"]["cisco"]["nexus_switches"] Hash, +# node['openstack']['network']['cisco']['nexus_switches'] Hash, # using the switch's IP address as the outer Hash key with each # hash containing this information: # @@ -460,11 +649,11 @@ default["openstack"]["network"]["cisco"]["nexus_driver"] = "quantum.plugins.cisc # # Example: # -# node["openstack"]["network"]["cisco"]["nexus_switches"]["1.1.1.1"]["ssh_port"] = 22 -# node["openstack"]["network"]["cisco"]["nexus_switches"]["1.1.1.1"]["username"] = "admin" -# node["openstack"]["network"]["cisco"]["nexus_switches"]["1.1.1.1"]["password"] = "mySecretPassword" -# node["openstack"]["network"]["cisco"]["nexus_switches"]["1.1.1.1"]["hosts"] = [ [ "compute1", "1/1" ], -# [ "compute2", "1/2" ]] +# node['openstack']['network']['cisco']['nexus_switches']['1.1.1.1']['ssh_port'] = 22 +# node['openstack']['network']['cisco']['nexus_switches']['1.1.1.1']['username'] = 'admin' +# node['openstack']['network']['cisco']['nexus_switches']['1.1.1.1']['password'] = 'mySecretPassword' +# node['openstack']['network']['cisco']['nexus_switches']['1.1.1.1']['hosts'] = [ [ 'compute1', '1/1' ], +# [ 'compute2', '1/2' ]] # # # will write the following to the Cisco plugin config INI file: @@ -475,7 +664,7 @@ default["openstack"]["network"]["cisco"]["nexus_driver"] = "quantum.plugins.cisc # username=admin # password=mySecretPassword # -default["openstack"]["network"]["cisco"]["nexus_switches"] = {} +default['openstack']['network']['cisco']['nexus_switches'] = {} # ============================= Hyper-V Plugin Configuration =============== @@ -485,7 +674,7 @@ default["openstack"]["network"]["cisco"]["nexus_switches"] = {} # 'vlan' and configure network_vlan_ranges below in order for tenant # networks to provide connectivity between hosts. Set to 'none' to # disable creation of tenant networks. -default["openstack"]["network"]["hyperv"]["tenant_network_type"] = 'local' +default['openstack']['network']['hyperv']['tenant_network_type'] = 'local' # Comma-separated list of [::] tuples enumerating # ranges of VLAN IDs on named physical networks that are available for allocation. @@ -495,14 +684,14 @@ default["openstack"]["network"]["hyperv"]["tenant_network_type"] = 'local' # networks may be created. # # Example: network_vlan_ranges = physnet1:1000:2999 -default["openstack"]["network"]["hyperv"]["network_vlan_ranges"] = "" +default['openstack']['network']['hyperv']['network_vlan_ranges'] = '' # Agent's polling interval in seconds -default["openstack"]["network"]["hyperv"]["polling_interval"] = 2 +default['openstack']['network']['hyperv']['polling_interval'] = 2 # (ListOpt) Comma separated list of : # where the physical networks can be expressed with wildcards, -# e.g.: ."*:external". +# e.g.: .'*:external'. # The referred external virtual switches need to be already present on # the Hyper-V server. # If a given physical network name will not match any value in the list @@ -510,178 +699,181 @@ default["openstack"]["network"]["hyperv"]["polling_interval"] = 2 # # Default: physical_network_vswitch_mappings = *:external # Example: physical_network_vswitch_mappings = net1:external1,net2:external2 -default["openstack"]["network"]["hyperv"]["physical_network_vswitch_mappings"] = "*:external" +default['openstack']['network']['hyperv']['physical_network_vswitch_mappings'] = '*:external' # (StrOpt) Private virtual switch name used for local networking. # # Default: local_network_vswitch = private # Example: local_network_vswitch = custom_vswitch -default["openstack"]["network"]["hyperv"]["local_network_vswitch"] = "private" +default['openstack']['network']['hyperv']['local_network_vswitch'] = 'private' + +# Firewall driver for realizing neutron security group function +default['openstack']['network']['hyperv']['firewall_driver'] = 'neutron.plugins.hyperv.agent.security_groups_driver.HyperVSecurityGroupsDriver' # ============================= Metaplugin Plugin Configuration ============ -## This is list of flavor:quantum_plugins +## This is list of flavor:neutron_plugins # extension method is used in the order of this list -default["openstack"]["network"]["metaplugin"]["plugin_list"] = "openvswitch:quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2,linuxbridge:quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2" -default["openstack"]["network"]["metaplugin"]["l3_plugin_list"] = "openvswitch:quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2,linuxbridge:quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2" +default['openstack']['network']['metaplugin']['plugin_list'] = 'openvswitch:neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2,linuxbridge:neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2' +default['openstack']['network']['metaplugin']['l3_plugin_list'] = 'openvswitch:neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2,linuxbridge:neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2' -# Default "flavor" for L2 and L3 -default["openstack"]["network"]["metaplugin"]["default_flavor"] = "openvswitch" -default["openstack"]["network"]["metaplugin"]["default_l3_flavor"] = "openvswitch" +# Default 'flavor' for L2 and L3 +default['openstack']['network']['metaplugin']['default_flavor'] = 'openvswitch' +default['openstack']['network']['metaplugin']['default_l3_flavor'] = 'openvswitch' # ============================= Midonet Plugin Configuration =============== # MidoNet API server URI -default["openstack"]["network"]["midonet"]["midonet_uri"] = "http://localhost:8080/midonet-api" +default['openstack']['network']['midonet']['midonet_uri'] = 'http://localhost:8080/midonet-api' # MidoNet admin username -default["openstack"]["network"]["midonet"]["username"] = "admin" +default['openstack']['network']['midonet']['username'] = 'admin' # MidoNet admin password -default["openstack"]["network"]["midonet"]["password"] = "passw0rd" +default['openstack']['network']['midonet']['password'] = 'passw0rd' # ID of the project that MidoNet admin user belongs to -default["openstack"]["network"]["midonet"]["project_id"] = "77777777-7777-7777-7777-777777777777" +default['openstack']['network']['midonet']['project_id'] = '77777777-7777-7777-7777-777777777777' # Virtual provider router ID -default["openstack"]["network"]["midonet"]["provider_router_id"] = "00112233-0011-0011-0011-001122334455" +default['openstack']['network']['midonet']['provider_router_id'] = '00112233-0011-0011-0011-001122334455' # Virtual metadata router ID -default["openstack"]["network"]["midonet"]["metadata_router_id"] = "ffeeddcc-ffee-ffee-ffee-ffeeddccbbaa" +default['openstack']['network']['midonet']['metadata_router_id'] = 'ffeeddcc-ffee-ffee-ffee-ffeeddccbbaa' # ============================= NEC Plugin Configuration =================== # Do not change this parameter unless you have a good reason to. # This is the name of the OVS integration bridge. There is one per hypervisor. -# The integration bridge acts as a virtual "patch port". All VM VIFs are -# attached to this bridge and then "patched" according to their network +# The integration bridge acts as a virtual 'patch port'. All VM VIFs are +# attached to this bridge and then 'patched' according to their network # connectivity. -default["openstack"]["network"]["nec"]["integration_bridge"] = "br-int" +default['openstack']['network']['nec']['integration_bridge'] = 'br-int' # Agent's polling interval in seconds -default["openstack"]["network"]["nec"]["polling_interval"] = 2 +default['openstack']['network']['nec']['polling_interval'] = 2 -# Firewall driver for realizing quantum security group function -default["openstack"]["network"]["nec"]["firewall_driver"] = "quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver" +# Firewall driver for realizing neutron security group function +default['openstack']['network']['nec']['firewall_driver'] = 'neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver' # Specify OpenFlow Controller Host, Port and Driver to connect. -default["openstack"]["network"]["nec"]["ofc_host"] = "127.0.0.1" -default["openstack"]["network"]["nec"]["ofc_port"] = 8888 +default['openstack']['network']['nec']['ofc_host'] = '127.0.0.1' +default['openstack']['network']['nec']['ofc_port'] = 8888 -# Drivers are in quantum/plugins/nec/drivers/ . -default["openstack"]["network"]["nec"]["ofc_driver"] = "trema" +# Drivers are in neutron/plugins/nec/drivers/ . +default['openstack']['network']['nec']['ofc_driver'] = 'trema' # PacketFilter is available when it's enabled in this configuration # and supported by the driver. -default["openstack"]["network"]["nec"]["ofc_enable_packet_filter"] = "true" +default['openstack']['network']['nec']['ofc_enable_packet_filter'] = 'true' # ============================= Nicira Plugin Configuration ================ # User name for NVP controller -default["openstack"]["network"]["nicira"]["nvp_user"] = "admin" +default['openstack']['network']['nicira']['nvp_user'] = 'admin' # Password for NVP controller -default["openstack"]["network"]["nicira"]["nvp_password"] = "admin" +default['openstack']['network']['nicira']['nvp_password'] = 'admin' # Total time limit for a cluster request # (including retries across different controllers) -default["openstack"]["network"]["nicira"]["req_timeout"] = 30 +default['openstack']['network']['nicira']['req_timeout'] = 30 # Time before aborting a request on an unresponsive controller -default["openstack"]["network"]["nicira"]["http_timeout"] = 10 +default['openstack']['network']['nicira']['http_timeout'] = 10 # Maximum number of times a particular request should be retried -default["openstack"]["network"]["nicira"]["retries"] = 2 +default['openstack']['network']['nicira']['retries'] = 2 # Maximum number of times a redirect response should be followed -default["openstack"]["network"]["nicira"]["redirects"] = 2 +default['openstack']['network']['nicira']['redirects'] = 2 # Comma-separated list of NVP controller endpoints (:). When port # is omitted, 443 is assumed. This option MUST be specified, e.g.: -default["openstack"]["network"]["nicira"]["nvp_controllers"] = "xx.yy.zz.ww:443, aa.bb.cc.dd, ee.ff.gg.hh.ee:80" +default['openstack']['network']['nicira']['nvp_controllers'] = 'xx.yy.zz.ww:443, aa.bb.cc.dd, ee.ff.gg.hh.ee:80' # UUID of the pre-existing default NVP Transport zone to be used for creating -# tunneled isolated "Quantum" networks. This option MUST be specified, e.g.: -default["openstack"]["network"]["nicira"]["default_tz_uuid"] = "1e8e52cf-fa7f-46b0-a14a-f99835a9cb53" +# tunneled isolated 'Neutron' networks. This option MUST be specified, e.g.: +default['openstack']['network']['nicira']['default_tz_uuid'] = '1e8e52cf-fa7f-46b0-a14a-f99835a9cb53' # (Optional) UUID of the cluster in NVP. It can be retrieved from NVP management -# console "admin" section. -default["openstack"]["network"]["nicira"]["nvp_cluster_uuid"] = "615be8e4-82e9-4fd2-b4b3-fd141e51a5a7" +# console 'admin' section. +default['openstack']['network']['nicira']['nvp_cluster_uuid'] = '615be8e4-82e9-4fd2-b4b3-fd141e51a5a7' # (Optional) UUID for the default l3 gateway service to use with this cluster. # To be specified if planning to use logical routers with external gateways. -default["openstack"]["network"]["nicira"]["default_l3_gw_service_uuid"] = "" +default['openstack']['network']['nicira']['default_l3_gw_service_uuid'] = '' # (Optional) UUID for the default l2 gateway service to use with this cluster. # To be specified for providing a predefined gateway tenant for connecting their networks. -default["openstack"]["network"]["nicira"]["default_l2_gw_service_uuid"] = "" +default['openstack']['network']['nicira']['default_l2_gw_service_uuid'] = '' # Name of the default interface name to be used on network-gateway. This value # will be used for any device associated with a network gateway for which an # interface name was not specified -default["openstack"]["network"]["nicira"]["default_iface_name"] = "breth0" +default['openstack']['network']['nicira']['default_iface_name'] = 'breth0' # number of network gateways allowed per tenant, -1 means unlimited -default["openstack"]["network"]["nicira"]["quota_network_gateway"] = 5 +default['openstack']['network']['nicira']['quota_network_gateway'] = 5 # Maximum number of ports for each bridged logical switch -default["openstack"]["network"]["nicira"]["max_lp_per_bridged_ls"] = 64 +default['openstack']['network']['nicira']['max_lp_per_bridged_ls'] = 64 # Maximum number of ports for each overlay (stt, gre) logical switch -default["openstack"]["network"]["nicira"]["max_lp_per_overlay_ls"] = 256 +default['openstack']['network']['nicira']['max_lp_per_overlay_ls'] = 256 # Number of connects to each controller node. -default["openstack"]["network"]["nicira"]["concurrent_connections"] = 3 +default['openstack']['network']['nicira']['concurrent_connections'] = 3 # Acceptable values for 'metadata_mode' are: # - 'access_network': this enables a dedicated connection to the metadata -# proxy for metadata server access via Quantum router. +# proxy for metadata server access via Neutron router. # - 'dhcp_host_route': this enables host route injection via the dhcp agent. # This option is only useful if running on a host that does not support # namespaces otherwise access_network should be used. -default["openstack"]["network"]["nicira"]["metadata_mode"] = "access_network" +default['openstack']['network']['nicira']['metadata_mode'] = 'access_network' # ============================= PLUMGrid Plugin Configuration ============== # This line should be pointing to the NOS server, # for the PLUMgrid platform. In other deployments, # this is known as controller -default["openstack"]["network"]["plumgrid"]["nos_server"] = "127.0.0.1" -default["openstack"]["network"]["plumgrid"]["nos_server_port"] = "" +default['openstack']['network']['plumgrid']['nos_server'] = '127.0.0.1' +default['openstack']['network']['plumgrid']['nos_server_port'] = '' # Authentification parameters for the NOS server. # These are the admin credentials to manage and control # the NOS server. -default["openstack"]["network"]["plumgrid"]["username"] = "" -default["openstack"]["network"]["plumgrid"]["password"] = "" -default["openstack"]["network"]["plumgrid"]["servertimeout"] = 5 +default['openstack']['network']['plumgrid']['username'] = '' +default['openstack']['network']['plumgrid']['password'] = '' +default['openstack']['network']['plumgrid']['servertimeout'] = 5 # Name of the network topology to be deployed by NOS -default["openstack"]["network"]["plumgrid"]["topologyname"] = "" +default['openstack']['network']['plumgrid']['topologyname'] = '' # ============================= Ryu Plugin Configuration =================== # Do not change this parameter unless you have a good reason to. # This is the name of the OVS integration bridge. There is one per hypervisor. -# The integration bridge acts as a virtual "patch port". All VM VIFs are -# attached to this bridge and then "patched" according to their network +# The integration bridge acts as a virtual 'patch port'. All VM VIFs are +# attached to this bridge and then 'patched' according to their network # connectivity. -default["openstack"]["network"]["ryu"]["integration_bridge"] = "br-int" +default['openstack']['network']['ryu']['integration_bridge'] = 'br-int' # openflow_rest_api = : -default["openstack"]["network"]["ryu"]["openflow_rest_api"] = "127.0.0.1:8080" +default['openstack']['network']['ryu']['openflow_rest_api'] = '127.0.0.1:8080' # tunnel key range: 0 < tunnel_key_min < tunnel_key_max # VLAN: 12bits, GRE, VXLAN: 24bits -default["openstack"]["network"]["ryu"]["tunnel_key_min"] = 1 -default["openstack"]["network"]["ryu"]["tunnel_key_max"] = "0xffffff" +default['openstack']['network']['ryu']['tunnel_key_min'] = 1 +default['openstack']['network']['ryu']['tunnel_key_max'] = '0xffffff' # tunnel_ip = # tunnel_interface = interface for tunneling # when tunnel_ip is NOT specified, ip address is read # from this interface -default["openstack"]["network"]["ryu"]["tunnel_ip"] = "" -default["openstack"]["network"]["ryu"]["tunnel_interface"] = "eth0" +default['openstack']['network']['ryu']['tunnel_ip'] = '' +default['openstack']['network']['ryu']['tunnel_interface'] = 'eth0' # ovsdb_port = port number on which ovsdb is listening # ryu-agent uses this parameter to setup ovsdb. @@ -692,100 +884,173 @@ default["openstack"]["network"]["ryu"]["tunnel_interface"] = "eth0" # ovsdb_interface = interface for ovsdb # when ovsdb_addr NOT specifiied, ip address is gotten # from this interface -default["openstack"]["network"]["ryu"]["ovsdb_port"] = 6634 -default["openstack"]["network"]["ryu"]["ovsdb_ip"] = "" -default["openstack"]["network"]["ryu"]["ovsdb_interface"] = "eth0" +default['openstack']['network']['ryu']['ovsdb_port'] = 6634 +default['openstack']['network']['ryu']['ovsdb_ip'] = '' +default['openstack']['network']['ryu']['ovsdb_interface'] = 'eth0' -# Firewall driver for realizing quantum security group function -default["openstack"]["network"]["ryu"]["firewall_driver"] = "quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver" +# Firewall driver for realizing neutron security group function +default['openstack']['network']['ryu']['firewall_driver'] = 'neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver' # Agent's polling interval in seconds -default["openstack"]["network"]["ryu"]["polling_interval"] = 2 +default['openstack']['network']['ryu']['polling_interval'] = 2 + +# ============================= ML2 Plugin Configuration =================== +# (ListOpt) List of network type driver entrypoints to be loaded from +# the neutron.ml2.type_drivers namespace. +# +# type_drivers = local,flat,vlan,gre,vxlan +# Example: type_drivers = flat,vlan,gre,vxlan +default['openstack']['network']['ml2']['type_drivers'] = 'local,flat,vlan,gre,vxlan' + +# (ListOpt) Ordered list of network_types to allocate as tenant +# networks. The default value 'local' is useful for single-box testing +# but provides no connectivity between hosts. +# +# tenant_network_types = local +# Example: tenant_network_types = vlan,gre,vxlan +default['openstack']['network']['ml2']['tenant_network_types'] = 'local' + +# (ListOpt) Ordered list of networking mechanism driver entrypoints +# to be loaded from the neutron.ml2.mechanism_drivers namespace. +# mechanism_drivers = +# Example: mechanism_drivers = arista +# Example: mechanism_drivers = cisco,logger +default['openstack']['network']['ml2']['mechanism_drivers'] = 'openvswitch' + +# (ListOpt) List of physical_network names with which flat networks +# can be created. Use * to allow flat networks with arbitrary +# physical_network names. +# +# flat_networks = +# Example:flat_networks = physnet1,physnet2 +# Example:flat_networks = * +default['openstack']['network']['ml2']['flat_networks'] = '' + +# (ListOpt) List of [::] tuples +# specifying physical_network names usable for VLAN provider and +# tenant networks, as well as ranges of VLAN tags on each +# physical_network available for allocation as tenant networks. +# +# network_vlan_ranges = +# Example: network_vlan_ranges = physnet1:1000:2999,physnet2 +default['openstack']['network']['ml2']['network_vlan_ranges'] = '' + +# (ListOpt) Comma-separated list of : tuples enumerating +# ranges of GRE tunnel IDs that are available for tenant network allocation +default['openstack']['network']['ml2']['tunnel_id_ranges'] = '' + +# (ListOpt) Comma-separated list of : tuples enumerating +# ranges of VXLAN VNI IDs that are available for tenant network allocation. +default['openstack']['network']['ml2']['vni_ranges'] = '' + +# (StrOpt) Multicast group for the VXLAN interface. When configured, will +# enable sending all broadcast traffic to this multicast group. When left +# unconfigured, will disable multicast VXLAN mode. +# +# vxlan_group = +# Example: vxlan_group = 239.1.1.1 +default['openstack']['network']['ml2']['vxlan_group'] = '' + +# Controls if neutron security group is enabled or not. +# It should be false when you use nova security group. +default['openstack']['network']['ml2']['enable_security_group'] = 'True' + +# Misc option support +# Allow additional strings to be added to neutron.conf +# For example: ['# Comment', 'key=value'] +default['openstack']['network']['misc_neutron'] = [] # platform-specific settings -case platform -when "fedora", "redhat", "centos" # :pragma-foodcritic: ~FC024 - won't fix this - default["openstack"]["network"]["platform"] = { - "user" => "quantum", - "group" => "quantum", - "mysql_python_packages" => [ "MySQL-python" ], - "postgresql_python_packages" => ["python-psycopg2"], - "nova_network_packages" => [ "openstack-nova-network" ], - "quantum_packages" => [ "openstack-quantum" ], - "quantum_client_packages" => [], - "quantum_dhcp_packages" => [ "openstack-quantum" ], - "quantum_dhcp_build_packages" => [], - "quantum_l3_packages" => [ "openstack-quantum" ], - "quantum_openvswitch_packages" => ["openvswitch", "bridge-utils"], - "quantum_openvswitch_agent_packages" => ["openstack-quantum-openvswitch"], - "quantum_linuxbridge_agent_packages" => ["openstack-quantum-linuxbridge"], - "quantum_metadata_agent_packages" => [], - "quantum_plugin_package" => "openstack-quantum-%plugin%", - "quantum_server_packages" => [], - "quantum_dhcp_agent_service" => "quantum-dhcp-agent", - "quantum_l3_agent_service" => "quantum-l3-agent", - "quantum_metadata_agent_service" => "quantum-metadata-agent", - "quantum_openvswitch_service" => "openvswitch", - "quantum_openvswitch_agent_service" => "quantum-openvswitch-agent", - "quantum_linuxbridge_agent_service" => "quantum-linuxbridge-agent", - "quantum_server_service" => "quantum-server", - "package_overrides" => "" +case platform_family +when 'fedora', 'rhel' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['network']['platform'] = { + 'user' => 'neutron', + 'group' => 'neutron', + 'mysql_python_packages' => ['MySQL-python'], + 'db2_python_packages' => ['python-ibm-db', 'python-ibm-db-sa'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'nova_network_packages' => ['openstack-nova-network'], + 'neutron_packages' => ['iproute', 'pyparsing', 'python-cliff', 'python-neutron', 'openstack-neutron'], + 'neutron_client_packages' => ['python-neutronclient'], + 'neutron_dhcp_packages' => ['openstack-neutron'], + 'neutron_dhcp_build_packages' => [], + 'neutron_l3_packages' => ['openstack-neutron'], + 'neutron_lb_packages' => ['openstack-neutron', 'haproxy'], + 'neutron_openvswitch_packages' => ['openvswitch'], + 'neutron_openvswitch_agent_packages' => ['openstack-neutron-openvswitch'], + 'neutron_linuxbridge_agent_packages' => ['openstack-neutron-linuxbridge'], + 'neutron_metadata_agent_packages' => [], + 'neutron_plugin_package' => 'openstack-neutron-%plugin%', + 'neutron_server_packages' => ['openstack-neutron-ml2'], + 'neutron_dhcp_agent_service' => 'neutron-dhcp-agent', + 'neutron_l3_agent_service' => 'neutron-l3-agent', + 'neutron_lb_agent_service' => 'neutron-lbaas-agent', + 'neutron_metadata_agent_service' => 'neutron-metadata-agent', + 'neutron_openvswitch_service' => 'openvswitch', + 'neutron_openvswitch_agent_service' => 'neutron-openvswitch-agent', + 'neutron_linuxbridge_agent_service' => 'neutron-linuxbridge-agent', + 'neutron_server_service' => 'neutron-server', + 'package_overrides' => '' } -when "suse" - default["openstack"]["network"]["platform"] = { - "user" => "openstack-quantum", - "group" => "openstack-quantum", - "mysql_python_packages" => ["python-mysql"], - "postgresql_python_packages" => ["python-psycopg2"], - "nova_network_packages" => ["openstack-nova-network"], - "quantum_packages" => ["openstack-quantum"], - "quantum_client_packages" => [], - "quantum_dhcp_packages" => ["openstack-quantum-dhcp-agent"], - "quantum_dhcp_build_packages" => [], - "quantum_l3_packages" => ["openstack-quantum-l3-agent"], - # plugins are installed by the main openstack-quantum package on SUSE - "quantum_plugin_package" => "", - "quantum_metadata_agent_packages" => ["openstack-quantum-metadata-agent"], - "quantum_openvswitch_packages" => ["openvswitch-switch"], - "quantum_openvswitch_agent_packages" => ["openstack-quantum-openvswitch-agent"], - "quantum_linuxbridge_agent_packages" => ["openstack-quantum-linuxbridge-agent"], - "quantum_metadata_agent_packages" => ["openstack-quantum-metadata-agent"], - "quantum_server_packages" => [], - "quantum_dhcp_agent_service" => "openstack-quantum-dhcp-agent", - "quantum_l3_agent_service" => "openstack-quantum-l3-agent", - "quantum_metadata_agent_service" => "openstack-quantum-metadata-agent", - "quantum_openvswitch_service" => "openvswitch-switch", - "quantum_openvswitch_agent_service" => "openstack-quantum-openvswitch-agent", - "quantum_linuxbridge_agent_service" => "openstack-quantum-linuxbridge-agent", - "quantum_server_service" => "openstack-quantum", - "package_overrides" => "" +when 'suse' + default['openstack']['network']['platform'] = { + 'user' => 'openstack-neutron', + 'group' => 'openstack-neutron', + 'mysql_python_packages' => ['python-mysql'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'nova_network_packages' => ['openstack-nova-network'], + 'neutron_packages' => ['openstack-neutron'], + 'neutron_client_packages' => ['python-neutronclient'], + 'neutron_dhcp_packages' => ['openstack-neutron-dhcp-agent'], + 'neutron_dhcp_build_packages' => [], + 'neutron_l3_packages' => ['openstack-neutron-l3-agent'], + 'neutron_lb_packages' => ['openstack-neutron-lbaas-agent'], + # plugins are installed by the main openstack-neutron package on SUSE + 'neutron_plugin_package' => '', + 'neutron_metadata_agent_packages' => ['openstack-neutron-metadata-agent'], + 'neutron_openvswitch_packages' => ['openvswitch-switch'], + 'neutron_openvswitch_agent_packages' => ['openstack-neutron-openvswitch-agent'], + 'neutron_linuxbridge_agent_packages' => ['openstack-neutron-linuxbridge-agent'], + 'neutron_metadata_agent_packages' => ['openstack-neutron-metadata-agent'], + 'neutron_server_packages' => [], + 'neutron_dhcp_agent_service' => 'openstack-neutron-dhcp-agent', + 'neutron_l3_agent_service' => 'openstack-neutron-l3-agent', + 'neutron_lb_agent_service' => 'openstack-neutron-lbaas-agent', + 'neutron_metadata_agent_service' => 'openstack-neutron-metadata-agent', + 'neutron_openvswitch_service' => 'openvswitch-switch', + 'neutron_openvswitch_agent_service' => 'openstack-neutron-openvswitch-agent', + 'neutron_linuxbridge_agent_service' => 'openstack-neutron-linuxbridge-agent', + 'neutron_server_service' => 'openstack-neutron', + 'package_overrides' => '' } -when "ubuntu" - default["openstack"]["network"]["platform"] = { - "user" => "quantum", - "group" => "quantum", - "mysql_python_packages" => [ "python-mysqldb" ], - "postgresql_python_packages" => [ "python-psycopg2" ], - "nova_network_packages" => [ "nova-network" ], - "quantum_lb_packages" => ["quantum-lbaas-agent", "haproxy"], - "quantum_packages" => [ "quantum-common", "python-pyparsing", "python-cliff" ], - "quantum_client_packages" => [ "python-quantumclient", "python-pyparsing" ], - "quantum_dhcp_packages" => [ "quantum-dhcp-agent" ], - "quantum_dhcp_build_packages" => [ "build-essential", "pkg-config", "libidn11-dev", "libdbus-1-dev", "libnetfilter-conntrack-dev", "gettext" ], - "quantum_l3_packages" => [ "quantum-l3-agent" ], - "quantum_openvswitch_packages" => [ "openvswitch-switch", "openvswitch-datapath-dkms", "bridge-utils" ], - "quantum_openvswitch_agent_packages" => [ "quantum-plugin-openvswitch", "quantum-plugin-openvswitch-agent" ], - "quantum_linuxbridge_agent_packages" => [ "quantum-plugin-linuxbridge", "quantum-plugin-linuxbridge-agent" ], - "quantum_metadata_agent_packages" => [ "quantum-metadata-agent" ], - "quantum_plugin_package" => "quantum-plugin-%plugin%", - "quantum_server_packages" => ["quantum-server"], - "quantum_dhcp_agent_service" => "quantum-dhcp-agent", - "quantum_l3_agent_service" => "quantum-l3-agent", - "quantum_metadata_agent_service" => "quantum-metadata-agent", - "quantum_openvswitch_service" => "openvswitch-switch", - "quantum_openvswitch_agent_service" => "quantum-plugin-openvswitch-agent", - "quantum_linuxbridge_agent_service" => "quantum-plugin-linuxbridge-agent", - "quantum_server_service" => "quantum-server", - "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" +when 'debian' + default['openstack']['network']['platform'] = { + 'user' => 'neutron', + 'group' => 'neutron', + 'mysql_python_packages' => ['python-mysqldb'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'nova_network_packages' => ['nova-network'], + 'neutron_packages' => ['neutron-common', 'python-pyparsing', 'python-cliff'], + 'neutron_client_packages' => ['python-neutronclient', 'python-pyparsing'], + 'neutron_dhcp_packages' => ['neutron-dhcp-agent'], + 'neutron_dhcp_build_packages' => %w(build-essential pkg-config libidn11-dev libdbus-1-dev libnetfilter-conntrack-dev gettext), + 'neutron_l3_packages' => ['neutron-l3-agent'], + 'neutron_lb_packages' => ['neutron-lbaas-agent', 'haproxy'], + 'neutron_openvswitch_packages' => ['openvswitch-switch', 'openvswitch-datapath-dkms', 'bridge-utils'], + 'neutron_openvswitch_build_packages' => %w(build-essential pkg-config fakeroot libssl-dev openssl debhelper autoconf dkms python-all python-qt4 python-zopeinterface python-twisted-conch), + 'neutron_openvswitch_agent_packages' => ['neutron-plugin-openvswitch', 'neutron-plugin-openvswitch-agent'], + 'neutron_linuxbridge_agent_packages' => ['neutron-plugin-linuxbridge', 'neutron-plugin-linuxbridge-agent'], + 'neutron_metadata_agent_packages' => ['neutron-metadata-agent'], + 'neutron_plugin_package' => 'neutron-plugin-%plugin%', + 'neutron_server_packages' => ['neutron-server'], + 'neutron_dhcp_agent_service' => 'neutron-dhcp-agent', + 'neutron_l3_agent_service' => 'neutron-l3-agent', + 'neutron_lb_agent_service' => 'neutron-lbaas-agent', + 'neutron_metadata_agent_service' => 'neutron-metadata-agent', + 'neutron_openvswitch_service' => 'openvswitch-switch', + 'neutron_openvswitch_agent_service' => 'neutron-plugin-openvswitch-agent', + 'neutron_linuxbridge_agent_service' => 'neutron-plugin-linuxbridge-agent', + 'neutron_server_service' => 'neutron-server', + 'package_overrides' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" } end diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/debug.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/debug.filters deleted file mode 100644 index 6dbb4d7..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/debug.filters +++ /dev/null @@ -1,14 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# This is needed because we should ping -# from inside a namespace which requires root -ping: RegExpFilter, /bin/ping, root, ping, -w, \d+, -c, \d+, [0-9\.]+ -ping6: RegExpFilter, /bin/ping6, root, ping6, -w, \d+, -c, \d+, [0-9A-Fa-f:]+ diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/dhcp.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/dhcp.filters deleted file mode 100644 index 89abb02..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/dhcp.filters +++ /dev/null @@ -1,40 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# dhcp-agent -ip_exec_dnsmasq: DnsmasqNetnsFilter, /sbin/ip, root -dnsmasq: DnsmasqFilter, /sbin/dnsmasq, root -dnsmasq_usr: DnsmasqFilter, /usr/sbin/dnsmasq, root -# dhcp-agent uses kill as well, that's handled by the generic KillFilter -# it looks like these are the only signals needed, per -# quantum/agent/linux/dhcp.py -kill_dnsmasq: KillFilter, root, /sbin/dnsmasq, -9, -HUP -kill_dnsmasq_usr: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP - -# dhcp-agent uses cat -cat: RegExpFilter, /bin/cat, root, cat, /proc/\d+/cmdline -ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root -ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root -ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root -ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root - -# metadata proxy -metadata_proxy: CommandFilter, /usr/bin/quantum-ns-metadata-proxy, root -# If installed from source (say, by devstack), the prefix will be -# /usr/local instead of /usr/bin. -metadata_proxy_local: CommandFilter, /usr/local/bin/quantum-ns-metadata-proxy, root -kill_metadata7: KillFilter, root, /usr/bin/python2.7, -9 -kill_metadata6: KillFilter, root, /usr/bin/python2.6, -9 - -# ip_lib -ip: IpFilter, /sbin/ip, root -ip_usr: IpFilter, /usr/sbin/ip, root -ip_exec: IpNetnsExecFilter, /sbin/ip, root -ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/iptables-firewall.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/iptables-firewall.filters deleted file mode 100644 index 2049e0e..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/iptables-firewall.filters +++ /dev/null @@ -1,21 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# quantum/agent/linux/iptables_manager.py -# "iptables-save", ... -iptables-save: CommandFilter, /sbin/iptables-save, root -iptables-restore: CommandFilter, /sbin/iptables-restore, root -ip6tables-save: CommandFilter, /sbin/ip6tables-save, root -ip6tables-restore: CommandFilter, /sbin/ip6tables-restore, root - -# quantum/agent/linux/iptables_manager.py -# "iptables", "-A", ... -iptables: CommandFilter, /sbin/iptables, root -ip6tables: CommandFilter, /sbin/ip6tables, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/l3.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/l3.filters deleted file mode 100644 index ec08d59..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/l3.filters +++ /dev/null @@ -1,43 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# arping -arping: CommandFilter, /usr/bin/arping, root -arping_sbin: CommandFilter, /sbin/arping, root - -# l3_agent -sysctl: CommandFilter, /sbin/sysctl, root -route: CommandFilter, /sbin/route, root - -# metadata proxy -metadata_proxy: CommandFilter, /usr/bin/quantum-ns-metadata-proxy, root -# If installed from source (say, by devstack), the prefix will be -# /usr/local instead of /usr/bin. -metadata_proxy_local: CommandFilter, /usr/local/bin/quantum-ns-metadata-proxy, root -kill_metadata7: KillFilter, root, /usr/bin/python2.7, -9 -kill_metadata6: KillFilter, root, /usr/bin/python2.6, -9 - -# ip_lib -ip: IpFilter, /sbin/ip, root -ip_usr: IpFilter, /usr/sbin/ip, root -ip_exec: IpNetnsExecFilter, /sbin/ip, root -ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root - -# ovs_lib (if OVSInterfaceDriver is used) -ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root -ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root -ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root -ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root - -# iptables_manager -iptables-save: CommandFilter, /sbin/iptables-save, root -iptables-restore: CommandFilter, /sbin/iptables-restore, root -ip6tables-save: CommandFilter, /sbin/ip6tables-save, root -ip6tables-restore: CommandFilter, /sbin/ip6tables-restore, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/lbaas-haproxy.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/lbaas-haproxy.filters deleted file mode 100644 index e00a719..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/lbaas-haproxy.filters +++ /dev/null @@ -1,29 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# haproxy -haproxy: CommandFilter, /usr/sbin/haproxy, root - -# lbaas-agent uses kill as well, that's handled by the generic KillFilter -kill_haproxy_usr: KillFilter, root, /usr/sbin/haproxy, -9, -HUP - -# lbaas-agent uses cat -cat: RegExpFilter, /bin/cat, root, cat, /proc/\d+/cmdline - -ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root -ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root -ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root -ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root - -# ip_lib -ip: IpFilter, /sbin/ip, root -ip_usr: IpFilter, /usr/sbin/ip, root -ip_exec: IpNetnsExecFilter, /sbin/ip, root -ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/linuxbridge-plugin.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/linuxbridge-plugin.filters deleted file mode 100644 index 301280c..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/linuxbridge-plugin.filters +++ /dev/null @@ -1,21 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# linuxbridge-agent -# unclear whether both variants are necessary, but I'm transliterating -# from the old mechanism -brctl: CommandFilter, /sbin/brctl, root -brctl_usr: CommandFilter, /usr/sbin/brctl, root - -# ip_lib -ip: IpFilter, /sbin/ip, root -ip_usr: IpFilter, /usr/sbin/ip, root -ip_exec: IpNetnsExecFilter, /sbin/ip, root -ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/nec-plugin.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/nec-plugin.filters deleted file mode 100644 index 6d8f9c2..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/nec-plugin.filters +++ /dev/null @@ -1,15 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# nec_quantum_agent -ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root -ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root -ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root -ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/openvswitch-plugin.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/openvswitch-plugin.filters deleted file mode 100644 index c316448..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/openvswitch-plugin.filters +++ /dev/null @@ -1,29 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# openvswitch-agent -# unclear whether both variants are necessary, but I'm transliterating -# from the old mechanism -ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root -ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root -ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root -ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root -ovs-ofctl: CommandFilter, /bin/ovs-ofctl, root -ovs-ofctl_usr: CommandFilter, /usr/bin/ovs-ofctl, root -ovs-ofctl_sbin: CommandFilter, /sbin/ovs-ofctl, root -ovs-ofctl_sbin_usr: CommandFilter, /usr/sbin/ovs-ofctl, root -xe: CommandFilter, /sbin/xe, root -xe_usr: CommandFilter, /usr/sbin/xe, root - -# ip_lib -ip: IpFilter, /sbin/ip, root -ip_usr: IpFilter, /usr/sbin/ip, root -ip_exec: IpNetnsExecFilter, /sbin/ip, root -ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/ryu-plugin.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/ryu-plugin.filters deleted file mode 100644 index 696c7d3..0000000 --- a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/ryu-plugin.filters +++ /dev/null @@ -1,25 +0,0 @@ -# quantum-rootwrap command filters for nodes on which quantum is -# expected to control network -# -# This file should be owned by (and only-writeable by) the root user - -# format seems to be -# cmd-name: filter-name, raw-command, user, args - -[Filters] - -# ryu-agent -# unclear whether both variants are necessary, but I'm transliterating -# from the old mechanism - -# quantum/plugins/ryu/agent/ryu_quantum_agent.py: -# "ovs-vsctl", "--timeout=2", ... -ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root -ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root -ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root -ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root - -# quantum/plugins/ryu/agent/ryu_quantum_agent.py: -# "xe", "vif-param-get", ... -xe: CommandFilter, /bin/xe, root -xe_usr: CommandFilter, /usr/bin/xe, root diff --git a/chef/cookbooks/openstack-network/files/default/neutron-ha-tool.py b/chef/cookbooks/openstack-network/files/default/neutron-ha-tool.py new file mode 100755 index 0000000..c1da046 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/neutron-ha-tool.py @@ -0,0 +1,549 @@ +#! /usr/bin/env python + +# Copyright 2013 AT&T Services, Inc. +# All Rights Reserved. +# +# 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. + + +import logging +import os +import sys +import argparse +import random +import time +from logging.handlers import SysLogHandler +from collections import OrderedDict +from neutronclient.neutron import client + + +LOG = logging.getLogger('neutron-ha-tool') +LOG_FORMAT = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s' +LOG_DATE = '%m-%d %H:%M' +DESCRIPTION = "neutron High Availability Tool" +TAKEOVER_DELAY = int(random.random()*30+30) + + +def parse_args(): + + # ensure environment has necessary items to authenticate + for key in ['OS_TENANT_NAME', 'OS_USERNAME', 'OS_PASSWORD', + 'OS_AUTH_URL']: + if key not in os.environ.keys(): + LOG.exception("Your environment is missing '%s'") + + ap = argparse.ArgumentParser(description=DESCRIPTION) + ap.add_argument('-d', '--debug', action='store_true', + default=False, help='Show debugging output') + ap.add_argument('-q', '--quiet', action='store_true', default=False, + help='Only show error and warning messages') + ap.add_argument('-n', '--noop', action='store_true', default=False, + help='Do not do any modifying operations (dry-run)') + ap.add_argument('--l3-agent-check', action='store_true', default=False, + help='Show routers associated with offline l3 agents') + ap.add_argument('--l3-agent-migrate', action='store_true', default=False, + help='Migrate routers away from offline l3 agents') + ap.add_argument('--l3-agent-rebalance', action='store_true', default=False, + help='Rebalance router count on all l3 agents') + ap.add_argument('--replicate-dhcp', action='store_true', default=False, + help='Replicate DHCP configuration to all agents') + ap.add_argument('--now', action='store_true', default=False, + help='Migrate Routers immediately without a delay.') + ap.add_argument('--insecure', action='store_true', default=False, + help='Explicitly allow neutron-ha-tool to perform ' + '"insecure" SSL (https) requests. The server\'s ' + 'certificate will not be verified against any ' + 'certificate authorities. This option should be used ' + 'with caution.') + return ap.parse_args() + + +def setup_logging(args): + level = logging.INFO + if args.quiet: + level = logging.WARN + if args.debug: + level = logging.DEBUG + logging.basicConfig(level=level, format=LOG_FORMAT, date_fmt=LOG_DATE) + handler = SysLogHandler(address='/dev/log') + syslog_formatter = logging.Formatter('%(name)s: %(levelname)s %(message)s') + handler.setFormatter(syslog_formatter) + LOG.addHandler(handler) + + +def run(args): + try: + ca = os.environ['OS_CACERT'] + except KeyError: + ca = None + + # instantiate client + qclient = client.Client('2.0', auth_url=os.environ['OS_AUTH_URL'], + username=os.environ['OS_USERNAME'], + tenant_name=os.environ['OS_TENANT_NAME'], + password=os.environ['OS_PASSWORD'], + endpoint_type='internalURL', + insecure=args.insecure, + ca_cert=ca) + + # set json return type + qclient.format = 'json' + + if args.l3_agent_check: + LOG.info("Performing L3 Agent Health Check") + l3_agent_check(qclient) + + if args.l3_agent_migrate: + LOG.info("Performing L3 Agent Migration for Offline L3 Agents") + l3_agent_migrate(qclient, args.noop, args.now) + + if args.l3_agent_rebalance: + LOG.info("Rebalancing L3 Agent Router Count") + l3_agent_rebalance(qclient, args.noop) + + if args.replicate_dhcp: + LOG.info("Performing DHCP Replication of Networks to Agents") + replicate_dhcp(qclient, args.noop) + + +def l3_agent_rebalance(qclient, noop=False): + """ + Rebalance l3 agent router count across agents. The number of routers + on each l3 agent will be as close as possible which should help + distribute load as new l3 agents come online. + + :param qclient: A neutronclient + :param noop: Optional noop flag + """ + + # {u'binary': u'neutron-l3-agent', + # u'description': None, + # u'admin_state_up': True, + # u'heartbeat_timestamp': u'2013-07-02 22:20:23', + # u'alive': True, + # u'topic': u'l3_agent', + # u'host': u'o3r3.int.san3.attcompute.com', + # u'agent_type': u'L3 agent', + # u'created_at': u'2013-07-02 14:50:58', + # u'started_at': u'2013-07-02 18:00:55', + # u'id': u'6efe494a-616c-41ea-9c8f-2c592f4d46ff', + # u'configurations': { + # u'router_id': u'', + # u'gateway_external_network_id': u'', + # u'handle_internal_only_routers': True, + # u'use_namespaces': True, + # u'routers': 5, + # u'interfaces': 3, + # u'floating_ips': 9, + # u'interface_driver': + # u'neutron.agent.linux.interface.OVSInterfaceDriver', + # u'ex_gw_ports': 3} + # } + + l3_agent_dict = {} + agents = list_agents(qclient, agent_type='L3 agent') + num_agents = len(agents) + if num_agents <= 1: + LOG.info("No rebalancing required for 1 or fewer agents") + return + + for l3_agent in agents: + l3_agent_dict[l3_agent['id']] = \ + list_routers_on_l3_agent(qclient, l3_agent['id']) + + ordered_l3_agent_dict = OrderedDict(sorted(l3_agent_dict.items(), + key=lambda t: len(t[0]))) + ordered_l3_agent_list = list(ordered_l3_agent_dict) + num_agents = len(ordered_l3_agent_list) + LOG.info("Agent list: %s", ordered_l3_agent_list[0:(num_agents-1/2)+1]) + i = 0 + for agent in ordered_l3_agent_list[0:num_agents-1/2]: + low_agent_id = ordered_l3_agent_list[i] + hgh_agent_id = ordered_l3_agent_list[-(i+1)] + + # do nothing if we end up comparing the same router + if low_agent_id == hgh_agent_id: + continue + + LOG.info("Examining low_agent=%s, high_agent=%s", + low_agent_id, hgh_agent_id) + + low_agent_router_count = len(l3_agent_dict[low_agent_id]) + hgh_agent_router_count = len(l3_agent_dict[hgh_agent_id]) + + LOG.info("Low Count=%s, High Count=%s", + low_agent_router_count, hgh_agent_router_count) + + for router_id in l3_agent_dict[hgh_agent_id]: + if low_agent_router_count >= hgh_agent_router_count: + break + else: + LOG.info("Migrating router=%s from agent=%s to agent=%s", + router_id, hgh_agent_id, low_agent_id) + try: + if not noop: + migrate_router(qclient, router_id, hgh_agent_id, + low_agent_id) + low_agent_router_count += 1 + hgh_agent_router_count -= 1 + except: + LOG.exception("Failed to migrate router=%s from agent=%s " + "to agent=%s", router_id, hgh_agent_id, + low_agent_id) + continue + i += 1 + + +def l3_agent_check(qclient): + """ + Walk the l3 agents searching for agents that are offline. Show routers + that are offline and where we would migrate them to. + + :param qclient: A neutronclient + + """ + + migration_count = 0 + agent_list = list_agents(qclient) + agent_dead_list = agent_dead_id_list(agent_list, 'L3 agent') + agent_alive_list = agent_alive_id_list(agent_list, 'L3 agent') + LOG.info("There are %s offline L3 agents and %s online L3 agents", + len(agent_dead_list), len(agent_alive_list)) + + if len(agent_dead_list) > 0: + for agent_id in agent_dead_list: + LOG.info("Querying agent_id=%s for routers to migrate", agent_id) + router_id_list = list_routers_on_l3_agent(qclient, agent_id) + + for router_id in router_id_list: + try: + target_id = random.choice(agent_alive_list) + except IndexError: + LOG.warn("There are no l3 agents alive we could " + "migrate routers onto.") + target_id = None + + migration_count += 1 + LOG.warn("Would like to migrate router=%s to agent=%s", + router_id, target_id) + + if migration_count > 0: + sys.exit(2) + + +def l3_agent_migrate(qclient, noop=False, now=False): + """ + Walk the l3 agents searching for agents that are offline. For those that + are offline, we will retrieve a list of routers on them and migrate them to + a random l3 agent that is online. + + :param qclient: A neutronclient + :param noop: Optional noop flag + :param now: Optional. If false (the default), we'll wait for a random + amount of time (between 30 and 60 seconds) before migration. If + true, routers are migrated immediately. + + """ + + migration_count = 0 + agent_list = list_agents(qclient) + agent_dead_list = agent_dead_id_list(agent_list, 'L3 agent') + agent_alive_list = agent_alive_id_list(agent_list, 'L3 agent') + LOG.info("There are %s offline L3 agents and %s online L3 agents", + len(agent_dead_list), len(agent_alive_list)) + + if len(agent_dead_list) > 0: + if len(agent_alive_list) < 1: + LOG.exception("There are no l3 agents alive to migrate " + "routers onto") + + timeout = 0 + if not now: + while timeout < TAKEOVER_DELAY: + + agent_list_new = list_agents(qclient) + agent_dead_list_new = agent_dead_id_list(agent_list_new, + 'L3 agent') + if len(agent_dead_list_new) < len(agent_dead_list): + LOG.info("Skipping router failover since an agent came " + "online while ensuring agents offline for %s " + "seconds", TAKEOVER_DELAY) + sys.exit(0) + + LOG.info("Agent found offline for seconds=%s but waiting " + "seconds=%s before migration", + timeout, TAKEOVER_DELAY) + timeout += 1 + time.sleep(1) + + for agent_id in agent_dead_list: + LOG.info("Querying agent_id=%s for routers to migrate", agent_id) + router_id_list = list_routers_on_l3_agent(qclient, agent_id) + + for router_id in router_id_list: + + target_id = random.choice(agent_alive_list) + LOG.info("Migrating router=%s to agent=%s", + router_id, target_id) + + try: + if not noop: + migrate_router(qclient, router_id, agent_id, target_id) + migration_count += 1 + except: + LOG.exception("There was an error migrating a router") + continue + + LOG.info("%s routers required migration from offline L3 agents", + migration_count) + + +def replicate_dhcp(qclient, noop=False): + """ + Retrieve a network list and then probe each DHCP agent to ensure + they have that network assigned. + + :param qclient: A neutronclient + :param noop: Optional noop flag + + """ + + added = 0 + networks = list_networks(qclient) + network_id_list = [n['id'] for n in networks] + agents = list_agents(qclient, agent_type='DHCP agent') + LOG.info("Replicating %s networks to %s DHCP agents", len(networks), + len(agents)) + for dhcp_agent_id in [a['id'] for a in agents]: + networks_on_agent = \ + qclient.list_networks_on_dhcp_agent(dhcp_agent_id)['networks'] + network_id_on_agent = [n['id'] for n in networks_on_agent] + for network_id in network_id_list: + if network_id not in network_id_on_agent: + try: + dhcp_body = {'network_id': network_id} + if not noop: + qclient.add_network_to_dhcp_agent(dhcp_agent_id, + dhcp_body) + LOG.info("Added missing network=%s to dhcp agent=%s", + network_id, dhcp_agent_id) + added += 1 + except: + LOG.exception("Failed to add network_id=%s to" + "dhcp_agent=%s", network_id, dhcp_agent_id) + continue + + LOG.info("Added %s networks to DHCP agents", added) + + +def migrate_router(qclient, router_id, agent_id, target_id): + """ + Returns nothing, and raises on exception + + :param qclient: A neutronclient + :param router_id: The id of the router to migrate + :param agent_id: The id of the l3 agent to migrate from + :param target_id: The id of the l3 agent to migrate to + """ + + # N.B. The neutron API will return "success" even when there is a + # subsequent failure during the add or remove process so we must check to + # ensure the router has been added or removed + + # remove the router from the dead agent + qclient.remove_router_from_l3_agent(agent_id, router_id) + + # ensure it is removed or log an error + if router_id in list_routers_on_l3_agent(qclient, agent_id): + LOG.exception("Failed to remove router_id=%s from agent_id=%s", + router_id, agent_id) + + # add the router id to a live agent + router_body = {'router_id': router_id} + qclient.add_router_to_l3_agent(target_id, router_body) + + # ensure it is removed or log an error + if router_id not in list_routers_on_l3_agent(qclient, target_id): + LOG.exception("Failed to add router_id=%s from agent_id=%s", + router_id, agent_id) + + +def list_networks(qclient): + """ + Return a list of network objects + + :param qclient: A neutronclient + """ + + resp = qclient.list_networks() + LOG.debug("list_networks: %s", resp) + return resp['networks'] + + +def list_dhcp_agent_networks(qclient, agent_id): + """ + Return a list of network ids assigned to a particular DHCP agent + + :param qclient: A neutronclient + :param agent_id: A DHCP agent id + """ + + resp = qclient.list_networks_on_dhcp_agent(agent_id) + LOG.debug("list_networks_on_dhcp_agent: %s", resp) + return [s['id'] for s in resp['networks']] + + +def list_routers(qclient): + """ + Return a list of router objects + + :param qclient: A neutronclient + + # {'routers': [ + # {u'status': u'ACTIVE', + # u'external_gateway_info': + # {u'network_id': u'b970297c-d80e-4527-86d7-e49d2da9fdef'}, + # u'name': u'router1', + # u'admin_state_up': True, + # u'tenant_id': u'5603b97ee7f047ea999e25492c7fcb23', + # u'routes': [], + # u'id': u'0a122e5c-1623-412e-8c53-a1e21d1daff8'} + # ]} + + """ + + resp = qclient.list_routers() + LOG.debug("list_routers: %s", resp) + return resp['routers'] + + +def list_routers_on_l3_agent(qclient, agent_id): + """ + Return a list of router ids on an agent + + :param qclient: A neutronclient + """ + + resp = qclient.list_routers_on_l3_agent(agent_id) + LOG.debug("list_routers_on_l3_agent: %s", resp) + return [r['id'] for r in resp['routers']] + + +def list_agents(qclient, agent_type=None): + """Return a list of agent objects + + :param qclient: A neutronclient + + + # {u'agents': [ + + # {u'binary': u'neutron-openvswitch-agent', + # u'description': None, + # u'admin_state_up': True, + # u'heartbeat_timestamp': u'2013-07-02 22:20:25', + # u'alive': True, + # u'topic': u'N/A', + # u'host': u'o3r3.int.san3.attcompute.com', + # u'agent_type': u'Open vSwitch agent', + # u'created_at': u'2013-07-02 14:50:57', + # u'started_at': u'2013-07-02 14:50:57', + # u'id': u'3a577f1d-d86e-4f1a-a395-8d4c8e4df1e2', + # u'configurations': {u'devices': 10}}, + + # {u'binary': u'neutron-dhcp-agent', + # u'description': None, + # u'admin_state_up': True, + # u'heartbeat_timestamp': u'2013-07-02 22:20:23', + # u'alive': True, + # u'topic': u'dhcp_agent', + # u'host': u'o5r4.int.san3.attcompute.com', + # u'agent_type': u'DHCP agent', + # u'created_at': u'2013-06-26 16:21:02', + # u'started_at': u'2013-06-28 13:32:52', + # u'id': u'3e8be28e-05a0-472b-9288-a59f8d8d2271', + # u'configurations': { + # u'subnets': 4, + # u'use_namespaces': True, + # u'dhcp_driver': u'neutron.agent.linux.dhcp.Dnsmasq', + # u'networks': 4, + # u'dhcp_lease_time': 120, + # u'ports': 38}}, + + + # {u'binary': u'neutron-l3-agent', + # u'description': None, + # u'admin_state_up': True, + # u'heartbeat_timestamp': u'2013-07-02 22:20:23', + # u'alive': True, + # u'topic': u'l3_agent', + # u'host': u'o3r3.int.san3.attcompute.com', + # u'agent_type': u'L3 agent', + # u'created_at': u'2013-07-02 14:50:58', + # u'started_at': u'2013-07-02 18:00:55', + # u'id': u'6efe494a-616c-41ea-9c8f-2c592f4d46ff', + # u'configurations': { + # u'router_id': u'', + # u'gateway_external_network_id': u'', + # u'handle_internal_only_routers': True, + # u'use_namespaces': True, + # u'routers': 5, + # u'interfaces': 3, + # u'floating_ips': 9, + # u'interface_driver': + # u'neutron.agent.linux.interface.OVSInterfaceDriver', + # u'ex_gw_ports': 3}}, + + """ + + resp = qclient.list_agents() + LOG.debug("list_agents: %s", resp) + if agent_type: + return [agent for agent in resp['agents'] + if agent['agent_type'] == agent_type] + return resp['agents'] + + +def agent_alive_id_list(agent_list, agent_type): + """ + Return a list of agents that are alive from an API list of agents + + :param agent_list: API response for list_agents() + + """ + return [agent['id'] for agent in agent_list + if agent['agent_type'] == agent_type and agent['alive'] is True] + + +def agent_dead_id_list(agent_list, agent_type): + """ + Return a list of agents that are dead from an API list of agents + + :param agent_list: API response for list_agents() + + """ + return [agent['id'] for agent in agent_list + if agent['agent_type'] == agent_type and agent['alive'] is False] + + +if __name__ == '__main__': + args = parse_args() + setup_logging(args) + + try: + run(args) + sys.exit(0) + except Exception as err: + LOG.error(err) + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) diff --git a/chef/cookbooks/openstack-network/files/default/ovs-dpctl-top b/chef/cookbooks/openstack-network/files/default/ovs-dpctl-top new file mode 100644 index 0000000..de6f2b0 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/ovs-dpctl-top @@ -0,0 +1,1687 @@ +#!/usr/bin/env python +# +# Copyright (c) 2013 Nicira, Inc. +# +# 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. +# +# +# The approximate_size code was copied from +# http://getpython3.com/diveintopython3/your-first-python-program.html#divingin +# which is licensed under # "Dive Into Python 3," Copyright 2011 Mark Pilgrim, +# used under a Creative Commons Attribution-Share-Alike license: +# http://creativecommons.org/licenses/by-sa/3.0/ +# +# + +"""Top like behavior for ovs-dpctl dump-flows output. + +This program summarizes ovs-dpctl flow content by aggregating the number +of packets, total bytes and occurrence of the following fields: + + - Datapath in_port + + - Ethernet type + + - Source and destination MAC addresses + + - IP protocol + + - Source and destination IPv4 addresses + + - Source and destination IPv6 addresses + + - UDP and TCP destination port + + - Tunnel source and destination addresses + + +Output shows four values: + - FIELDS: the flow fields for example in_port(1). + + - PACKETS: the total number of packets containing the flow field. + + - BYTES: the total number of bytes containing the flow field. If units are + not present then values are in bytes. + + - AVERAGE: the average packets size (BYTES/PACKET). + + - COUNT: the number of lines in the dump-flow output contain the flow field. + +Top Behavior + +While in top mode, the default behavior, the following single character +commands are supported: + + a - toggles top in accumulate and live mode. Accumulate mode is described + below. + + s - toggles which column is used to sort content in decreasing order. A + DESC title is placed over the column. + + _ - a space indicating to collect dump-flow content again + + h - halt output. Any character will restart sampling + + f - cycle through flow fields + + q - q for quit. + +Accumulate Mode + +There are two supported modes: live and accumulate. The default is live. +The parameter --accumulate or the 'a' character in top mode enables the +latter. In live mode, recent dump-flow content is presented. +Where as accumulate mode keeps track of the prior historical +information until the flow is reset not when the flow is purged. Reset +flows are determined when the packet count for a flow has decreased from +its previous sample. There is one caveat, eventually the system will +run out of memory if, after the accumulate-decay period any flows that +have not been refreshed are purged. The goal here is to free memory +of flows that are not active. Statistics are not decremented. Their purpose +is to reflect the overall history of the flow fields. + + +Debugging Errors + +Parsing errors are counted and displayed in the status line at the beginning +of the output. Use the --verbose option with --script to see what output + was not parsed, like this: +$ ovs-dpctl dump-flows | ovs-dpctl-top --script --verbose + +Error messages will identify content that failed to parse. + + +Access Remote Hosts + +The --host must follow the format user@hostname. This script simply calls +'ssh user@Hostname' without checking for login credentials therefore public +keys should be installed on the system identified by hostname, such as: + +$ ssh-copy-id user@hostname + +Consult ssh-copy-id man pages for more details. + + +Expected usage + +$ ovs-dpctl-top + +or to run as a script: +$ ovs-dpctl dump-flows > dump-flows.log +$ ovs-dpctl-top --script --flow-file dump-flows.log + +""" + +# pylint: disable-msg=C0103 +# pylint: disable-msg=C0302 +# pylint: disable-msg=R0902 +# pylint: disable-msg=R0903 +# pylint: disable-msg=R0904 +# pylint: disable-msg=R0912 +# pylint: disable-msg=R0913 +# pylint: disable-msg=R0914 + +import sys +import os +try: + ## + # Arg parse is not installed on older Python distributions. + # ovs ships with a version in the directory mentioned below. + import argparse +except ImportError: + sys.path.append(os.path.join("@pkgdatadir@", "python")) + import argparse +import logging +import re +import unittest +import copy +import curses +import operator +import subprocess +import fcntl +import struct +import termios +import datetime +import threading +import time +import socket + + +## +# The following two definitions provide the necessary netaddr functionality. +# Python netaddr module is not part of the core installation. Packaging +# netaddr was involved and seems inappropriate given that only two +# methods where used. +def ipv4_to_network(ip_str): + """ Calculate the network given a ipv4/mask value. + If a mask is not present simply return ip_str. + """ + pack_length = '!HH' + try: + (ip, mask) = ip_str.split("/") + except ValueError: + # just an ip address no mask. + return ip_str + + ip_p = socket.inet_pton(socket.AF_INET, ip) + ip_t = struct.unpack(pack_length, ip_p) + mask_t = struct.unpack(pack_length, socket.inet_pton(socket.AF_INET, mask)) + network_n = [ii & jj for (ii, jj) in zip(ip_t, mask_t)] + + return socket.inet_ntop(socket.AF_INET, + struct.pack('!HH', network_n[0], network_n[1])) + + +def ipv6_to_network(ip_str): + """ Calculate the network given a ipv6/mask value. + If a mask is not present simply return ip_str. + """ + pack_length = '!HHHHHHHH' + try: + (ip, mask) = ip_str.split("/") + except ValueError: + # just an ip address no mask. + return ip_str + + ip_p = socket.inet_pton(socket.AF_INET6, ip) + ip_t = struct.unpack(pack_length, ip_p) + mask_t = struct.unpack(pack_length, + socket.inet_pton(socket.AF_INET6, mask)) + network_n = [ii & jj for (ii, jj) in zip(ip_t, mask_t)] + + return socket.inet_ntop(socket.AF_INET6, + struct.pack(pack_length, + network_n[0], network_n[1], + network_n[2], network_n[3], + network_n[4], network_n[5], + network_n[6], network_n[7])) + + +## +# columns displayed +## +class Columns: + """ Holds column specific content. + Titles needs to be less than 8 characters. + """ + VALUE_WIDTH = 9 + FIELDS = "fields" + PACKETS = "packets" + COUNT = "count" + BYTES = "bytes" + AVERAGE = "average" + + def __init__(self): + pass + + @staticmethod + def assoc_list(obj): + """ Return a associated list. """ + return [(Columns.FIELDS, repr(obj)), + (Columns.PACKETS, obj.packets), + (Columns.BYTES, obj.bytes), + (Columns.COUNT, obj.count), + (Columns.AVERAGE, obj.average), + ] + + +def element_eth_get(field_type, element, stats_dict): + """ Extract eth frame src and dst from a dump-flow element.""" + fmt = "%s(src=%s,dst=%s)" + + element = fmt % (field_type, element["src"], element["dst"]) + return SumData(field_type, element, stats_dict["packets"], + stats_dict["bytes"], element) + + +def element_ipv4_get(field_type, element, stats_dict): + """ Extract src and dst from a dump-flow element.""" + fmt = "%s(src=%s,dst=%s)" + element_show = fmt % (field_type, element["src"], element["dst"]) + + element_key = fmt % (field_type, ipv4_to_network(element["src"]), + ipv4_to_network(element["dst"])) + + return SumData(field_type, element_show, stats_dict["packets"], + stats_dict["bytes"], element_key) + + +def element_tunnel_get(field_type, element, stats_dict): + """ Extract src and dst from a tunnel.""" + return element_ipv4_get(field_type, element, stats_dict) + + +def element_ipv6_get(field_type, element, stats_dict): + """ Extract src and dst from a dump-flow element.""" + + fmt = "%s(src=%s,dst=%s)" + element_show = fmt % (field_type, element["src"], element["dst"]) + + element_key = fmt % (field_type, ipv6_to_network(element["src"]), + ipv6_to_network(element["dst"])) + + return SumData(field_type, element_show, stats_dict["packets"], + stats_dict["bytes"], element_key) + + +def element_dst_port_get(field_type, element, stats_dict): + """ Extract src and dst from a dump-flow element.""" + element_key = "%s(dst=%s)" % (field_type, element["dst"]) + return SumData(field_type, element_key, stats_dict["packets"], + stats_dict["bytes"], element_key) + + +def element_passthrough_get(field_type, element, stats_dict): + """ Extract src and dst from a dump-flow element.""" + element_key = "%s(%s)" % (field_type, element) + return SumData(field_type, element_key, + stats_dict["packets"], stats_dict["bytes"], element_key) + + +# pylint: disable-msg=R0903 +class OutputFormat: + """ Holds field_type and function to extract element value. """ + def __init__(self, field_type, generator): + self.field_type = field_type + self.generator = generator + +OUTPUT_FORMAT = [ + OutputFormat("eth", element_eth_get), + OutputFormat("ipv4", element_ipv4_get), + OutputFormat("ipv6", element_ipv6_get), + OutputFormat("tunnel", element_tunnel_get), + OutputFormat("udp", element_dst_port_get), + OutputFormat("tcp", element_dst_port_get), + OutputFormat("eth_type", element_passthrough_get), + OutputFormat("in_port", element_passthrough_get) + ] + + +ELEMENT_KEY = { + "udp": "udp.dst", + "tcp": "tcp.dst" + } + + +def top_input_get(args): + """ Return subprocess stdout.""" + cmd = [] + if (args.host): + cmd += ["ssh", args.host] + cmd += ["ovs-dpctl", "dump-flows"] + + return subprocess.Popen(cmd, stderr=subprocess.STDOUT, + stdout=subprocess.PIPE).stdout + + +def args_get(): + """ read program parameters handle any necessary validation of input. """ + + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__) + ## + # None is a special value indicating to read flows from stdin. + # This handles the case + # ovs-dpctl dump-flows | ovs-dpctl-flows.py + parser.add_argument("-v", "--version", version="@VERSION@", + action="version", help="show version") + parser.add_argument("-f", "--flow-file", dest="flowFiles", default=None, + action="append", + help="file containing flows from ovs-dpctl dump-flow") + parser.add_argument("-V", "--verbose", dest="verbose", + default=logging.CRITICAL, + action="store_const", const=logging.DEBUG, + help="enable debug level verbosity") + parser.add_argument("-s", "--script", dest="top", action="store_false", + help="Run from a script (no user interface)") + parser.add_argument("--host", dest="host", + help="Specify a user@host for retrieving flows see" + "Accessing Remote Hosts for more information") + + parser.add_argument("-a", "--accumulate", dest="accumulate", + action="store_true", default=False, + help="Accumulate dump-flow content") + parser.add_argument("--accumulate-decay", dest="accumulateDecay", + default=5.0 * 60, type=float, + help="Decay old accumulated flows. " + "The default is 5 minutes. " + "A value of 0 disables decay.") + parser.add_argument("-d", "--delay", dest="delay", type=int, + default=1000, + help="Delay in milliseconds to collect dump-flow " + "content (sample rate).") + + args = parser.parse_args() + + logging.basicConfig(level=args.verbose) + + return args + +### +# Code to parse a single line in dump-flow +### +# key(values) +FIELDS_CMPND = re.compile("([\w]+)\((.+)\)") +# key:value +FIELDS_CMPND_ELEMENT = re.compile("([\w:]+)=([/\.\w:]+)") +FIELDS_ELEMENT = re.compile("([\w]+):([-\.\w]+)") + + +def flow_line_iter(line): + """ iterate over flow dump elements. + return tuples of (true, element) or (false, remaining element) + """ + # splits by , except for when in a (). Actions element was not + # split properly but we don't need it. + rc = [] + + element = "" + paren_count = 0 + + for ch in line: + if (ch == '('): + paren_count += 1 + elif (ch == ')'): + paren_count -= 1 + + if (ch == ' '): + # ignore white space. + continue + elif ((ch == ',') and (paren_count == 0)): + rc.append(element) + element = "" + else: + element += ch + + if (paren_count): + raise ValueError(line) + else: + if (len(element) > 0): + rc.append(element) + return rc + + +def flow_line_compound_parse(compound): + """ Parse compound element + for example + src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03 + which is in + eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03) + """ + result = {} + for element in flow_line_iter(compound): + match = FIELDS_CMPND_ELEMENT.search(element) + if (match): + key = match.group(1) + value = match.group(2) + result[key] = value + + match = FIELDS_CMPND.search(element) + if (match): + key = match.group(1) + value = match.group(2) + result[key] = flow_line_compound_parse(value) + continue + + if (len(result.keys()) == 0): + return compound + return result + + +def flow_line_split(line): + """ Convert a flow dump line into ([fields], [stats], actions) tuple. + Where fields and stats are lists. + This function relies on a the following ovs-dpctl dump-flow + output characteristics: + 1. The dumpe flow line consists of a list of frame fields, list of stats + and action. + 2. list of frame fields, each stat and action field are delimited by ', '. + 3. That all other non stat field are not delimited by ', '. + + """ + + results = re.split(', ', line) + + (field, stats, action) = (results[0], results[1:-1], results[-1]) + + fields = flow_line_iter(field) + return (fields, stats, action) + + +def elements_to_dict(elements): + """ Convert line to a hierarchy of dictionaries. """ + result = {} + for element in elements: + match = FIELDS_CMPND.search(element) + if (match): + key = match.group(1) + value = match.group(2) + result[key] = flow_line_compound_parse(value) + continue + + match = FIELDS_ELEMENT.search(element) + if (match): + key = match.group(1) + value = match.group(2) + result[key] = value + else: + raise ValueError("can't parse >%s<" % element) + return result + + +# pylint: disable-msg=R0903 +class SumData(object): + """ Interface that all data going into SumDb must implement. + Holds the flow field and its corresponding count, total packets, + total bytes and calculates average. + + __repr__ is used as key into SumData singleton. + __str__ is used as human readable output. + """ + + def __init__(self, field_type, field, packets, flow_bytes, key): + # Count is the number of lines in the dump-flow log. + self.field_type = field_type + self.field = field + self.count = 1 + self.packets = int(packets) + self.bytes = int(flow_bytes) + self.key = key + + def decrement(self, decr_packets, decr_bytes, decr_count): + """ Decrement content to calculate delta from previous flow sample.""" + self.packets -= decr_packets + self.bytes -= decr_bytes + self.count -= decr_count + + def __iadd__(self, other): + """ Add two objects. """ + + if (self.key != other.key): + raise ValueError("adding two unrelated types") + + self.count += other.count + self.packets += other.packets + self.bytes += other.bytes + return self + + def __isub__(self, other): + """ Decrement two objects. """ + + if (self.key != other.key): + raise ValueError("adding two unrelated types") + + self.count -= other.count + self.packets -= other.packets + self.bytes -= other.bytes + return self + + def __getattr__(self, name): + """ Handle average. """ + if (name == "average"): + if (self.packets == 0): + return float(0.0) + else: + return float(self.bytes) / float(self.packets) + raise AttributeError(name) + + def __str__(self): + """ Used for debugging. """ + return "%s %s %s %s" % (self.field, self.count, + self.packets, self.bytes) + + def __repr__(self): + """ Used as key in the FlowDB table. """ + return self.key + + +def flow_aggregate(fields_dict, stats_dict): + """ Search for content in a line. + Passed the flow port of the dump-flows plus the current stats consisting + of packets, bytes, etc + """ + result = [] + + for output_format in OUTPUT_FORMAT: + field = fields_dict.get(output_format.field_type, None) + if (field): + obj = output_format.generator(output_format.field_type, + field, stats_dict) + result.append(obj) + + return result + + +def flows_read(ihdl, flow_db): + """ read flow content from ihdl and insert into flow_db. """ + + done = False + while (not done): + line = ihdl.readline() + if (len(line) == 0): + # end of input + break + + try: + flow_db.flow_line_add(line) + except ValueError, arg: + logging.error(arg) + + return flow_db + + +def get_terminal_size(): + """ + return column width and height of the terminal + """ + for fd_io in [0, 1, 2]: + try: + result = struct.unpack('hh', + fcntl.ioctl(fd_io, termios.TIOCGWINSZ, + '1234')) + except IOError: + result = None + continue + + if (result is None or result == (0, 0)): + # Maybe we can't get the width. In that case assume (25, 80) + result = (25, 80) + + return result + +## +# Content derived from: +# http://getpython3.com/diveintopython3/your-first-python-program.html#divingin +## +SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], + 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']} + + +def approximate_size(size, a_kilobyte_is_1024_bytes=True): + """Convert a file size to human-readable form. + + Keyword arguments: + size -- file size in bytes + a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024 + if False, use multiples of 1000 + + Returns: string + + """ + size = float(size) + if size < 0: + raise ValueError('number must be non-negative') + + if (a_kilobyte_is_1024_bytes): + multiple = 1024 + else: + multiple = 1000 + for suffix in SUFFIXES[multiple]: + size /= multiple + if size < multiple: + return "%.1f %s" % (size, suffix) + + raise ValueError('number too large') + + +## +# End copied content +## +class ColMeta: + """ Concepts about columns. """ + def __init__(self, sortable, width): + self.sortable = sortable + self.width = width + + +class RowMeta: + """ How to render rows. """ + def __init__(self, label, fmt): + self.label = label + self.fmt = fmt + + +def fmt_packet(obj, width): + """ Provide a string for packets that is appropriate for output.""" + return str(obj.packets).rjust(width) + + +def fmt_count(obj, width): + """ Provide a string for average that is appropriate for output.""" + return str(obj.count).rjust(width) + + +def fmt_avg(obj, width): + """ Provide a string for average that is appropriate for output.""" + return str(int(obj.average)).rjust(width) + + +def fmt_field(obj, width): + """ truncate really long flow and insert ellipses to help make it + clear. + """ + + ellipses = " ... " + value = obj.field + if (len(obj.field) > width): + value = value[:(width - len(ellipses))] + ellipses + return value.ljust(width) + + +def fmt_bytes(obj, width): + """ Provide a string for average that is appropriate for output.""" + if (len(str(obj.bytes)) <= width): + value = str(obj.bytes) + else: + value = approximate_size(obj.bytes) + return value.rjust(width) + + +def title_center(value, width): + """ Center a column title.""" + return value.upper().center(width) + + +def title_rjust(value, width): + """ Right justify a column title. """ + return value.upper().rjust(width) + + +def column_picker(order, obj): + """ return the column as specified by order. """ + if (order == 1): + return obj.count + elif (order == 2): + return obj.packets + elif (order == 3): + return obj.bytes + elif (order == 4): + return obj.average + else: + raise ValueError("order outside of range %s" % order) + + +class Render: + """ Renders flow data. """ + def __init__(self, console_width): + """ Calculate column widths taking into account changes in format.""" + + self._start_time = datetime.datetime.now() + + self._cols = [ColMeta(False, 0), + ColMeta(True, Columns.VALUE_WIDTH), + ColMeta(True, Columns.VALUE_WIDTH), + ColMeta(True, Columns.VALUE_WIDTH), + ColMeta(True, Columns.VALUE_WIDTH)] + self._console_width = console_width + self.console_width_set(console_width) + + # Order in this array dictate the order of the columns. + # The 0 width for the first entry is a place holder. This is + # dynamically calculated. The first column is special. We need a + # way to indicate which field are presented. + self._descs = [RowMeta("", title_rjust), + RowMeta("", title_rjust), + RowMeta("", title_rjust), + RowMeta("", title_rjust), + RowMeta("", title_rjust)] + self._column_sort_select = 0 + self.column_select_event() + + self._titles = [ + RowMeta(Columns.FIELDS, title_center), + RowMeta(Columns.COUNT, title_rjust), + RowMeta(Columns.PACKETS, title_rjust), + RowMeta(Columns.BYTES, title_rjust), + RowMeta(Columns.AVERAGE, title_rjust) + ] + + self._datas = [ + RowMeta(None, fmt_field), + RowMeta(None, fmt_count), + RowMeta(None, fmt_packet), + RowMeta(None, fmt_bytes), + RowMeta(None, fmt_avg) + ] + + ## + # _field_types hold which fields are displayed in the field + # column, with the keyword all implying all fields. + ## + self._field_types = ["all"] + [ii.field_type for ii in OUTPUT_FORMAT] + + ## + # The default is to show all field types. + ## + self._field_type_select = -1 + self.field_type_toggle() + + def _field_type_select_get(self): + """ Return which field type to display. """ + return self._field_types[self._field_type_select] + + def field_type_toggle(self): + """ toggle which field types to show. """ + self._field_type_select += 1 + if (self._field_type_select >= len(self._field_types)): + self._field_type_select = 0 + value = Columns.FIELDS + " (%s)" % self._field_type_select_get() + self._titles[0].label = value + + def column_select_event(self): + """ Handles column select toggle. """ + + self._descs[self._column_sort_select].label = "" + for _ in range(len(self._cols)): + self._column_sort_select += 1 + if (self._column_sort_select >= len(self._cols)): + self._column_sort_select = 0 + + # Now look for the next sortable column + if (self._cols[self._column_sort_select].sortable): + break + self._descs[self._column_sort_select].label = "DESC" + + def console_width_set(self, console_width): + """ Adjust the output given the new console_width. """ + self._console_width = console_width + + spaces = len(self._cols) - 1 + ## + # Calculating column width can be tedious but important. The + # flow field value can be long. The goal here is to dedicate + # fixed column space for packets, bytes, average and counts. Give the + # remaining space to the flow column. When numbers get large + # transition output to output generated by approximate_size which + # limits output to ###.# XiB in other words 9 characters. + ## + # At this point, we know the maximum length values. We may + # truncate the flow column to get everything to fit. + self._cols[0].width = 0 + values_max_length = sum([ii.width for ii in self._cols]) + spaces + flow_max_length = console_width - values_max_length + self._cols[0].width = flow_max_length + + def format(self, flow_db): + """ shows flows based on --script parameter.""" + + rc = [] + ## + # Top output consists of + # Title + # Column title (2 rows) + # data + # statistics and status + + ## + # Title + ## + rc.append("Flow Summary".center(self._console_width)) + + stats = " Total: %(flow_total)s errors: %(flow_errors)s " % \ + flow_db.flow_stats_get() + accumulate = flow_db.accumulate_get() + if (accumulate): + stats += "Accumulate: on " + else: + stats += "Accumulate: off " + + duration = datetime.datetime.now() - self._start_time + stats += "Duration: %s " % str(duration) + rc.append(stats.ljust(self._console_width)) + + ## + # 2 rows for columns. + ## + # Indicate which column is in descending order. + rc.append(" ".join([ii.fmt(ii.label, col.width) + for (ii, col) in zip(self._descs, self._cols)])) + + rc.append(" ".join([ii.fmt(ii.label, col.width) + for (ii, col) in zip(self._titles, self._cols)])) + + ## + # Data. + ## + for dd in flow_db.field_values_in_order(self._field_type_select_get(), + self._column_sort_select): + rc.append(" ".join([ii.fmt(dd, col.width) + for (ii, col) in zip(self._datas, + self._cols)])) + + return rc + + +def curses_screen_begin(): + """ begin curses screen control. """ + stdscr = curses.initscr() + curses.cbreak() + curses.noecho() + stdscr.keypad(1) + return stdscr + + +def curses_screen_end(stdscr): + """ end curses screen control. """ + curses.nocbreak() + stdscr.keypad(0) + curses.echo() + curses.endwin() + + +class FlowDB: + """ Implements live vs accumulate mode. + + Flows are stored as key value pairs. The key consists of the content + prior to stat fields. The value portion consists of stats in a dictionary + form. + + @ \todo future add filtering here. + """ + def __init__(self, accumulate): + self._accumulate = accumulate + self._error_count = 0 + # Values are (stats, last update time.) + # The last update time is used for aging. + self._flow_lock = threading.Lock() + # This dictionary holds individual flows. + self._flows = {} + # This dictionary holds aggregate of flow fields. + self._fields = {} + + def accumulate_get(self): + """ Return the current accumulate state. """ + return self._accumulate + + def accumulate_toggle(self): + """ toggle accumulate flow behavior. """ + self._accumulate = not self._accumulate + + def begin(self): + """ Indicate the beginning of processing flow content. + if accumulate is false clear current set of flows. """ + + if (not self._accumulate): + self._flow_lock.acquire() + try: + self._flows.clear() + finally: + self._flow_lock.release() + self._fields.clear() + + def flow_line_add(self, line): + """ Split a line from a ovs-dpctl dump-flow into key and stats. + The order of the content in the flow should be: + - flow content + - stats for the flow + - actions + + This method also assumes that the dump flow output does not + change order of fields of the same flow. + """ + + line = line.rstrip("\n") + (fields, stats, _) = flow_line_split(line) + + try: + fields_dict = elements_to_dict(fields) + + if (len(fields_dict) == 0): + raise ValueError("flow fields are missing %s", line) + + stats_dict = elements_to_dict(stats) + if (len(stats_dict) == 0): + raise ValueError("statistics are missing %s.", line) + + ## + # In accumulate mode, the Flow database can reach 10,000's of + # persistent flows. The interaction of the script with this many + # flows is too slow. Instead, delta are sent to the flow_db + # database allow incremental changes to be done in O(m) time + # where m is the current flow list, instead of iterating over + # all flows in O(n) time where n is the entire history of flows. + key = ",".join(fields) + + self._flow_lock.acquire() + try: + (stats_old_dict, _) = self._flows.get(key, (None, None)) + finally: + self._flow_lock.release() + + self.flow_event(fields_dict, stats_old_dict, stats_dict) + + except ValueError, arg: + logging.error(arg) + self._error_count += 1 + raise + + self._flow_lock.acquire() + try: + self._flows[key] = (stats_dict, datetime.datetime.now()) + finally: + self._flow_lock.release() + + def decay(self, decayTimeInSeconds): + """ Decay content. """ + now = datetime.datetime.now() + for (key, value) in self._flows.items(): + (stats_dict, updateTime) = value + delta = now - updateTime + + if (delta.seconds > decayTimeInSeconds): + self._flow_lock.acquire() + try: + del self._flows[key] + + fields_dict = elements_to_dict(flow_line_iter(key)) + matches = flow_aggregate(fields_dict, stats_dict) + for match in matches: + self.field_dec(match) + + finally: + self._flow_lock.release() + + def flow_stats_get(self): + """ Return statistics in a form of a dictionary. """ + rc = None + self._flow_lock.acquire() + try: + rc = {"flow_total": len(self._flows), + "flow_errors": self._error_count} + finally: + self._flow_lock.release() + return rc + + def field_types_get(self): + """ Return the set of types stored in the singleton. """ + types = set((ii.field_type for ii in self._fields.values())) + return types + + def field_add(self, data): + """ Collect dump-flow data to sum number of times item appears. """ + current = self._fields.get(repr(data), None) + if (current is None): + current = copy.copy(data) + else: + current += data + self._fields[repr(current)] = current + + def field_dec(self, data): + """ Collect dump-flow data to sum number of times item appears. """ + current = self._fields.get(repr(data), None) + if (current is None): + raise ValueError("decrementing field missing %s" % repr(data)) + + current -= data + self._fields[repr(current)] = current + if (current.count == 0): + del self._fields[repr(current)] + + def field_values_in_order(self, field_type_select, column_order): + """ Return a list of items in order maximum first. """ + values = self._fields.values() + if (field_type_select != "all"): + # If a field type other than "all" then reduce the list. + values = [ii for ii in values + if (ii.field_type == field_type_select)] + values = [(column_picker(column_order, ii), ii) for ii in values] + values.sort(key=operator.itemgetter(0)) + values.reverse() + values = [ii[1] for ii in values] + return values + + def flow_event(self, fields_dict, stats_old_dict, stats_new_dict): + """ Receives new flow information. """ + + # In order to avoid processing every flow at every sample + # period, changes in flow packet count is used to determine the + # delta in the flow statistics. This delta is used in the call + # to self.decrement prior to self.field_add + + if (stats_old_dict is None): + # This is a new flow + matches = flow_aggregate(fields_dict, stats_new_dict) + for match in matches: + self.field_add(match) + else: + old_packets = int(stats_old_dict.get("packets", 0)) + new_packets = int(stats_new_dict.get("packets", 0)) + if (old_packets == new_packets): + # ignore. same data. + pass + else: + old_bytes = stats_old_dict.get("bytes", 0) + # old_packets != new_packets + # if old_packets > new_packets then we end up decrementing + # packets and bytes. + matches = flow_aggregate(fields_dict, stats_new_dict) + for match in matches: + match.decrement(int(old_packets), int(old_bytes), 1) + self.field_add(match) + + +class DecayThread(threading.Thread): + """ Periodically call flow database to see if any flows are old. """ + def __init__(self, flow_db, interval): + """ Start decay thread. """ + threading.Thread.__init__(self) + + self._interval = max(1, interval) + self._min_interval = min(1, interval / 10) + self._flow_db = flow_db + self._event = threading.Event() + self._running = True + + self.daemon = True + + def run(self): + """ Worker thread which handles decaying accumulated flows. """ + + while(self._running): + self._event.wait(self._min_interval) + if (self._running): + self._flow_db.decay(self._interval) + + def stop(self): + """ Stop thread. """ + self._running = False + self._event.set() + ## + # Give the calling thread time to terminate but not too long. + # this thread is a daemon so the application will terminate if + # we timeout during the join. This is just a cleaner way to + # release resources. + self.join(2.0) + + +def flow_top_command(stdscr, render, flow_db): + """ Handle input while in top mode. """ + ch = stdscr.getch() + ## + # Any character will restart sampling. + if (ch == ord('h')): + # halt output. + ch = stdscr.getch() + while (ch == -1): + ch = stdscr.getch() + + if (ch == ord('s')): + # toggle which column sorts data in descending order. + render.column_select_event() + elif (ch == ord('a')): + flow_db.accumulate_toggle() + elif (ch == ord('f')): + render.field_type_toggle() + elif (ch == ord(' ')): + # resample + pass + + return ch + + +def decay_timer_start(flow_db, accumulateDecay): + """ If accumulateDecay greater than zero then start timer. """ + if (accumulateDecay > 0): + decay_timer = DecayThread(flow_db, accumulateDecay) + decay_timer.start() + return decay_timer + else: + return None + + +def flows_top(args): + """ handles top like behavior when --script is not specified. """ + + flow_db = FlowDB(args.accumulate) + render = Render(0) + + decay_timer = decay_timer_start(flow_db, args.accumulateDecay) + lines = [] + + try: + stdscr = curses_screen_begin() + try: + ch = 'X' + #stdscr.nodelay(1) + stdscr.timeout(args.delay) + + while (ch != ord('q')): + flow_db.begin() + + try: + ihdl = top_input_get(args) + try: + flows_read(ihdl, flow_db) + finally: + ihdl.close() + except OSError, arg: + logging.critical(arg) + break + + (console_height, console_width) = stdscr.getmaxyx() + render.console_width_set(console_width) + + output_height = console_height - 1 + line_count = range(output_height) + line_output = render.format(flow_db) + lines = zip(line_count, line_output[:output_height]) + + stdscr.erase() + for (count, line) in lines: + stdscr.addstr(count, 0, line[:console_width]) + stdscr.refresh() + + ch = flow_top_command(stdscr, render, flow_db) + + finally: + curses_screen_end(stdscr) + except KeyboardInterrupt: + pass + if (decay_timer): + decay_timer.stop() + + # repeat output + for (count, line) in lines: + print line + + +def flows_script(args): + """ handles --script option. """ + + flow_db = FlowDB(args.accumulate) + flow_db.begin() + + if (args.flowFiles is None): + logging.info("reading flows from stdin") + ihdl = os.fdopen(sys.stdin.fileno(), 'r', 0) + try: + flow_db = flows_read(ihdl, flow_db) + finally: + ihdl.close() + else: + for flowFile in args.flowFiles: + logging.info("reading flows from %s", flowFile) + ihdl = open(flowFile, "r") + try: + flow_db = flows_read(ihdl, flow_db) + finally: + ihdl.close() + + (_, console_width) = get_terminal_size() + render = Render(console_width) + + for line in render.format(flow_db): + print line + + +def main(): + """ Return 0 on success or 1 on failure. + + Algorithm + There are four stages to the process ovs-dpctl dump-flow content. + 1. Retrieve current input + 2. store in FlowDB and maintain history + 3. Iterate over FlowDB and aggregating stats for each flow field + 4. present data. + + Retrieving current input is currently trivial, the ovs-dpctl dump-flow + is called. Future version will have more elaborate means for collecting + dump-flow content. FlowDB returns all data as in the form of a hierarchical + dictionary. Input will vary. + + In the case of accumulate mode, flows are not purged from the FlowDB + manager. Instead at the very least, merely the latest statistics are + kept. In the case, of live output the FlowDB is purged prior to sampling + data. + + Aggregating results requires identify flow fields to aggregate out + of the flow and summing stats. + + """ + args = args_get() + + try: + if (args.top): + flows_top(args) + else: + flows_script(args) + except KeyboardInterrupt: + return 1 + return 0 + +if __name__ == '__main__': + sys.exit(main()) +elif __name__ == 'ovs-dpctl-top': + # pylint: disable-msg=R0915 + + ## + # Test case beyond this point. + # pylint: disable-msg=R0904 + class TestsuiteFlowParse(unittest.TestCase): + """ + parse flow into hierarchy of dictionaries. + """ + def test_flow_parse(self): + """ test_flow_parse. """ + line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ + "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ + "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ + "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ + "udp(src=61252,dst=5355), packets:1, bytes:92, "\ + "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ + "38,41,44,47,50,53,56,59,62,65" + + (fields, stats, _) = flow_line_split(line) + flow_dict = elements_to_dict(fields + stats) + self.assertEqual(flow_dict["eth"]["src"], "00:50:56:b4:4e:f8") + self.assertEqual(flow_dict["eth"]["dst"], "33:33:00:01:00:03") + self.assertEqual(flow_dict["ipv6"]["src"], + "fe80::55bf:fe42:bc96:2812") + self.assertEqual(flow_dict["ipv6"]["dst"], "ff02::1:3") + self.assertEqual(flow_dict["packets"], "1") + self.assertEqual(flow_dict["bytes"], "92") + + line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ + "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ + "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ + "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ + "udp(src=61252,dst=5355), packets:1, bytes:92, "\ + "used:-0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ + "38,41,44,47,50,53,56,59,62,65" + + (fields, stats, _) = flow_line_split(line) + flow_dict = elements_to_dict(fields + stats) + self.assertEqual(flow_dict["used"], "-0.703s") + self.assertEqual(flow_dict["packets"], "1") + self.assertEqual(flow_dict["bytes"], "92") + + def test_flow_sum(self): + """ test_flow_sum. """ + line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ + "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ + "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ + "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ + "udp(src=61252,dst=5355), packets:2, bytes:92, "\ + "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ + "38,41,44,47,50,53,56,59,62,65" + + (fields, stats, _) = flow_line_split(line) + stats_dict = elements_to_dict(stats) + fields_dict = elements_to_dict(fields) + ## + # Test simple case of one line. + flow_db = FlowDB(False) + matches = flow_aggregate(fields_dict, stats_dict) + for match in matches: + flow_db.field_add(match) + + flow_types = flow_db.field_types_get() + expected_flow_types = ["eth", "eth_type", "udp", "in_port", "ipv6"] + self.assert_(len(flow_types) == len(expected_flow_types)) + for flow_type in flow_types: + self.assertTrue(flow_type in expected_flow_types) + + for flow_type in flow_types: + sum_value = flow_db.field_values_in_order("all", 1) + self.assert_(len(sum_value) == 5) + self.assert_(sum_value[0].packets == 2) + self.assert_(sum_value[0].count == 1) + self.assert_(sum_value[0].bytes == 92) + + ## + # Add line again just to see counts go up. + matches = flow_aggregate(fields_dict, stats_dict) + for match in matches: + flow_db.field_add(match) + + flow_types = flow_db.field_types_get() + self.assert_(len(flow_types) == len(expected_flow_types)) + for flow_type in flow_types: + self.assertTrue(flow_type in expected_flow_types) + + for flow_type in flow_types: + sum_value = flow_db.field_values_in_order("all", 1) + self.assert_(len(sum_value) == 5) + self.assert_(sum_value[0].packets == 4) + self.assert_(sum_value[0].count == 2) + self.assert_(sum_value[0].bytes == 2 * 92) + + def test_assoc_list(self): + """ test_assoc_list. """ + line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ + "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ + "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ + "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ + "udp(src=61252,dst=5355), packets:2, bytes:92, "\ + "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ + "38,41,44,47,50,53,56,59,62,65" + + valid_flows = [ + 'eth_type(0x86dd)', + 'udp(dst=5355)', + 'in_port(4)', + 'ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3)', + 'eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)' + ] + + (fields, stats, _) = flow_line_split(line) + stats_dict = elements_to_dict(stats) + fields_dict = elements_to_dict(fields) + + ## + # Test simple case of one line. + flow_db = FlowDB(False) + matches = flow_aggregate(fields_dict, stats_dict) + for match in matches: + flow_db.field_add(match) + + for sum_value in flow_db.field_values_in_order("all", 1): + assoc_list = Columns.assoc_list(sum_value) + for item in assoc_list: + if (item[0] == "fields"): + self.assertTrue(item[1] in valid_flows) + elif (item[0] == "packets"): + self.assertTrue(item[1] == 2) + elif (item[0] == "count"): + self.assertTrue(item[1] == 1) + elif (item[0] == "average"): + self.assertTrue(item[1] == 46.0) + elif (item[0] == "bytes"): + self.assertTrue(item[1] == 92) + else: + raise ValueError("unknown %s", item[0]) + + def test_human_format(self): + """ test_assoc_list. """ + + self.assertEqual(approximate_size(0.0), "0.0 KiB") + self.assertEqual(approximate_size(1024), "1.0 KiB") + self.assertEqual(approximate_size(1024 * 1024), "1.0 MiB") + self.assertEqual(approximate_size((1024 * 1024) + 100000), + "1.1 MiB") + value = (1024 * 1024 * 1024) + 100000000 + self.assertEqual(approximate_size(value), "1.1 GiB") + + def test_flow_line_split(self): + """ Splitting a flow line is not trivial. + There is no clear delimiter. Comma is used liberally.""" + expected_fields = ["in_port(4)", + "eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)", + "eth_type(0x86dd)", + "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3," + "label=0,proto=17,tclass=0,hlimit=1,frag=no)", + "udp(src=61252,dst=5355)"] + expected_stats = ["packets:2", "bytes:92", "used:0.703s"] + expected_actions = "actions:3,8,11,14,17,20,23,26,29,32,35," \ + "38,41,44,47,50,53,56,59,62,65" + + line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ + "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ + "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ + "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ + "udp(src=61252,dst=5355), packets:2, bytes:92, "\ + "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ + "38,41,44,47,50,53,56,59,62,65" + + (fields, stats, actions) = flow_line_split(line) + + self.assertEqual(fields, expected_fields) + self.assertEqual(stats, expected_stats) + self.assertEqual(actions, expected_actions) + + def test_accumulate_decay(self): + """ test_accumulate_decay: test accumulated decay. """ + lines = ["in_port(1),eth(src=00:50:56:4f:dc:3b," + "dst=ff:ff:ff:ff:ff:ff)," + "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," + "tip=10.24.104.230/255.255.255.255,op=1/0xff," + "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," + "tha=00:00:00:00:00:00/00:00:00:00:00:00), " + "packets:1, bytes:120, used:0.004s, actions:1"] + + flow_db = FlowDB(True) + flow_db.begin() + flow_db.flow_line_add(lines[0]) + + # Make sure we decay + time.sleep(4) + self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) + flow_db.decay(1) + self.assertEqual(flow_db.flow_stats_get()["flow_total"], 0) + + flow_db.flow_line_add(lines[0]) + self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) + flow_db.decay(30) + # Should not be deleted. + self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) + + flow_db.flow_line_add(lines[0]) + self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) + timer = decay_timer_start(flow_db, 2) + time.sleep(10) + self.assertEqual(flow_db.flow_stats_get()["flow_total"], 0) + timer.stop() + + def test_accumulate(self): + """ test_accumulate test that FlowDB supports accumulate. """ + + lines = ["in_port(1),eth(src=00:50:56:4f:dc:3b," + "dst=ff:ff:ff:ff:ff:ff)," + "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," + "tip=10.24.104.230/255.255.255.255,op=1/0xff," + "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," + "tha=00:00:00:00:00:00/00:00:00:00:00:00), " + "packets:1, bytes:120, used:0.004s, actions:1", + "in_port(2)," + "eth(src=68:ef:bd:25:ef:c0,dst=33:33:00:00:00:66)," + "eth_type(0x86dd),ipv6(src=fe80::6aef:bdff:fe25:efc0/::," + "dst=ff02::66/::,label=0/0,proto=17/0xff,tclass=0xe0/0," + "hlimit=255/0,frag=no/0),udp(src=2029,dst=2029), " + "packets:2, bytes:5026, used:0.348s, actions:1", + "in_port(1),eth(src=ee:ee:ee:ee:ee:ee," + "dst=ff:ff:ff:ff:ff:ff)," + "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," + "tip=10.24.104.230/255.255.255.255,op=1/0xff," + "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," + "tha=00:00:00:00:00:00/00:00:00:00:00:00), packets:2, " + "bytes:240, used:0.004s, actions:1"] + + lines = [ + "in_port(1),eth_type(0x0806), packets:1, bytes:120, actions:1", + "in_port(2),eth_type(0x0806), packets:2, bytes:126, actions:1", + "in_port(1),eth_type(0x0806), packets:2, bytes:240, actions:1", + "in_port(1),eth_type(0x0800), packets:1, bytes:120, actions:1", + "in_port(1),eth_type(0x0800), packets:2, bytes:240, actions:1", + "in_port(1),eth_type(0x0806), packets:1, bytes:120, actions:1", + ] + + # Turn on accumulate. + flow_db = FlowDB(True) + flow_db.begin() + + flow_db.flow_line_add(lines[0]) + + # Test one flow exist. + sum_values = flow_db.field_values_in_order("all", 1) + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 1) + self.assertEqual(in_ports[0].bytes, 120) + self.assertEqual(in_ports[0].count, 1) + + # simulate another sample + # Test two different flows exist. + flow_db.begin() + flow_db.flow_line_add(lines[1]) + sum_values = flow_db.field_values_in_order("all", 1) + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 1) + self.assertEqual(in_ports[0].bytes, 120) + self.assertEqual(in_ports[0].count, 1) + + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 2) + self.assertEqual(in_ports[0].bytes, 126) + self.assertEqual(in_ports[0].count, 1) + + # Test first flow increments packets. + flow_db.begin() + flow_db.flow_line_add(lines[2]) + sum_values = flow_db.field_values_in_order("all", 1) + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 2) + self.assertEqual(in_ports[0].bytes, 240) + self.assertEqual(in_ports[0].count, 1) + + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 2) + self.assertEqual(in_ports[0].bytes, 126) + self.assertEqual(in_ports[0].count, 1) + + # Test third flow but with the same in_port(1) as the first flow. + flow_db.begin() + flow_db.flow_line_add(lines[3]) + sum_values = flow_db.field_values_in_order("all", 1) + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 3) + self.assertEqual(in_ports[0].bytes, 360) + self.assertEqual(in_ports[0].count, 2) + + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 2) + self.assertEqual(in_ports[0].bytes, 126) + self.assertEqual(in_ports[0].count, 1) + + # Third flow has changes. + flow_db.begin() + flow_db.flow_line_add(lines[4]) + sum_values = flow_db.field_values_in_order("all", 1) + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 4) + self.assertEqual(in_ports[0].bytes, 480) + self.assertEqual(in_ports[0].count, 2) + + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 2) + self.assertEqual(in_ports[0].bytes, 126) + self.assertEqual(in_ports[0].count, 1) + + # First flow reset. + flow_db.begin() + flow_db.flow_line_add(lines[5]) + sum_values = flow_db.field_values_in_order("all", 1) + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 3) + self.assertEqual(in_ports[0].bytes, 360) + self.assertEqual(in_ports[0].count, 2) + + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 2) + self.assertEqual(in_ports[0].bytes, 126) + self.assertEqual(in_ports[0].count, 1) + + def test_parse_character_errors(self): + """ test_parsing errors. + The flow parses is purposely loose. Its not designed to validate + input. Merely pull out what it can but there are situations + that a parse error can be detected. + """ + + lines = ["complete garbage", + "in_port(2),eth(src=68:ef:bd:25:ef:c0," + "dst=33:33:00:00:00:66)," + "eth_type(0x86dd),ipv6(src=fe80::6aef:bdff:fe25:efc0/::," + "dst=ff02::66/::,label=0/0,proto=17/0xff,tclass=0xe0/0," + "hlimit=255/0,frag=no/0),udp(src=2029,dst=2029)," + "packets:2,bytes:5026,actions:1"] + + flow_db = FlowDB(False) + flow_db.begin() + for line in lines: + try: + flow_db.flow_line_add(line) + except ValueError: + # We want an exception. That is how we know we have + # correctly found a simple parsing error. We are not + # looking to validate flow output just catch simple issues. + continue + self.assertTrue(False) + + def test_tunnel_parsing(self): + """ test_tunnel_parsing test parse flows with tunnel. """ + lines = [ + "tunnel(tun_id=0x0,src=192.168.1.1,dst=192.168.1.10," + "tos=0x0,ttl=64,flags(key)),in_port(1)," + "eth(src=9e:40:f5:ef:ec:ee,dst=01:23:20:00:00:30)," + "eth_type(0x8902), packets:6, bytes:534, used:0.128s, " + "actions:userspace(pid=4294962691,slow_path(cfm))" + ] + flow_db = FlowDB(False) + flow_db.begin() + flow_db.flow_line_add(lines[0]) + sum_values = flow_db.field_values_in_order("all", 1) + in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] + self.assertEqual(len(in_ports), 1) + self.assertEqual(in_ports[0].packets, 6) + self.assertEqual(in_ports[0].bytes, 534) + self.assertEqual(in_ports[0].count, 1) + + def test_flow_multiple_paren(self): + """ test_flow_multiple_paren. """ + line = "tunnel(tun_id=0x0,src=192.168.1.1,flags(key)),in_port(2)" + valid = ["tunnel(tun_id=0x0,src=192.168.1.1,flags(key))", + "in_port(2)"] + rc = flow_line_iter(line) + self.assertEqual(valid, rc) + + def test_to_network(self): + """ test_to_network test ipv4_to_network and ipv6_to_network. """ + ipv4s = [ + ("192.168.0.1", "192.168.0.1"), + ("192.168.0.1/255.255.255.255", "192.168.0.1"), + ("192.168.0.1/255.255.255.0", "192.168.0.0"), + ("192.168.0.1/255.255.0.0", "192.168.0.0"), + ("192.168.0.1/255.0.0.0", "192.0.0.0"), + ("192.168.0.1/0.0.0.0", "0.0.0.0"), + ("10.24.106.230/255.255.255.255", "10.24.106.230"), + ("10.24.106.230/255.255.255.0", "10.24.106.0"), + ("10.24.106.0/255.255.255.0", "10.24.106.0"), + ("10.24.106.0/255.255.252.0", "10.24.104.0") + ] + + ipv6s = [ + ("1::192:168:0:1", "1::192:168:0:1"), + ("1::192:168:0:1/1::ffff:ffff:ffff:ffff", "1::192:168:0:1"), + ("1::192:168:0:1/1::ffff:ffff:ffff:0", "1::192:168:0:0"), + ("1::192:168:0:1/1::ffff:ffff:0:0", "1::192:168:0:0"), + ("1::192:168:0:1/1::ffff:0:0:0", "1::192:0:0:0"), + ("1::192:168:0:1/1::0:0:0:0", "1::"), + ("1::192:168:0:1/::", "::") + ] + + for (ipv4_test, ipv4_check) in ipv4s: + self.assertEqual(ipv4_to_network(ipv4_test), ipv4_check) + + for (ipv6_test, ipv6_check) in ipv6s: + self.assertEqual(ipv6_to_network(ipv6_test), ipv6_check) diff --git a/chef/cookbooks/openstack-network/files/default/quantum-ha-tool.py b/chef/cookbooks/openstack-network/files/default/quantum-ha-tool.py deleted file mode 100755 index 55822d3..0000000 --- a/chef/cookbooks/openstack-network/files/default/quantum-ha-tool.py +++ /dev/null @@ -1,424 +0,0 @@ -#! /usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 AT&T Services, Inc. -# All Rights Reserved. -# -# 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. - - -import logging -import os -import sys -import json -import argparse -from logging.handlers import SysLogHandler -from collections import OrderedDict -from random import choice -from quantumclient.quantum import client - -LOG = logging.getLogger('quantum-ha-tool') -LOG_FORMAT='%(asctime)s %(name)-12s %(levelname)-8s %(message)s' -LOG_DATE = '%m-%d %H:%M' -DESCRIPTION = "Quantum High Availability Tool" - -def parse_args(): - - # ensure environment has necessary items to authenticate - for key in ['OS_TENANT_NAME', 'OS_USERNAME', 'OS_PASSWORD', - 'OS_AUTH_URL']: - if key not in os.environ.keys(): - LOG.exception("Your environment is missing '%s'") - - ap = argparse.ArgumentParser(description=DESCRIPTION) - ap.add_argument('-d', '--debug', action='store_true', - default=False, help='Show debugging output') - ap.add_argument('-n', '--noop', action='store_true', - default=False, help='Do not do any modifying operations (dry-run)') - ap.add_argument('--l3-agent-check', action='store_true', - default=False, help='Show routers associated with offline l3 agents') - ap.add_argument('--l3-agent-migrate', action='store_true', - default=False, help='Migrate routers away from offline l3 agents') - ap.add_argument('--l3-agent-rebalance', action='store_true', - default=False, help='Rebalance router count on all l3 agents') - ap.add_argument('--replicate-dhcp', action='store_true', - default=False, help='Replicate DHCP configuration to all agents') - return ap.parse_args() - -def setup_logging(args): - level = logging.INFO - if args.debug: - level = logging.DEBUG - logging.basicConfig(level=level, format=LOG_FORMAT, date_fmt=LOG_DATE) - handler = SysLogHandler(address = '/dev/log') - syslog_formatter = logging.Formatter('%(name)s: %(levelname)s %(message)s') - handler.setFormatter(syslog_formatter) - LOG.addHandler(handler) - -def run(args): - - # instantiate client - qclient = client.Client('2.0', auth_url=os.environ['OS_AUTH_URL'], - username=os.environ['OS_USERNAME'], - tenant_name=os.environ['OS_TENANT_NAME'], - password=os.environ['OS_PASSWORD']) - - # set json return type - qclient.format = 'json' - - if args.l3_agent_check: - LOG.info("Performing L3 Agent Health Check") - l3_agent_check(qclient, args.noop) - - if args.l3_agent_migrate: - LOG.info("Performing L3 Agent Migration for Offline L3 Agents") - l3_agent_migrate(qclient, args.noop) - - if args.l3_agent_rebalance: - LOG.info("Rebalancing L3 Agent Router Count") - l3_agent_rebalance(qclient, args.noop) - - if args.replicate_dhcp: - LOG.info("Performing DHCP Replication of Networks to Agents") - replicate_dhcp(qclient, args.noop) - -def l3_agent_rebalance(qclient, noop=False): - """ - Rebalance l3 agent router count across agents. The number of routers - on each l3 agent will be as close as possible which should help - distribute load as new l3 agents come online. - - :param qclient: A quantumclient - :param noop: Optional noop flag - """ - - # {u'binary': u'quantum-l3-agent', u'description': None, u'admin_state_up': True, u'heartbeat_timestamp': u'2013-07-02 22:20:23', u'alive': True, u'topic': - # u'l3_agent', u'host': u'o3r3.int.san3.attcompute.com', u'agent_type': u'L3 agent', u'created_at': u'2013-07-02 14:50:58', u'started_at': u'2013-07-02 18:00:55', - # u'id': u'6efe494a-616c-41ea-9c8f-2c592f4d46ff', u'configurations': {u'router_id': u'', u'gateway_external_network_id': u'', u'handle_internal_only_routers': True, - # u'use_namespaces': True, u'routers': 5, u'interfaces': 3, u'floating_ips': 9, u'interface_driver': u'quantum.agent.linux.interface.OVSInterfaceDriver', u'ex_gw_ports': 3}}, - - l3_agent_dict={} - agents = list_agents(qclient, agent_type='L3 agent') - num_agents = len(agents) - if num_agents <= 1: - LOG.info("No rebalancing required for 1 or fewer agents") - return - - for l3_agent in agents: - num_routers=l3_agent['configurations']['routers'] - l3_agent_dict[l3_agent['id']] = list_routers_on_l3_agent(qclient, l3_agent['id']) - - ordered_l3_agent_dict = OrderedDict(sorted(l3_agent_dict.items(), key=lambda t: len(t[0]))) - ordered_l3_agent_list = list(ordered_l3_agent_dict) - num_agents = len(ordered_l3_agent_list) - LOG.info("Agent list: %s", ordered_l3_agent_list[0:(num_agents-1/2)+1]) - i=0 - for agent in ordered_l3_agent_list[0:num_agents-1/2]: - low_agent_id=ordered_l3_agent_list[i] - hgh_agent_id=ordered_l3_agent_list[-(i+1)] - - # do nothing if we end up comparing the same router - if low_agent_id == hgh_agent_id: - continue - - LOG.info("Examining low_agent=%s, high_agent=%s", low_agent_id, hgh_agent_id) - - low_agent_router_count = len(l3_agent_dict[low_agent_id]) - hgh_agent_router_count = len(l3_agent_dict[hgh_agent_id]) - - LOG.info("Low Count=%s, High Count=%s", low_agent_router_count, hgh_agent_router_count) - - for router_id in l3_agent_dict[hgh_agent_id]: - if low_agent_router_count >= hgh_agent_router_count: - break - else: - LOG.info("Migrating router=%s from agent=%s to agent=%s", router_id, hgh_agent_id, low_agent_id) - try: - if not noop: - migrate_router(qclient, router_id, hgh_agent_id, low_agent_id) - low_agent_router_count += 1 - hgh_agent_router_count -= 1 - except: - LOG.traceback("Failed to migrate router=%s from agent=%s to agent=%s" % (router_id, hgh_agent_id, low_agent_id)) - continue - i+=1 - -def l3_agent_check(qclient, noop=False): - """ - Walk the l3 agents searching for agents that are offline. Show routers - that are offline and where we would migrate them too. - - :param qclient: A quantumclient - :param noop: Optional noop flag - - """ - - migration_count = 0 - agent_list = list_agents(qclient) - agent_dead_list = agent_dead_id_list(agent_list, 'L3 agent') - agent_alive_list = agent_alive_id_list(agent_list, 'L3 agent') - LOG.info("There are %s offline L3 agents and %s online L3 agents", len(agent_dead_list), len(agent_alive_list)) - - if len(agent_dead_list) > 0: - - for agent_id in agent_dead_list: - - LOG.info("Querying agent_id=%s for routers to migrate", agent_id) - router_id_list = list_routers_on_l3_agent(qclient, agent_id) - - for router_id in router_id_list: - - try: - target_id = choice(agent_alive_list) - except: - LOG.warn("There are no l3 agents alive we could migrate routers onto") - target_id = None - - LOG.info("Would like to migrate router=%s to agent=%s", router_id, target_id) - -def l3_agent_migrate(qclient, noop=False): - """ - Walk the l3 agents searching for agents that are offline. For those that are - offline, we will retrieve a list of routers on them and migrate them to a - random l3 agent that is online. - - :param qclient: A quantumclient - :param noop: Optional noop flag - - """ - - migration_count = 0 - agent_list = list_agents(qclient) - agent_dead_list = agent_dead_id_list(agent_list, 'L3 agent') - agent_alive_list = agent_alive_id_list(agent_list, 'L3 agent') - LOG.info("There are %s offline L3 agents and %s online L3 agents", len(agent_dead_list), len(agent_alive_list)) - - if len(agent_dead_list) > 0: - - if len(agent_alive_list) < 1: - LOG.exception("There are no l3 agents alive to migrate routers onto") - - for agent_id in agent_dead_list: - - LOG.info("Querying agent_id=%s for routers to migrate", agent_id) - router_id_list = list_routers_on_l3_agent(qclient, agent_id) - - for router_id in router_id_list: - - target_id = choice(agent_alive_list) - LOG.info("Migrating router=%s to agent=%s", router_id, target_id) - router_body = {'router_id': router_id} - - try: - - if not noop: - migrate_router(qclient, router_id, agent_id, target_id) - migration_count+=1 - - except: - - LOG.exception("There was an error migrating a router") - continue - - LOG.info("%s routers required migration from offline L3 agents", migration_count) - -def replicate_dhcp(qclient, noop=False): - """ - Retrieve a network list and then probe each DHCP agent to ensure they have that - network assigned. - - :param qclient: A quantumclient - :param noop: Optional noop flag - """ - - added=0 - networks = list_networks(qclient) - network_id_list = [n['id'] for n in networks] - agents = list_agents(qclient, agent_type='DHCP agent') - LOG.info("Replicating %s networks to %s DHCP agents", len(networks), len(agents)) - for dhcp_agent_id in [a['id'] for a in agents]: - networks_on_agent = qclient.list_networks_on_dhcp_agent(dhcp_agent_id)['networks'] - network_id_on_agent = [n['id'] for n in networks_on_agent] - for network_id in network_id_list: - if network_id not in network_id_on_agent: - try: - dhcp_body = {'network_id': network_id} - if not noop: - qclient.add_network_to_dhcp_agent(dhcp_agent_id, dhcp_body) - LOG.info("Added missing network=%s to dhcp agent=%s", network_id, dhcp_agent_id) - added+=1 - except: - LOG.exception("Failed to add network_id=%s to dhcp_agent=%s", network_id, dhcp_agent_id) - continue - - LOG.info("Added %s networks to DHCP agents", added) - - -def migrate_router(qclient, router_id, agent_id, target_id): - """ - Returns nothing, and raises on exception - - :param qclient: A quantumclient - :param router_id: The id of the router to migrate - :param agent_id: The id of the l3 agent to migrate from - :param target_id: The id of the l3 agent to migrate to - """ - - # N.B. The quantum API will return "success" even when there is a subsequent - # failure during the add or remove process so we must check to ensure the - # router has been added or removed - - # remove the router from the dead agent - qclient.remove_router_from_l3_agent(agent_id, router_id) - - # ensure it is removed or log an error - if router_id in list_routers_on_l3_agent(qclient, agent_id): - LOG.exception("Failed to remove router_id=%s from agent_id=%s", router_id, agent_id) - - - # add the router id to a live agent - router_body = {'router_id': router_id} - qclient.add_router_to_l3_agent(target_id, router_body) - - # ensure it is removed or log an error - if router_id not in list_routers_on_l3_agent(qclient, target_id): - LOG.exception("Failed to add router_id=%s from agent_id=%s", router_id, agent_id) - - -def list_networks(qclient): - """ - Return a list of network objects - - :param qclient: A quantumclient - """ - - resp = qclient.list_networks() - LOG.debug("list_networks: %s", resp) - return resp['networks'] - -def list_dhcp_agent_networks(qclient, agent_id): - """ - Return a list of network ids assigned to a particular DHCP agent - - :param qclient: A quantumclient - :param agent_id: A DHCP agent id - """ - - resp = qclient.list_networks_on_dhcp_agent(agent_id) - LOG.debug("list_networks_on_dhcp_agent: %s", resp) - return [s['id'] for s in resp['networks']] - - - -def list_routers(qclient): - """ - Return a list of router objects - - :param qclient: A quantumclient - - # {'routers': [{u'status': u'ACTIVE', u'external_gateway_info': {u'network_id': u'b970297c-d80e-4527-86d7-e49d2da9fdef'}, u'name': u'router1', - # u'admin_state_up': True, u'tenant_id': u'5603b97ee7f047ea999e25492c7fcb23', u'routes': [], u'id': u'0a122e5c-1623-412e-8c53-a1e21d1daff8'}, - - """ - - resp = qclient.list_routers() - LOG.debug("list_routers: %s", resp) - return resp['routers'] - -def list_routers_on_l3_agent(qclient, agent_id): - """ - Return a list of router ids on an agent - - :param qclient: A quantumclient - """ - - resp = qclient.list_routers_on_l3_agent(agent_id) - LOG.debug("list_routers_on_l3_agent: %s", resp) - return [r['id'] for r in resp['routers']] - -def list_agents(qclient, agent_type=None): - """ - Return a list of agent objects - - :param qclient: A quantumclient - - # openvswitch - # - # {u'agents': [{u'binary': u'quantum-openvswitch-agent', u'description': None, u'admin_state_up': True, u'heartbeat_timestamp': u'2013-07-02 22:20:25' - # u'alive': True, u'topic': u'N/A', u'host': u'o3r3.int.san3.attcompute.com', u'agent_type': u'Open vSwitch agent', u'created_at': u'2013-07-02 14:50:57', - # u'started_at': u'2013-07-02 14:50:57', u'id': u'3a577f1d-d86e-4f1a-a395-8d4c8e4df1e2', u'configurations': {u'devices': 10}}, - # - # dhcp - # - # {u'binary': u'quantum-dhcp-agent', u'description': None, u'admin_state_up': True, u'heartbeat_timestamp': u'2013-07-02 22:20:23', u'alive': True, - # u'topic': u'dhcp_agent', u'host': u'o5r4.int.san3.attcompute.com', u'agent_type': u'DHCP agent', u'created_at': u'2013-06-26 16:21:02', u'started_at': - # u'2013-06-28 13:32:52', u'id': u'3e8be28e-05a0-472b-9288-a59f8d8d2271', u'configurations': {u'subnets': 4, u'use_namespaces': True, u'dhcp_driver': - # u'quantum.agent.linux.dhcp.Dnsmasq', u'networks': 4, u'dhcp_lease_time': 120, u'ports': 38}}, - # - # l3 - # - # {u'binary': u'quantum-l3-agent', u'description': None, u'admin_state_up': True, u'heartbeat_timestamp': u'2013-07-02 22:20:23', u'alive': True, u'topic': - # u'l3_agent', u'host': u'o3r3.int.san3.attcompute.com', u'agent_type': u'L3 agent', u'created_at': u'2013-07-02 14:50:58', u'started_at': u'2013-07-02 18:00:55', - # u'id': u'6efe494a-616c-41ea-9c8f-2c592f4d46ff', u'configurations': {u'router_id': u'', u'gateway_external_network_id': u'', u'handle_internal_only_routers': True, - # u'use_namespaces': True, u'routers': 5, u'interfaces': 3, u'floating_ips': 9, u'interface_driver': u'quantum.agent.linux.interface.OVSInterfaceDriver', u'ex_gw_ports': 3}}, - """ - - resp = qclient.list_agents() - LOG.debug("list_agents: %s", resp) - if agent_type: - filtered=[] - for agent in resp['agents']: - if agent['agent_type'] == agent_type: - filtered.append(agent) - return filtered - return resp['agents'] - -def agent_alive_id_list(agent_list, agent_type): - """ - Return a list of agents that are alive from an API list of agents - - :param agent_list: API response for list_agents() - - """ - live_list=[] - for agent in agent_list: - if agent['agent_type'] == agent_type and agent['alive'] is True: - live_list.append(agent['id']) - return live_list - -def agent_dead_id_list(agent_list, agent_type): - """ - Return a list of agents that are dead from an API list of agents - - :param agent_list: API response for list_agents() - - """ - dead_list=[] - for agent in agent_list: - if agent['agent_type'] == agent_type and agent['alive'] is False: - dead_list.append(agent['id']) - return dead_list - -if __name__ == '__main__': - - args = parse_args() - setup_logging(args) - - try: - run(args) - sys.exit(0) - except Exception as err: - print "ERROR: %s" % err - sys.exit(1) - except KeyboardInterrupt: - sys.exit(1) diff --git a/chef/cookbooks/openstack-network/metadata.rb b/chef/cookbooks/openstack-network/metadata.rb index 2cd3ab5..806810c 100644 --- a/chef/cookbooks/openstack-network/metadata.rb +++ b/chef/cookbooks/openstack-network/metadata.rb @@ -1,19 +1,20 @@ -name "openstack-network" -maintainer "Jay Pipes " -license "Apache 2.0" -description "Installs and configures the OpenStack Network API Service and various agents and plugins" +# Encoding: utf-8 + +name 'openstack-network' +maintainer 'Jay Pipes ' +license 'Apache 2.0' +description 'Installs and configures the OpenStack Network API Service and various agents and plugins' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "7.0.0" -recipe "openstack-network::server", "Installs packages required for a OpenStack Network server" -recipe "openstack-network::openvswitch", "Installs packages required for OVS" -recipe "openstack-network::metadata_agent", "Installs packages required for a OpenStack Network Metadata Agent" -recipe "openstack-network::identity_registration", "Registers OpenStack Network endpoints and service user with Keystone" +version '9.0.8' +recipe 'openstack-network::client', 'Install packages required for network client' +recipe 'openstack-network::server', 'Installs packages required for a OpenStack Network server' +recipe 'openstack-network::openvswitch', 'Installs packages required for OVS' +recipe 'openstack-network::metadata_agent', 'Installs packages required for a OpenStack Network Metadata Agent' +recipe 'openstack-network::identity_registration', 'Registers OpenStack Network endpoints and service user with Keystone' %w{ ubuntu fedora redhat centos suse }.each do |os| supports os end -depends "openstack-identity", "~> 7.0" -depends "openstack-common", "~> 0.4.0" -depends "mysql" -depends "postgresql" +depends 'openstack-identity', '~> 9.0' +depends 'openstack-common', '~> 9.4' diff --git a/chef/cookbooks/openstack-network/recipes/balancer.rb b/chef/cookbooks/openstack-network/recipes/balancer.rb index e0b0601..deb20c9 100644 --- a/chef/cookbooks/openstack-network/recipes/balancer.rb +++ b/chef/cookbooks/openstack-network/recipes/balancer.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: balancer @@ -20,29 +21,30 @@ # This recipe should be placed in the run_list of the node that # runs the network server or network controller server. -platform_options = node["openstack"]["network"]["platform"] +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return -service "quantum-server" do - service_name platform_options["quantum_server_service"] - supports :status => true, :restart => true +include_recipe 'openstack-network::common' - action :nothing +platform_options = node['openstack']['network']['platform'] + +platform_options['neutron_lb_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade + end end -platform_options["quantum_lb_packages"].each do |pkg| - package pkg do - action :install - end +template '/etc/neutron/lbaas_agent.ini' do + source 'lbaas_agent.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00640 + notifies :restart, 'service[neutron-lb-agent]', :delayed end -directory node["openstack"]["network"]["lbaas_config_path"] do - action :create - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] - recursive true -end - -template "#{node["openstack"]["network"]["lbaas_config_path"]}/lbaas_agent.ini" do - source "lbaas_agent.ini.erb" - notifies :restart, "service[quantum-server]", :immediately +service 'neutron-lb-agent' do + service_name platform_options['neutron_lb_agent_service'] + supports status: true, restart: true + action :enable + subscribes :restart, 'template[/etc/neutron/neutron.conf]', :delayed end diff --git a/chef/cookbooks/openstack-network/recipes/bigswitch.rb b/chef/cookbooks/openstack-network/recipes/bigswitch.rb index 4e387b2..2bb8843 100644 --- a/chef/cookbooks/openstack-network/recipes/bigswitch.rb +++ b/chef/cookbooks/openstack-network/recipes/bigswitch.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: bigswitch @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/brocade.rb b/chef/cookbooks/openstack-network/recipes/brocade.rb index 8c191ce..6fee60e 100644 --- a/chef/cookbooks/openstack-network/recipes/brocade.rb +++ b/chef/cookbooks/openstack-network/recipes/brocade.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: brocade @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/build_openvswitch_source.rb b/chef/cookbooks/openstack-network/recipes/build_openvswitch_source.rb new file mode 100644 index 0000000..cccccb5 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/build_openvswitch_source.rb @@ -0,0 +1,99 @@ +# Encoding: utf-8 +# +# Cookbook Name:: openstack-network +# Recipe:: build_openvswitch_source +# +# Copyright 2013, AT&T +# +# 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. +# + +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +platform_options = node['openstack']['network']['platform'] + +platform_options['neutron_openvswitch_build_packages'].each do |pkg| + package pkg do + action :install + end +end + +ovs_options = node['openstack']['network']['openvswitch'] +src_filename = ovs_options['openvswitch_filename'] +src_filepath = "#{Chef::Config['file_cache_path']}/#{src_filename}" +extract_path = "#{Chef::Config['file_cache_path']}/#{ovs_options['openvswitch_checksum']}" + +remote_file src_filepath do + source ovs_options['openvswitch_url'] + checksum ovs_options['openvswitch_checksum'] + owner 'root' + group 'root' + mode 00644 + not_if { ::File.exists?("#{Chef::Config['file_cache_path']}/#{ovs_options['openvswitch_filename']}") } +end + +bash 'disable_openvswitch_before_upgrade' do + cwd '/tmp' + not_if "dpkg -l | grep openvswitch-switch | grep #{ovs_options['openvswitch_dpkgversion']}" + code <<-EOH + # Politely stop OVS + service openvswitch-switch stop || exit 0 + + sleep 2; + + # After stopping it, ensure it's down + killall -9 ovs-vswitchd || exit 0 + killall -9 ovsdb-server || exit 0 + fi + EOH +end + +bash 'extract_package' do + cwd ::File.dirname(src_filepath) + code <<-EOH + rm -rf #{extract_path} + mkdir -p #{extract_path} + tar xzf #{src_filename} -C #{extract_path} + cd #{extract_path}/#{ovs_options['openvswitch_base_filename']} + DEB_BUILD_OPTIONS='parallel=8' fakeroot debian/rules binary + EOH + not_if "dpkg -l | grep openvswitch-switch | grep #{ovs_options['openvswitch_dpkgversion']}" + notifies :install, 'dpkg_package[openvswitch-common]', :immediately + notifies :install, 'dpkg_package[openvswitch-datapath-dkms]', :immediately + notifies :install, 'dpkg_package[openvswitch-pki]', :immediately + notifies :install, 'dpkg_package[openvswitch-switch]', :immediately +end + +dpkg_package 'openvswitch-common' do + source "#{extract_path}/openvswitch-common_#{ovs_options['openvswitch_dpkgversion']}_#{ovs_options['openvswitch_architecture']}.deb" + action :nothing +end +dpkg_package 'openvswitch-common' do + source "#{extract_path}/openvswitch-common_#{ovs_options['openvswitch_dpkgversion']}_#{ovs_options['openvswitch_architecture']}.deb" + action :nothing +end + +dpkg_package 'openvswitch-datapath-dkms' do + source "#{extract_path}/openvswitch-datapath-dkms_#{ovs_options['openvswitch_dpkgversion']}_all.deb" + action :nothing +end + +dpkg_package 'openvswitch-pki' do + source "#{extract_path}/openvswitch-pki_#{ovs_options['openvswitch_dpkgversion']}_all.deb" + action :nothing +end + +dpkg_package 'openvswitch-switch' do + source "#{extract_path}/openvswitch-switch_#{ovs_options['openvswitch_dpkgversion']}_#{ovs_options['openvswitch_architecture']}.deb" + action :nothing +end diff --git a/chef/cookbooks/openstack-network/recipes/cisco.rb b/chef/cookbooks/openstack-network/recipes/cisco.rb index 17a8c9b..75be005 100644 --- a/chef/cookbooks/openstack-network/recipes/cisco.rb +++ b/chef/cookbooks/openstack-network/recipes/cisco.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: cisco @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/client.rb b/chef/cookbooks/openstack-network/recipes/client.rb new file mode 100644 index 0000000..1369e33 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/client.rb @@ -0,0 +1,32 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-network +# Recipe:: client +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +platform_options = node['openstack']['network']['platform'] +platform_options['neutron_client_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end diff --git a/chef/cookbooks/openstack-network/recipes/common.rb b/chef/cookbooks/openstack-network/recipes/common.rb index e78dc47..3aab2ae 100644 --- a/chef/cookbooks/openstack-network/recipes/common.rb +++ b/chef/cookbooks/openstack-network/recipes/common.rb @@ -1,9 +1,11 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: common # # Copyright 2013, AT&T # Copyright 2013, SUSE Linux GmbH +# Copyright 2013-2014, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,391 +20,422 @@ # limitations under the License. # -require "uri" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return +require 'uri' + +# Make Openstack object available in Chef::Recipe class ::Chef::Recipe include ::Openstack end -platform_options = node["openstack"]["network"]["platform"] - -driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase -main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] -core_plugin = node["openstack"]["network"]["core_plugin"] - -if node["openstack"]["network"]["syslog"]["use"] - include_recipe "openstack-common::logging" +# Make Openstack object available in Chef::Resource::RubyBlock +class ::Chef::Resource::RubyBlock + include ::Openstack end -platform_options["nova_network_packages"].each do |pkg| +platform_options = node['openstack']['network']['platform'] + +core_plugin = node['openstack']['network']['core_plugin'] +main_plugin = node['openstack']['network']['core_plugin_map'][core_plugin.split('.').last.downcase] + +if node['openstack']['network']['syslog']['use'] + include_recipe 'openstack-common::logging' +end + +platform_options['nova_network_packages'].each do |pkg| package pkg do action :purge end end -platform_options["quantum_packages"].each do |pkg| +platform_options['neutron_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end -directory "/etc/quantum/plugins" do +db_type = node['openstack']['db']['network']['service_type'] +platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade + end +end + +directory '/etc/neutron/plugins' do recursive true - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00700 action :create end -directory "/var/cache/quantum" do - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] +directory '/var/cache/neutron' do + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00700 action :create end -directory ::File.dirname node["openstack"]["network"]["api"]["auth"]["cache_dir"] do - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] +directory ::File.dirname node['openstack']['network']['api']['auth']['cache_dir'] do + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00700 - only_if { node["openstack"]["auth"]["strategy"] == "pki" } + only_if { node['openstack']['auth']['strategy'] == 'pki' } end -# This will copy recursively all the files in -# /files/default/etc/quantum/rootwrap.d -remote_directory "/etc/quantum/rootwrap.d" do - source "etc/quantum/rootwrap.d" - files_owner node["openstack"]["network"]["platform"]["user"] - files_group node["openstack"]["network"]["platform"]["group"] - files_mode 00700 -end - -template "/etc/quantum/rootwrap.conf" do - source "rootwrap.conf.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] +template '/etc/neutron/rootwrap.conf' do + source 'rootwrap.conf.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 end -template "/etc/quantum/policy.json" do - source "policy.json.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] - mode 00644 - - notifies :restart, "service[quantum-server]", :delayed +if node['openstack']['network']['policyfile_url'] + remote_file '/etc/neutron/policy.json' do + source node['openstack']['network']['policyfile_url'] + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00644 + notifies :restart, 'service[neutron-server]' + end end -# Added by Sam, CentOS think Qpid as it's default queue, it will auto add qpid -# configuration at /etc/quantum/quantum.conf when invoke /usr/bin/quantum-xxx-setup, -# because these scripts was hard coded a line as below: -# "openstack-config --set ${Q_CONF} DEFAULT -# rpc_backend quantum.openstack.common.rpc.impl_qpid" -# It should be a bug of redhat OS -execute "delete_auto_qpid" do - command %Q|sed -i "s/^rpc_backend = quantum.openstack.common.rpc.impl_qpid/rpc_backend = quantum.openstack.common.rpc.impl_kombu/g" /etc/quantum/quantum.conf; sed -i "s/^qpid_hostname =//g" /etc/quantum/quantum.conf| - only_if { - (node['openstack']['mq']['service_type'] == "rabbitmq") and - platform?(%w{fedora centos redhat}) - } - action :nothing +mq_service_type = node['openstack']['mq']['network']['service_type'] + +if mq_service_type == 'rabbitmq' + rabbit_hosts = rabbit_servers if node['openstack']['mq']['network']['rabbit']['ha'] + mq_password = get_password 'user', \ + node['openstack']['mq']['user'], \ + node['openstack']['mq']['password'] +elsif mq_service_type == 'qpid' + mq_password = get_password 'user', node['openstack']['mq']['network']['qpid']['username'] end - -rabbit_server_role = node["openstack"]["network"]["rabbit_server_chef_role"] -if node["openstack"]["network"]["rabbit"]["ha"] - rabbit_hosts = rabbit_servers -end -rabbit_pass = user_password node['openstack']['mq']['password'] - -identity_endpoint = endpoint "identity-api" +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' auth_uri = ::URI.decode identity_endpoint.to_s +auth_uri = auth_uri_transform identity_endpoint.to_s, node['openstack']['network']['api']['auth']['version'] + db_user = node['openstack']['db']['network']['username'] -db_pass = db_password node['openstack']['db']['network']['password'] -sql_connection = db_uri("network", db_user, db_pass) +db_pass = get_password 'db', 'neutron' +sql_connection = db_uri('network', db_user, db_pass) -api_endpoint = endpoint "network-api" -service_pass = service_password node['openstack']['identity']['network']['password'] -service_tenant_name = node['openstack']['identity']['network']['tenant'] -service_user = node['openstack']['identity']['network']['username'] +network_api_bind = endpoint 'network-api-bind' +service_pass = get_password 'service', 'openstack-network' -if node["openstack"]["network"]["api"]["bind_interface"].nil? - bind_address = api_endpoint.host - bind_port = api_endpoint.port -else - bind_address = address_for node["openstack"]["network"]["api"]["bind_interface"] - #bind_address = node['openstack']['endpoints']['network-api']['host'] - bind_port = node["openstack"]["network"]["api"]["bind_port"] -end - -# retrieve the local interface for tunnels -if node["openstack"]["network"]["openvswitch"]["local_ip_interface"].nil? - local_ip = localhost -else - local_ip = node["openstack"]["network"]["openvswitch"]["local_ip"] -end - -platform_options["quantum_client_packages"].each do |pkg| +platform_options['neutron_client_packages'].each do |pkg| package pkg do action :upgrade - options platform_options["package_overrides"] + options platform_options['package_overrides'] end end # all recipes include common.rb, and some servers # may just be running a subset of agents (like l3_agent) # and not the api server components, so we ignore restart -# failures here as there may be no quantum-server process -service "quantum-server" do - service_name platform_options["quantum_server_service"] - supports :status => true, :restart => true +# failures here as there may be no neutron-server process +service 'neutron-server' do + service_name platform_options['neutron_server_service'] + supports status: true, restart: true ignore_failure true action :nothing end -template "/etc/quantum/quantum.conf" do - source "quantum.conf.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] - mode 00644 - variables( - :bind_address => bind_address, - :bind_port => bind_port, - :rabbit_hosts => rabbit_hosts, - :rabbit_pass => rabbit_pass, - :core_plugin => core_plugin, - :identity_endpoint => identity_endpoint, - :service_tenant_name => service_tenant_name, - :service_user => service_user, - :service_pass => service_pass - ) - - notifies :restart, "service[quantum-server]", :delayed +# Nova interactions +nova_endpoint = endpoint 'compute-api' +# TODO(MRV): Need to allow for this in common. +# Neutron will append the admin_tenant_id for these nova interaction calls, +# remove the tenant_id so we don't end up with two of them on the url. +# Need to also allow for getting at nova endpoint version. +# https://github.com/openstack/neutron/blob/master/neutron/common/config.py#L89 +# https://github.com/openstack/neutron/blob/master/neutron/notifiers/nova.py#L43 +nova_version = node['openstack']['network']['nova']['url_version'] +nova_endpoint = uri_from_hash('host' => nova_endpoint.host.to_s, 'port' => nova_endpoint.port.to_s, 'path' => nova_version) +nova_admin_pass = get_password 'service', 'openstack-compute' +ruby_block 'query service tenant uuid' do + # query keystone for the service tenant uuid + block do + begin + admin_user = node['openstack']['identity']['admin_user'] + admin_tenant = node['openstack']['identity']['admin_tenant_name'] + env = openstack_command_env admin_user, admin_tenant + tenant_id = identity_uuid 'tenant', 'name', 'service', env + Chef::Log.error("service tenant UUID tenant_id=#{tenant_id} for nova_admin_tenant_id not found.") if tenant_id.nil? + return false if tenant_id.nil? + # Chef::Log.error('service tenant UUID for nova_admin_tenant_id not found.') if tenant_id.nil? + node.set['openstack']['network']['nova']['admin_tenant_id'] = tenant_id + rescue RuntimeError => e + # Chef::Log.error("Could not query service tenant UUID for nova_admin_tenant_id. Error was #{e.message}") + end + end + action :run + only_if do + (node['openstack']['network']['nova']['notify_nova_on_port_status_changes'] == 'True' || + node['openstack']['network']['nova']['notify_nova_on_port_data_changes'] == 'True') && + node['openstack']['network']['nova']['admin_tenant_id'].nil? + end end - -template "/etc/quantum/api-paste.ini" do - source "api-paste.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] +template '/etc/neutron/neutron.conf' do + source 'neutron.conf.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 variables( - "identity_endpoint" => identity_endpoint, - "service_tenant_name" => service_tenant_name, - "service_user" => service_user, - "service_pass" => service_pass + bind_address: network_api_bind.host, + bind_port: network_api_bind.port, + rabbit_hosts: rabbit_hosts, + mq_service_type: mq_service_type, + mq_password: mq_password, + core_plugin: core_plugin, + auth_uri: auth_uri, + identity_admin_endpoint: identity_admin_endpoint, + service_pass: service_pass, + sql_connection: sql_connection, + nova_endpoint: nova_endpoint, + nova_admin_pass: nova_admin_pass ) - notifies :restart, "service[quantum-server]", :delayed + notifies :restart, 'service[neutron-server]', :delayed end -directory "/etc/quantum/plugins/#{main_plugin}" do +template '/etc/neutron/api-paste.ini' do + source 'api-paste.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00640 + + notifies :restart, 'service[neutron-server]', :delayed +end + +directory "/etc/neutron/plugins/#{main_plugin}" do recursive true - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00700 end # For several plugins, the plugin configuration -# is required by both the quantum-server and +# is required by both the neutron-server and # ancillary services that may be on different # physical servers like the l3 agent, so we assume # the plugin configuration is a "common" file template_file = nil +plugin_file = '/etc/neutron/plugin.ini' case main_plugin -when "bigswitch" +when 'bigswitch' - template_file = "/etc/quantum/plugins/bigswitch/restproxy.ini" - template "/etc/quantum/plugins/bigswitch/restproxy.ini" do - source "plugins/bigswitch/restproxy.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + template_file = '/etc/neutron/plugins/bigswitch/restproxy.ini' + + template template_file do + source 'plugins/bigswitch/restproxy.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - variables( - :sql_connection => sql_connection - ) - notifies :restart, "service[quantum-server]", :delayed + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed end -when "brocade" +when 'brocade' - template_file = "/etc/quantum/plugins/brocade/brocade.ini" - template "/etc/quantum/plugins/brocade/brocade.ini" do - source "plugins/brocade/brocade.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + template_file = '/etc/neutron/plugins/brocade/brocade.ini' + + template template_file do + source 'plugins/brocade/brocade.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - variables( - :sql_connection => sql_connection - ) - notifies :restart, "service[quantum-server]", :delayed + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed end -when "cisco" +when 'cisco' - template_file = "/etc/quantum/plugins/cisco/cisco_plugins.ini" - template "/etc/quantum/plugins/cisco/cisco_plugins.ini" do - source "plugins/cisco/cisco_plugins.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + template_file = '/etc/neutron/plugins/cisco/cisco_plugins.ini' + + template template_file do + source 'plugins/cisco/cisco_plugins.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - variables( - :sql_connection => sql_connection - ) - notifies :restart, "service[quantum-server]", :delayed + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed end -when "hyperv" +when 'hyperv' - template_file = "/etc/quantum/plugins/hyperv/hyperv_quantum_plugin.ini.erb" - template "/etc/quantum/plugins/hyperv/hyperv_quantum_plugin.ini.erb" do - source "plugins/hyperv/hyperv_quantum_plugin.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + template_file = '/etc/neutron/plugins/hyperv/hyperv_neutron_plugin.ini.erb' + + template template_file do + source 'plugins/hyperv/hyperv_neutron_plugin.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - variables( - :sql_connection => sql_connection - ) - notifies :restart, "service[quantum-server]", :delayed + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed end -when "linuxbridge" +when 'linuxbridge' - template_file = "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini" - template "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini" do - source "plugins/linuxbridge/linuxbridge_conf.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + linuxbridge_endpoint = endpoint 'network-linuxbridge' + template_file = '/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini' + + template template_file do + source 'plugins/linuxbridge/linuxbridge_conf.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 variables( - :sql_connection => sql_connection + local_ip: linuxbridge_endpoint.host ) - notifies :restart, "service[quantum-server]", :delayed - end - -when "midonet" - - template_file = "/etc/quantum/plugins/metaplugin/metaplugin.ini" - template "/etc/quantum/plugins/metaplugin/metaplugin.ini" do - source "plugins/metaplugin/metaplugin.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] - mode 00644 - variables( - :sql_connection => sql_connection - ) - - notifies :restart, "service[quantum-server]", :delayed - end - -when "nec" - - template_file = "/etc/quantum/plugins/nec/nec.ini" - template "/etc/quantum/plugins/nec/nec.ini" do - source "plugins/nec/nec.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] - mode 00644 - variables( - :sql_connection => sql_connection - ) - - notifies :restart, "service[quantum-server]", :delayed - end - -when "nicira" - - template_file = "/etc/quantum/plugins/nicira/nvp.ini" - template "/etc/quantum/plugins/nicira/nvp.ini" do - source "plugins/nicira/nvp.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] - mode 00644 - variables( - :sql_connection => sql_connection - ) - - notifies :restart, "service[quantum-server]", :delayed - end - -when "openvswitch" - - template_file = "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini" - - service "quantum-plugin-openvswitch-agent" do - service_name platform_options["quantum_openvswitch_agent_service"] - action :nothing - end - - template "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini" do - source "plugins/openvswitch/ovs_quantum_plugin.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] - mode 00644 - variables( - :sql_connection => sql_connection, - :local_ip => local_ip - ) - notifies :restart, "service[quantum-server]", :delayed - if node.run_list.expand(node.chef_environment).recipes.include?("openstack-network::openvswitch") - notifies :restart, "service[quantum-plugin-openvswitch-agent]", :delayed + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed + if node.run_list.expand(node.chef_environment).recipes.include?('openstack-network::linuxbridge') + notifies :restart, 'service[neutron-plugin-linuxbridge-agent]', :delayed end end +when 'midonet' -when "plumgrid" + template_file = '/etc/neutron/plugins/metaplugin/metaplugin.ini' - template_file = "/etc/quantum/plugins/plumgrid/plumgrid.ini" - template "/etc/quantum/plugins/plumgrid/plumgrid.ini" do - source "plugins/plumgrid/plumgrid.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + template template_file do + source 'plugins/metaplugin/metaplugin.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - variables( - :sql_connection => sql_connection - ) - notifies :restart, "service[quantum-server]", :delayed + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed end -when "ryu" +when 'ml2' - template_file = "/etc/quantum/plugins/ryu/ryu.ini" - template "/etc/quantum/plugins/ryu/ryu.ini" do - source "plugins/ryu/ryu.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] + template_file = '/etc/neutron/plugins/ml2/ml2_conf.ini' + + template template_file do + source 'plugins/ml2/ml2_conf.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - variables( - :sql_connection => sql_connection - ) - notifies :restart, "service[quantum-server]", :delayed + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed end +when 'nec' + + template_file = '/etc/neutron/plugins/nec/nec.ini' + + template template_file do + source 'plugins/nec/nec.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00644 + + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed + end + +when 'nicira' + + template_file = '/etc/neutron/plugins/nicira/nvp.ini' + + template template_file do + source 'plugins/nicira/nvp.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00644 + + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed + end + +when 'openvswitch' + + openvswitch_endpoint = endpoint 'network-openvswitch' + template_file = '/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini' + + template template_file do + source 'plugins/openvswitch/ovs_neutron_plugin.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00644 + variables( + local_ip: openvswitch_endpoint.host + ) + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed + if node.run_list.expand(node.chef_environment).recipes.include?('openstack-network::openvswitch') + notifies :restart, 'service[neutron-plugin-openvswitch-agent]', :delayed + end + end + +when 'plumgrid' + + template_file = '/etc/neutron/plugins/plumgrid/plumgrid.ini' + + template template_file do + source 'plugins/plumgrid/plumgrid.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00644 + + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed + end + +when 'ryu' + + template_file = '/etc/neutron/plugins/ryu/ryu.ini' + + template template_file do + source 'plugins/ryu/ryu.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00644 + + notifies :create, "link[#{plugin_file}]", :immediately + notifies :restart, 'service[neutron-server]', :delayed + end + +else + Chef::Log.fatal("Main plugin #{main_plugin}is not supported") end +link plugin_file do + to template_file + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + action :nothing + only_if { platform_family? %w{fedora rhel} } +end -template "/etc/default/quantum-server" do - source "quantum-server.erb" - owner "root" - group "root" +node.set['openstack']['network']['plugin_config_file'] = template_file + +template '/etc/default/neutron-server' do + source 'neutron-server.erb' + owner 'root' + group 'root' mode 00644 - variables( - :plugin_config => template_file - ) - only_if { - node.run_list.expand(node.chef_environment).recipes.include?("openstack-network::server") - platform?(%w{ubuntu debian}) - } + variables( + plugin_config: template_file + ) + only_if do + node.run_list.expand(node.chef_environment).recipes.include?('openstack-network::server') + platform_family?(%w{debian}) + end end diff --git a/chef/cookbooks/openstack-network/recipes/dhcp_agent.rb b/chef/cookbooks/openstack-network/recipes/dhcp_agent.rb index d67878b..623da8b 100644 --- a/chef/cookbooks/openstack-network/recipes/dhcp_agent.rb +++ b/chef/cookbooks/openstack-network/recipes/dhcp_agent.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: dhcp_agent @@ -17,54 +18,52 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return -platform_options = node["openstack"]["network"]["platform"] -driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase -main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] +include_recipe 'openstack-network::common' -platform_options["quantum_dhcp_packages"].each do |pkg| +platform_options = node['openstack']['network']['platform'] +core_plugin = node['openstack']['network']['core_plugin'] +main_plugin = node['openstack']['network']['core_plugin_map'][core_plugin.split('.').last.downcase] + +platform_options['neutron_dhcp_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] - action :install + options platform_options['package_overrides'] + action :upgrade end end -service "quantum-dhcp-agent" do - service_name platform_options["quantum_dhcp_agent_service"] - supports :status => true, :restart => true +service 'neutron-dhcp-agent' do + service_name platform_options['neutron_dhcp_agent_service'] + supports status: true, restart: true action :enable + subscribes :restart, 'template[/etc/neutron/neutron.conf]' end # Some plugins have DHCP functionality, so we install the plugin # Python package and include the plugin-specific recipe here... -package platform_options["quantum_plugin_package"].gsub("%plugin%", main_plugin) do - options platform_options["package_overrides"] - action :install - # plugins are installed by the main openstack-quantum package on SUSE - not_if { platform_family? "suse" } +package platform_options['neutron_plugin_package'].gsub('%plugin%', main_plugin) do + options platform_options['package_overrides'] + action :upgrade + # plugins are installed by the main openstack-neutron package on SUSE + not_if { platform_family? 'suse' } end -#execute "quantum-dhcp-setup --plugin #{main_plugin}" do -# notifies :run, "execute[delete_auto_qpid]", :immediately -# only_if { platform?(%w(fedora redhat centos)) } # :pragma-foodcritic: ~FC024 - won't fix this -#end - -template "/etc/quantum/dnsmasq.conf" do - source "dnsmasq.conf.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] +template '/etc/neutron/dnsmasq.conf' do + source 'dnsmasq.conf.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - notifies :restart, "service[quantum-dhcp-agent]", :delayed + notifies :restart, 'service[neutron-dhcp-agent]', :delayed end -template "/etc/quantum/dhcp_agent.ini" do - source "dhcp_agent.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] +template '/etc/neutron/dhcp_agent.ini' do + source 'dhcp_agent.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - notifies :restart, "service[quantum-dhcp-agent]", :immediately + notifies :restart, 'service[neutron-dhcp-agent]', :immediately end # Deal with ubuntu precise dnsmasq 2.59 version by custom @@ -78,11 +77,10 @@ end # # Would prefer a PPA or backport but there are none and upstream # has no plans to fix -if node['lsb'] && node['lsb']['codename'] == "precise" - - platform_options["quantum_dhcp_build_packages"].each do |pkg| +if node['lsb'] && node['lsb']['codename'] == 'precise' && node['openstack']['network']['dhcp']['dnsmasq_compile'] == true + platform_options['neutron_dhcp_build_packages'].each do |pkg| package pkg do - action :install + action :upgrade end end @@ -111,22 +109,39 @@ if node['lsb'] && node['lsb']['codename'] == "precise" debian/rules binary EOH not_if { ::File.exists?(extract_path) } - notifies :install, "dpkg_package[dnsmasq-utils]", :immediately - notifies :install, "dpkg_package[dnsmasq-base]", :immediately - notifies :install, "dpkg_package[dnsmasq]", :immediately + notifies :install, 'dpkg_package[dnsmasq-utils]', :immediately + notifies :install, 'dpkg_package[dnsmasq-base]', :immediately + notifies :install, 'dpkg_package[dnsmasq]', :immediately end - dpkg_package "dnsmasq-utils" do + # wait for dnsmasq to start properly. Don't wait forever + ruby_block 'wait for dnsmasq' do + block do + count = 5 + counter = 0 + while counter < count + run = Mixlib::ShellOut.new('dig +time=5 @localhost | grep -q root-server[s]').run_command + break unless run.exitstatus > 0 + counter += 1 + Chef::Log.fatal('dnsmasq never became ready') if counter == count + sleep 1 + end + end + action :nothing + end + + dpkg_package 'dnsmasq-utils' do source "#{extract_path}/../dnsmasq-utils_#{dhcp_options['dnsmasq_dpkgversion']}_#{dhcp_options['dnsmasq_architecture']}.deb" action :nothing end - dpkg_package "dnsmasq-base" do + dpkg_package 'dnsmasq-base' do source "#{extract_path}/../dnsmasq-base_#{dhcp_options['dnsmasq_dpkgversion']}_#{dhcp_options['dnsmasq_architecture']}.deb" action :nothing end - dpkg_package "dnsmasq" do + dpkg_package 'dnsmasq' do source "#{extract_path}/../dnsmasq_#{dhcp_options['dnsmasq_dpkgversion']}_all.deb" action :nothing + notifies :create, 'ruby_block[wait for dnsmasq]', :immediately end end diff --git a/chef/cookbooks/openstack-network/recipes/hyperv.rb b/chef/cookbooks/openstack-network/recipes/hyperv.rb index dff4b5e..be68fe6 100644 --- a/chef/cookbooks/openstack-network/recipes/hyperv.rb +++ b/chef/cookbooks/openstack-network/recipes/hyperv.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: hyperv @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/identity_registration.rb b/chef/cookbooks/openstack-network/recipes/identity_registration.rb index fdb727d..382dae5 100644 --- a/chef/cookbooks/openstack-network/recipes/identity_registration.rb +++ b/chef/cookbooks/openstack-network/recipes/identity_registration.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: identity_registration @@ -18,39 +19,42 @@ # limitations under the License. # -require "uri" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return +require 'uri' + +# Make Openstack object available in Chef::Recipe class ::Chef::Recipe include ::Openstack end -identity_admin_endpoint = endpoint "identity-admin" +identity_admin_endpoint = endpoint 'identity-admin' -bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +bootstrap_token = get_secret 'openstack_identity_bootstrap_token' auth_uri = ::URI.decode identity_admin_endpoint.to_s -api_endpoint = endpoint "network-api" +api_endpoint = endpoint 'network-api' -service_pass = service_password "openstack-network" -service_tenant_name = node["openstack"]["network"]["service_tenant_name"] -service_user = node["openstack"]["network"]["service_user"] -service_role = node["openstack"]["network"]["service_role"] +service_pass = get_password 'service', 'openstack-network' +service_tenant_name = node['openstack']['network']['service_tenant_name'] +service_user = node['openstack']['network']['service_user'] +service_role = node['openstack']['network']['service_role'] -openstack_identity_register "Register Network API Service" do +openstack_identity_register 'Register Network API Service' do auth_uri auth_uri bootstrap_token bootstrap_token - service_name node["openstack"]["network"]["service_name"] - service_type node["openstack"]["network"]["service_type"] - service_description "OpenStack Network Service" + service_name node['openstack']['network']['service_name'] + service_type node['openstack']['network']['service_type'] + service_description 'OpenStack Network Service' action :create_service end -openstack_identity_register "Register Network Endpoint" do +openstack_identity_register 'Register Network Endpoint' do auth_uri auth_uri bootstrap_token bootstrap_token - service_type node["openstack"]["network"]["service_type"] - endpoint_region node["openstack"]["network"]["region"] + service_type node['openstack']['network']['service_type'] + endpoint_region node['openstack']['network']['region'] endpoint_adminurl api_endpoint.to_s endpoint_internalurl api_endpoint.to_s endpoint_publicurl api_endpoint.to_s @@ -58,11 +62,11 @@ openstack_identity_register "Register Network Endpoint" do action :create_endpoint end -openstack_identity_register "Register Service Tenant" do +openstack_identity_register 'Register Service Tenant' do auth_uri auth_uri bootstrap_token bootstrap_token tenant_name service_tenant_name - tenant_description "Service Tenant" + tenant_description 'Service Tenant' action :create_tenant end diff --git a/chef/cookbooks/openstack-network/recipes/l3_agent.rb b/chef/cookbooks/openstack-network/recipes/l3_agent.rb index a346c04..e3a50e6 100644 --- a/chef/cookbooks/openstack-network/recipes/l3_agent.rb +++ b/chef/cookbooks/openstack-network/recipes/l3_agent.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: l3_agent @@ -17,52 +18,51 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return -platform_options = node["openstack"]["network"]["platform"] -driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase -main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] +include_recipe 'openstack-network::common' -# Sam added the if case, cannot find the independent l3-agent packages at centos 6.4 -platform_options["quantum_l3_packages"].each do |pkg| +platform_options = node['openstack']['network']['platform'] +core_plugin = node['openstack']['network']['core_plugin'] +main_plugin = node['openstack']['network']['core_plugin_map'][core_plugin.split('.').last.downcase] + +platform_options['neutron_l3_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] - action :install + options platform_options['package_overrides'] + action :upgrade # The providers below do not use the generic L3 agent... - not_if { ["nicira", "plumgrid", "bigswitch"].include?(main_plugin) } + not_if { ['nicira', 'plumgrid', 'bigswitch'].include?(main_plugin) } end end -service "quantum-l3-agent" do - service_name platform_options["quantum_l3_agent_service"] - supports :status => true, :restart => true +service 'neutron-l3-agent' do + service_name platform_options['neutron_l3_agent_service'] + supports status: true, restart: true action :enable + subscribes :restart, 'template[/etc/neutron/neutron.conf]' end -#execute "quantum-l3-setup --plugin #{main_plugin}" do -# notifies :run, "execute[delete_auto_qpid]", :immediately -# only_if { -# platform?(%w(fedora redhat centos)) and not # :pragma-foodcritic: ~FC024 - won't fix this -# ["nicira", "plumgrid", "bigswitch"].include?(main_plugin) -# } -#end - -template "/etc/quantum/l3_agent.ini" do - source "l3_agent.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] +template '/etc/neutron/l3_agent.ini' do + source 'l3_agent.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 - notifies :restart, "service[quantum-l3-agent]", :immediately + notifies :restart, 'service[neutron-l3-agent]', :immediately end -if not ["nicira", "plumgrid", "bigswitch", "linuxbridge"].include?(main_plugin) - # See http://docs.openstack.org/trunk/openstack-network/admin/content/install_quantum-l3.html - ext_bridge = node["openstack"]["network"]["l3"]["external_network_bridge"] - ext_bridge_iface = node["openstack"]["network"]["l3"]["external_network_bridge_interface"] - execute "create external network bridge" do +driver_name = node['openstack']['network']['interface_driver'].split('.').last +# See http://docs.openstack.org/admin-guide-cloud/content/section_adv_cfg_l3_agent.html +case driver_name +when 'OVSInterfaceDriver' + ext_bridge = node['openstack']['network']['l3']['external_network_bridge'] + ext_bridge_iface = node['openstack']['network']['l3']['external_network_bridge_interface'] + execute 'create external network bridge' do command "ovs-vsctl add-br #{ext_bridge} && ovs-vsctl add-port #{ext_bridge} #{ext_bridge_iface}" action :run - not_if "ovs-vsctl show | grep 'Bridge #{ext_bridge}'" + not_if "ovs-vsctl br-exists #{ext_bridge}" + only_if "ip link show #{ext_bridge_iface}" end +when 'BridgeInterfaceDriver' + # TODO: Handle linuxbridge case end diff --git a/chef/cookbooks/openstack-network/recipes/linuxbridge.rb b/chef/cookbooks/openstack-network/recipes/linuxbridge.rb index d34df30..41d656b 100644 --- a/chef/cookbooks/openstack-network/recipes/linuxbridge.rb +++ b/chef/cookbooks/openstack-network/recipes/linuxbridge.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: linuxbridge @@ -17,19 +18,41 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return -platform_options = node["openstack"]["network"]["platform"] +include_recipe 'openstack-network::common' -platform_options["quantum_linuxbridge_agent_packages"].each do |pkg| +platform_options = node['openstack']['network']['platform'] + +platform_options['neutron_linuxbridge_agent_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] - action :install + options platform_options['package_overrides'] + action :upgrade end end -service "quantum-plugin-linuxbridge-agent" do - service_name platform_options["quantum_linuxbridge_agent_service"] - supports :status => true, :restart => true - action :enable +directory '/etc/neutron/plugins/linuxbridge' do + recursive true + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00700 +end + +linuxbridge_endpoint = endpoint 'network-linuxbridge' +template '/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini' do + source 'plugins/linuxbridge/linuxbridge_conf.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00644 + variables( + local_ip: linuxbridge_endpoint.host + ) +end + +service 'neutron-plugin-linuxbridge-agent' do + service_name platform_options['neutron_linuxbridge_agent_service'] + supports status: true, restart: true + action :enable + subscribes :restart, 'template[/etc/neutron/neutron.conf]' + subscribes :restart, 'template[/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini]' end diff --git a/chef/cookbooks/openstack-network/recipes/metadata_agent.rb b/chef/cookbooks/openstack-network/recipes/metadata_agent.rb index 1c83aaf..2da97eb 100644 --- a/chef/cookbooks/openstack-network/recipes/metadata_agent.rb +++ b/chef/cookbooks/openstack-network/recipes/metadata_agent.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: metadata_agent @@ -17,43 +18,42 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return -platform_options = node["openstack"]["network"]["platform"] -driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase -main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] +include_recipe 'openstack-network::common' -identity_endpoint = endpoint "identity-api" -service_tenant_name = node['openstack']['identity']['network']['tenant'] -service_user = node['openstack']['identity']['network']['username'] -service_pass = service_password node['openstack']['identity']['network']['password'] -metadata_secret = secret "secrets", node["openstack"]["network"]["metadata"]["secret_name"] +platform_options = node['openstack']['network']['platform'] -template "/etc/quantum/metadata_agent.ini" do - source "metadata_agent.ini.erb" - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] +identity_endpoint = endpoint 'identity-api' +service_pass = get_password 'service', 'openstack-network' +metadata_secret = get_secret node['openstack']['network']['metadata']['secret_name'] +compute_api_endpoint = endpoint 'compute-api' || {} + +template '/etc/neutron/metadata_agent.ini' do + source 'metadata_agent.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] mode 00644 variables( - :identity_endpoint => identity_endpoint, - :metadata_secret => metadata_secret, - :service_tenant_name => service_tenant_name, - :service_user => service_user, - :service_pass => service_pass + identity_endpoint: identity_endpoint, + metadata_secret: metadata_secret, + nova_metadata_ip: compute_api_endpoint.host, + service_pass: service_pass ) - notifies :restart, "service[quantum-metadata-agent]", :immediately + notifies :restart, 'service[neutron-metadata-agent]', :immediately action :create end -platform_options["quantum_metadata_agent_packages"].each do |pkg| +platform_options['neutron_metadata_agent_packages'].each do |pkg| package pkg do - action :install - options platform_options["package_overrides"] + action :upgrade + options platform_options['package_overrides'] end end -service "quantum-metadata-agent" do - service_name platform_options["quantum_metadata_agent_service"] - supports :status => true, :restart => true +service 'neutron-metadata-agent' do + service_name platform_options['neutron_metadata_agent_service'] + supports status: true, restart: true action :enable + subscribes :restart, 'template[/etc/neutron/neutron.conf]' end diff --git a/chef/cookbooks/openstack-network/recipes/metaplugin.rb b/chef/cookbooks/openstack-network/recipes/metaplugin.rb index 2351012..c7e7d5e 100644 --- a/chef/cookbooks/openstack-network/recipes/metaplugin.rb +++ b/chef/cookbooks/openstack-network/recipes/metaplugin.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: metaplugin @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/midonet.rb b/chef/cookbooks/openstack-network/recipes/midonet.rb index fd12cc3..db13faa 100644 --- a/chef/cookbooks/openstack-network/recipes/midonet.rb +++ b/chef/cookbooks/openstack-network/recipes/midonet.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: midonet @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/nec.rb b/chef/cookbooks/openstack-network/recipes/nec.rb index 89a8cd0..7b3f63b 100644 --- a/chef/cookbooks/openstack-network/recipes/nec.rb +++ b/chef/cookbooks/openstack-network/recipes/nec.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: nec @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/nicira.rb b/chef/cookbooks/openstack-network/recipes/nicira.rb index abe667e..9cb4433 100644 --- a/chef/cookbooks/openstack-network/recipes/nicira.rb +++ b/chef/cookbooks/openstack-network/recipes/nicira.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: nicira @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/openvswitch.rb b/chef/cookbooks/openstack-network/recipes/openvswitch.rb index f92a8d8..99d511f 100644 --- a/chef/cookbooks/openstack-network/recipes/openvswitch.rb +++ b/chef/cookbooks/openstack-network/recipes/openvswitch.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: opensvswitch @@ -17,163 +18,210 @@ # limitations under the License. # +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + require 'uri' +# Make Openstack object available in Chef::Recipe class ::Chef::Recipe include ::Openstack end -include_recipe "openstack-network::common" +include_recipe 'openstack-network::common' -platform_options = node["openstack"]["network"]["platform"] -driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase -main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] -core_plugin = node["openstack"]["network"]["core_plugin"] +platform_options = node['openstack']['network']['platform'] +core_plugin = node['openstack']['network']['core_plugin'] +main_plugin = node['openstack']['network']['core_plugin_map'][core_plugin.split('.').last.downcase] + +if platform_family?('debian') -if platform?("ubuntu", "debian") # obtain kernel version for kernel header # installation on ubuntu and debian - kernel_ver = node["kernel"]["release"] + kernel_ver = node['kernel']['release'] package "linux-headers-#{kernel_ver}" do - options platform_options["package_overrides"] - action :install + options platform_options['package_overrides'] + action :upgrade end end -directory "/var/run/openvswitch" do +if node['openstack']['network']['openvswitch']['use_source_version'] + if node['lsb'] && node['lsb']['codename'] == 'precise' + include_recipe 'openstack-network::build_openvswitch_source' + end +else + platform_options['neutron_openvswitch_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + action :upgrade + end + end +end + +if platform_family?('debian') + + # NOTE:(mancdaz):sometimes the openvswitch module does not get reloaded + # properly when openvswitch-datapath-dkms recompiles it. This ensures + # that it does + + begin + if resources('package[openvswitch-datapath-dkms]') + execute '/usr/share/openvswitch/scripts/ovs-ctl force-reload-kmod' do + action :nothing + subscribes :run, resources('package[openvswitch-datapath-dkms]'), :immediately + end + end + rescue Chef::Exceptions::ResourceNotFound # rubocop:disable HandleExceptions + end + +end + +service 'neutron-openvswitch-switch' do + service_name platform_options['neutron_openvswitch_service'] + supports status: true, restart: true + action [:enable, :start] +end + +if node.run_list.expand(node.chef_environment).recipes.include?('openstack-network::server') + service 'neutron-server' do + service_name platform_options['neutron_server_service'] + supports status: true, restart: true + action :nothing + end +end + +platform_options['neutron_openvswitch_agent_packages'].each do |pkg| + package pkg do + action :upgrade + options platform_options['package_overrides'] + end +end + +directory '/etc/neutron/plugins/openvswitch' do recursive true - owner node["openstack"]["network"]["platform"]["user"] - group node["openstack"]["network"]["platform"]["group"] - mode 00755 - action :create + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00700 + only_if { platform_family?('rhel') } end +openvswitch = address node['openstack']['network']['openvswitch'] +template '/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini' do + source 'plugins/openvswitch/ovs_neutron_plugin.ini.erb' + owner node['openstack']['network']['platform']['user'] + group node['openstack']['network']['platform']['group'] + mode 00644 + variables( + local_ip: openvswitch + ) + only_if { platform_family?('rhel') } +end -platform_options["quantum_openvswitch_packages"].each do |pkg| - package pkg do - action :install +service 'neutron-plugin-openvswitch-agent' do + service_name platform_options['neutron_openvswitch_agent_service'] + supports status: true, restart: true + action :enable + subscribes :restart, 'template[/etc/neutron/neutron.conf]' + if platform_family?('rhel') + subscribes :restart, 'template[/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini]' end end -service "quantum-server" do - service_name node["openstack"]["network"]["platform"]["quantum_server_service"] - supports :status => true, :restart => true - action :nothing -end - -service "quantum-openvswitch-switch" do - service_name platform_options["quantum_openvswitch_service"] - supports :status => true, :restart => true, :start => true - action :start -end - -if platform?(%w(fedora redhat centos)) - template "/etc/init.d/openvswitch" do - source "plugins/openvswitch/openvswitch.erb" - owner "root" - group "root" - mode "0755" - notifies :restart, "service[quantum-openvswitch-switch]", :immediately - end -end - -service "quantum-server" do - service_name platform_options["quantum_server_service"] - supports :status => true, :restart => true - ignore_failure true - action :nothing -end - -platform_options["quantum_openvswitch_agent_packages"].each do |pkg| - package pkg do - action :install - options platform_options["package_overrides"] - end -end - -service "quantum-plugin-openvswitch-agent" do - service_name platform_options["quantum_openvswitch_agent_service"] - supports :status => true, :restart => true - action :nothing -end - execute "chkconfig openvswitch on" do - only_if { platform?(%w(fedora redhat centos)) } + only_if {platform?(%w(fedora redhat centos))} end -#execute "quantum-node-setup --plugin openvswitch" do - # :pragma-foodcritic: ~FC024 - won't fix this -# only_if { platform?(%w(fedora redhat centos))} -# notifies :run, "execute[delete_auto_qpid]", :immediately -#end - -if not ["nicira", "plumgrid", "bigswitch"].include?(main_plugin) - int_bridge = node["openstack"]["network"]["openvswitch"]["integration_bridge"] - execute "create internal network bridge" do +unless ['nicira', 'plumgrid', 'bigswitch'].include?(main_plugin) + # create internal network bridge + int_bridge = node['openstack']['network']['openvswitch']['integration_bridge'] + execute 'create internal network bridge' do ignore_failure true command "ovs-vsctl add-br #{int_bridge}" action :run - not_if "ovs-vsctl show | grep 'Bridge #{int_bridge}'" - notifies :restart, "service[quantum-plugin-openvswitch-agent]", :delayed + not_if "ovs-vsctl br-exists #{int_bridge}" + notifies :restart, 'service[neutron-plugin-openvswitch-agent]', :delayed end -end + + # create tenant network bridge + case node['openstack']['network']['openvswitch']['tenant_network_type'] + when 'gre' + bridge = node['openstack']['network']['openvswitch']['tunnel_bridge'] + cmd = "ovs-vsctl --may-exist add-br #{bridge}" -if not ["nicira", "plumgrid", "bigswitch"].include?(main_plugin) - if node["openstack"]["network"]["openvswitch"]["tenant_network_type"] == 'gre' - tun_bridge = node["openstack"]["network"]["openvswitch"]["tunnel_bridge"] - execute "create tunnel network bridge" do + when 'vlan' + bridge_mappings = node['openstack']['network']['openvswitch']['bridge_mappings'] + bridge = bridge_mappings.split(":").map(&:strip).reject(&:empty?)[1] + interface = bridge.split("-").map(&:strip).reject(&:empty?)[1] + cmd = "ovs-vsctl --may-exist add-br #{bridge};\ + ovs-vsctl --may-exist add-port #{bridge} #{interface}" + + else + # "TODO" + print "The tenant_network_type is not suppported/n" + end + if defined?(cmd) + execute "create tenant network bridge" do ignore_failure true - command "ovs-vsctl add-br #{tun_bridge}" + command cmd action :run - not_if "ovs-vsctl show | grep '#{tun_bridge}'" - notifies :restart, "service[quantum-plugin-openvswitch-agent]", :delayed + not_if "ovs-vsctl brexists #{bridge}" + notifies :restart, "service[neutron-plugin-openvswitch-agent]", :delayed end end - - if node["openstack"]["network"]["openvswitch"]["tenant_network_type"] == 'vlan' - ethernet=node['openstack']['networking']['tenant']['interface'] - bridge_mappings = node["openstack"]["network"]["openvswitch"]["bridge_mappings"] - bridge = bridge_mappings.split(":").map(&:strip).reject(&:empty?)[1] - execute "create tunnel network bridge" do - ignore_failure true - command "ovs-vsctl add-br #{bridge};ovs-vsctl add-port #{bridge} #{ethernet}" - action :run - not_if "ovs-vsctl show | grep '#{bridge}'" - notifies :restart, "service[quantum-plugin-openvswitch-agent]", :delayed +end + +=begin +unless ['nicira', 'plumgrid', 'bigswitch'].include?(main_plugin) + if ['False', 'false'].include?(node['openstack']['network']['openvswitch']['enable_tunneling']) and \ + not node['openstack']['network']['openvswitch']['bridge_mappings'].to_s.empty? + bridge_mappings = node['openstack']['network']['openvswitch']['bridge_mappings'].split(',') + for bridge_mapping in bridge_mappings + bridge = bridge_mapping.split(':').last + bridge_iface = bridge.split('-').last + execute 'create data network bridge' do + command "ovs-vsctl add-br #{bridge} -- add-port #{bridge} #{bridge_iface}" + action :run + not_if "ovs-vsctl br-exists #{bridge}" + only_if "ip link show #{bridge_iface}" + notifies :restart, 'service[neutron-plugin-openvswitch-agent]', :delayed + end end - end + end end - - -execute "force-start-openvswitch-agent" do - command "service #{platform_options['quantum_openvswitch_agent_service']} start" - action :run -end - +=end if node['openstack']['network']['disable_offload'] - - package "ethtool" do - action :install - options platform_options["package_overrides"] + package 'ethtool' do + action :upgrade + options platform_options['package_overrides'] end - service "disable-eth-offload" do - supports :restart => false, :start => true, :stop => false, :reload => false - priority({ 2 => [ :start, 19 ]}) + service 'disable-eth-offload' do + supports restart: false, start: true, stop: false, reload: false + priority( + 2 => [:start, 19] + ) action :nothing end # a priority of 19 ensures we start before openvswitch # at least on ubuntu and debian - cookbook_file "disable-eth-offload-script" do - path "/etc/init.d/disable-eth-offload" - source "disable-eth-offload.sh" - owner "root" - group "root" - mode "0755" - notifies :enable, "service[disable-eth-offload]" - notifies :start, "service[disable-eth-offload]" + cookbook_file 'disable-eth-offload-script' do + path '/etc/init.d/disable-eth-offload' + source 'disable-eth-offload.sh' + owner 'root' + group 'root' + mode '0755' + notifies :enable, 'service[disable-eth-offload]' + notifies :start, 'service[disable-eth-offload]' end end + +# From http://git.openvswitch.org/cgi-bin/gitweb.cgi?p=openvswitch;a=blob_plain;f=utilities/ovs-dpctl-top.in;h=f43fdeb7ab52e3ef642a22579036249ec3a4bc22;hb=14b4c575c28421d1181b509dbeae6e4849c7da69 +cookbook_file 'ovs-dpctl-top' do + path '/usr/bin/ovs-dpctl-top' + source 'ovs-dpctl-top' + owner 'root' + group 'root' + mode 0755 +end diff --git a/chef/cookbooks/openstack-network/recipes/plumgrid.rb b/chef/cookbooks/openstack-network/recipes/plumgrid.rb index bd715ae..39471e2 100644 --- a/chef/cookbooks/openstack-network/recipes/plumgrid.rb +++ b/chef/cookbooks/openstack-network/recipes/plumgrid.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: plumgrid @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/ryu.rb b/chef/cookbooks/openstack-network/recipes/ryu.rb index 0be3e54..8701002 100644 --- a/chef/cookbooks/openstack-network/recipes/ryu.rb +++ b/chef/cookbooks/openstack-network/recipes/ryu.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: ryu @@ -17,4 +18,6 @@ # limitations under the License. # -include_recipe "openstack-network::common" +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +include_recipe 'openstack-network::common' diff --git a/chef/cookbooks/openstack-network/recipes/server.rb b/chef/cookbooks/openstack-network/recipes/server.rb index 427818e..c0038ec 100644 --- a/chef/cookbooks/openstack-network/recipes/server.rb +++ b/chef/cookbooks/openstack-network/recipes/server.rb @@ -1,3 +1,4 @@ +# Encoding: utf-8 # # Cookbook Name:: openstack-network # Recipe:: server @@ -18,63 +19,62 @@ # limitations under the License. # +['quantum', 'neutron'].include?(node['openstack']['compute']['network']['service_type']) || return + +# Make Openstack object available in Chef::Recipe class ::Chef::Recipe include ::Openstack end -include_recipe "openstack-network::common" +include_recipe 'openstack-network::common' -platform_options = node["openstack"]["network"]["platform"] -driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase -main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] -core_plugin = node["openstack"]["network"]["core_plugin"] +platform_options = node['openstack']['network']['platform'] +core_plugin = node['openstack']['network']['core_plugin'] -platform_options = node["openstack"]["network"]["platform"] - -platform_options["quantum_server_packages"].each do |pkg| +platform_options['neutron_server_packages'].each do |pkg| package pkg do - options platform_options["package_overrides"] - action :install + options platform_options['package_overrides'] + action :upgrade end end -platform_options["quantum_openvswitch_agent_packages"].each do|pkg| - package pkg do - options platform_options["package_overrides"] - action :install - end +# Migrate network database +# If the database has never migrated, make the current version of alembic_version to Icehouse, +# else migrate the database to latest version. +# The node['openstack']['network']['plugin_config_file'] attribute is set in the common.rb recipe + +=begin +bash 'migrate network database' do + plugin_config_file = node['openstack']['network']['plugin_config_file'] + db_stamp = node['openstack']['network']['db_stamp'] + migrate_command = "neutron-db-manage --config-file /etc/neutron/neutron.conf --config-file #{plugin_config_file}" + code <<-EOF +current_version_line=`#{migrate_command} current 2>&1 | tail -n 1` +# determine if the $current_version_line ends with ": None" +if [[ $current_version_line == *:\\ None ]]; then + #{migrate_command} stamp #{db_stamp} +else + #{migrate_command} upgrade head +fi +EOF +end +=end + +service 'neutron-server' do + service_name platform_options['neutron_server_service'] + supports status: true, restart: true + action :enable end -if node['platform_family'] == "rhel" - directory "/var/cache/quantum/api" do - owner node['openstack']['services']['network']['name'] - group node['openstack']['services']['network']['name'] - recursive true - end - - template "/etc/init.d/quantum-server" do - source "quantum-server.start.erb" - owner "root" - group "root" - mode 00755 - end -end - -service "quantum-server" do - service_name platform_options["quantum_server_service"] - supports :status => true, :restart => true - action [ :enable, :restart ] -end - -cookbook_file "quantum-ha-tool" do - source "quantum-ha-tool.py" - path node["openstack"]["network"]["quantum_ha_cmd"] - owner "root" - group "root" +cookbook_file 'neutron-ha-tool' do + source 'neutron-ha-tool.py' + path node['openstack']['network']['neutron_ha_cmd'] + owner 'root' + group 'root' mode 00755 end -if node["openstack"]["network"]["quantum_ha_cmd_cron"] +if node['openstack']['network']['neutron_ha_cmd_cron'] # ensure period checks are offset between multiple l3 agent nodes # and assumes splay will remain constant (i.e. based on hostname) # Generate a uniformly distributed unique number to sleep. @@ -82,27 +82,27 @@ if node["openstack"]["network"]["quantum_ha_cmd_cron"] splay = node['chef_client']['splay'].to_i || 3000 sleep_time = checksum.to_s.hex % splay - cron "quantum-ha-healthcheck" do - minute node["openstack"]["network"]["cron_l3_healthcheck"] - command "sleep #{sleep_time} ; . /root/openrc && #{node["openstack"]["network"]["quantum_ha_cmd"]} --l3-agent-migrate > /dev/null 2>&1" + cron 'neutron-ha-healthcheck' do + minute node['openstack']['network']['cron_l3_healthcheck'] + command "sleep #{sleep_time} ; . /root/openrc && #{node["openstack"]["network"]["neutron_ha_cmd"]} --l3-agent-migrate > /dev/null 2>&1" end - cron "quantum-ha-replicate-dhcp" do - minute node["openstack"]["network"]["cron_replicate_dhcp"] - command "sleep #{sleep_time} ; . /root/openrc && #{node["openstack"]["network"]["quantum_ha_cmd"]} --replicate-dhcp > /dev/null 2>&1" + cron 'neutron-ha-replicate-dhcp' do + minute node['openstack']['network']['cron_replicate_dhcp'] + command "sleep #{sleep_time} ; . /root/openrc && #{node["openstack"]["network"]["neutron_ha_cmd"]} --replicate-dhcp > /dev/null 2>&1" end end # the default SUSE initfile uses this sysconfig file to determine the -# quantum plugin to use -template "/etc/sysconfig/quantum" do - only_if { platform? "suse" } - source "quantum.sysconfig.erb" - owner "root" - group "root" +# neutron plugin to use +template '/etc/sysconfig/neutron' do + only_if { platform_family? 'suse' } + source 'neutron.sysconfig.erb' + owner 'root' + group 'root' mode 00644 variables( - :plugin_conf => node["openstack"]["network"]["plugin_conf_map"][driver_name] + plugin_conf: node['openstack']['network']['plugin_conf_map'][core_plugin.split('.').last.downcase] ) - notifies :restart, "service[quantum-server]" + notifies :restart, 'service[neutron-server]' end diff --git a/chef/cookbooks/openstack-network/spec/balancer-redhat_spec.rb b/chef/cookbooks/openstack-network/spec/balancer-redhat_spec.rb new file mode 100644 index 0000000..0527cf5 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/balancer-redhat_spec.rb @@ -0,0 +1,26 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::balancer' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + ['haproxy', 'openstack-neutron'].each do |pack| + it "upgrades #{pack} package" do + expect(chef_run).to upgrade_package(pack) + end + end + + it 'enables agent service' do + expect(chef_run).to enable_service('neutron-lb-agent') + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/balancer-suse_spec.rb b/chef/cookbooks/openstack-network/spec/balancer-suse_spec.rb new file mode 100644 index 0000000..cf7f9ea --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/balancer-suse_spec.rb @@ -0,0 +1,26 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::balancer' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + ['openstack-neutron-lbaas-agent'].each do |pack| + it "upgrades #{pack} package" do + expect(chef_run).to upgrade_package(pack) + end + end + + it 'enables agent service' do + expect(chef_run).to enable_service('neutron-lb-agent') + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/balancer_spec.rb b/chef/cookbooks/openstack-network/spec/balancer_spec.rb index 750e732..f753692 100644 --- a/chef/cookbooks/openstack-network/spec/balancer_spec.rb +++ b/chef/cookbooks/openstack-network/spec/balancer_spec.rb @@ -1,30 +1,67 @@ +# Encoding: utf-8 require_relative 'spec_helper' describe 'openstack-network::balancer' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' - describe "ubuntu" do - - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-network::balancer" + runner.converge(described_recipe) end - ['haproxy', 'quantum-lbaas-agent'].each do |pack| - it "installs #{pack} package" do - expect(@chef_run).to install_package pack + include_context 'neutron-stubs' + + it 'subscribes the agent service to its relevant config files' do + expect(chef_run.service('neutron-lb-agent')).to subscribe_to('template[/etc/neutron/neutron.conf]').delayed + end + + it 'does not upgrade neutron-lbaas-agent when nova networking.' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package('neutron-lbaas-agent') + end + + ['haproxy', 'neutron-lbaas-agent'].each do |pack| + it "upgrades #{pack} package" do + expect(chef_run).to upgrade_package(pack) end end - it 'creates directory /etc/quantum/plugins/services/agent_loadbalancer' do - expect(@chef_run).to create_directory '/etc/quantum/plugins/services/agent_loadbalancer' + it 'enables agent service' do + expect(chef_run).to enable_service('neutron-lb-agent') end - it 'balancer config' do - configf = "/etc/quantum/plugins/services/agent_loadbalancer/lbaas_agent.ini" - expect(@chef_run).to create_file configf - expect(@chef_run).to create_file_with_content configf, /periodic_interval = 10/ - expect(@chef_run).to create_file_with_content configf, /interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver/ + describe 'lbaas_agent.ini' do + let(:file) { chef_run.template('/etc/neutron/lbaas_agent.ini') } + + it 'creates lbaas_agent.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0640 + ) + end + + it 'has default settings' do + expect(chef_run).to render_file(file.name).with_content(/periodic_interval = 10/) + expect(chef_run).to render_file(file.name).with_content( + /interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver/) + expect(chef_run).to render_file(file.name).with_content( + /device_driver = neutron.services.loadbalancer.drivers.haproxy.namespace_driver.HaproxyNSDriver/) + end + + it 'has configurable device_driver setting' do + node.set['openstack']['network']['lbaas']['device_driver'] = 'SomeRandomDriver' + + expect(chef_run).to render_file(file.name).with_content( + /device_driver = SomeRandomDriver/) + end + + it 'notifies the lb agent service' do + expect(file).to notify('service[neutron-lb-agent]').to(:restart).delayed + end end end diff --git a/chef/cookbooks/openstack-network/spec/build_openvswitch_source_spec.rb b/chef/cookbooks/openstack-network/spec/build_openvswitch_source_spec.rb new file mode 100644 index 0000000..fce3e5f --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/build_openvswitch_source_spec.rb @@ -0,0 +1,61 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::build_openvswitch_source' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + + runner.converge('openstack-network::openvswitch') + runner.converge(described_recipe) + end + let(:ovs_switch) { chef_run.dpkg_package('openvswitch-switch') } + let(:ovs_dkms) { chef_run.dpkg_package('openvswitch-datapath-dkms') } + let(:ovs_pki) { chef_run.dpkg_package('openvswitch-pki') } + let(:ovs_common) { chef_run.dpkg_package('openvswitch-common') } + + include_context 'neutron-stubs' + + it 'does not install openvswitch build dependencies when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + %w(build-essential pkg-config fakeroot libssl-dev openssl debhelper autoconf).each do |pkg| + expect(chef_run).to_not install_package(pkg) + end + end + + # since our mocked version of ubuntu is precise, our compile + # utilities should be installed to build OVS from source + it 'installs openvswitch build dependencies' do + %w(build-essential pkg-config fakeroot libssl-dev openssl debhelper autoconf dkms python-all python-qt4 python-zopeinterface python-twisted-conch).each do |pkg| + expect(chef_run).to install_package(pkg) + end + end + + it 'installs openvswitch switch dpkg' do + ovs_switch.source.should include 'openvswitch-switch_1.10.2-1_amd64.deb' + ovs_switch.action.should eq [:nothing] + expect(chef_run).to_not install_dpkg_package(ovs_switch.name) + end + + it 'installs openvswitch datapath dkms dpkg' do + ovs_dkms.source.should include 'openvswitch-datapath-dkms_1.10.2-1_all.deb' + ovs_dkms.action.should eq [:nothing] + expect(chef_run).to_not install_dpkg_package(ovs_dkms.name) + end + + it 'installs openvswitch pki dpkg' do + ovs_pki.source.should include 'openvswitch-pki_1.10.2-1_all.deb' + ovs_pki.action.should eq [:nothing] + expect(chef_run).to_not install_dpkg_package(ovs_pki.name) + end + + it 'installs openvswitch common dpkg' do + ovs_common.source.should include 'openvswitch-common_1.10.2-1_amd64.deb' + ovs_common.action.should eq [:nothing] + expect(chef_run).to_not install_dpkg_package(ovs_common.name) + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/client-redhat_spec.rb b/chef/cookbooks/openstack-network/spec/client-redhat_spec.rb new file mode 100644 index 0000000..628222d --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/client-redhat_spec.rb @@ -0,0 +1,16 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-network::client' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades python neutron client package' do + expect(chef_run).to upgrade_package('python-neutronclient') + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/client_spec.rb b/chef/cookbooks/openstack-network/spec/client_spec.rb new file mode 100644 index 0000000..d690271 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/client_spec.rb @@ -0,0 +1,17 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-network::client' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'upgrades python neutron client package' do + expect(chef_run).to upgrade_package('python-neutronclient') + expect(chef_run).to upgrade_package('python-pyparsing') + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/common-redhat_spec.rb b/chef/cookbooks/openstack-network/spec/common-redhat_spec.rb new file mode 100644 index 0000000..44b123d --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/common-redhat_spec.rb @@ -0,0 +1,28 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::common' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package('MySQL-python') + end + + it 'upgrades db2 python packages if explicitly told' do + node.set['openstack']['db']['network']['service_type'] = 'db2' + + ['python-ibm-db', 'python-ibm-db-sa'].each do |pkg| + expect(chef_run).to upgrade_package(pkg) + end + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/common_spec.rb b/chef/cookbooks/openstack-network/spec/common_spec.rb index 3190d97..9fe33b2 100644 --- a/chef/cookbooks/openstack-network/spec/common_spec.rb +++ b/chef/cookbooks/openstack-network/spec/common_spec.rb @@ -1,19 +1,87 @@ +# Encoding: utf-8 require_relative 'spec_helper' -describe "openstack-network::common" do - describe "ubuntu" do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-network::common" +describe 'openstack-network::common' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + + runner.converge(described_recipe) end - it "upgrades python quantumclient" do - expect(@chef_run).to upgrade_package "python-quantumclient" + include_context 'neutron-stubs' + + it 'does not upgrade python-neutronclient when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package('python-neutronclient') end - it "upgrades python pyparsing" do - expect(@chef_run).to upgrade_package "python-pyparsing" + it 'upgrades python neutronclient package' do + expect(chef_run).to upgrade_package('python-neutronclient') + end + + it 'upgrades python pyparsing package' do + expect(chef_run).to upgrade_package('python-pyparsing') + end + + it 'upgrades mysql python package' do + expect(chef_run).to upgrade_package('python-mysqldb') + end + + describe 'neutron.conf' do + let(:file) { chef_run.template('/etc/neutron/neutron.conf') } + + it 'has proper owner' do + expect(file.owner).to eq('neutron') + expect(file.group).to eq('neutron') + end + + it 'has proper modes' do + expect(sprintf('%o', file.mode)).to eq '644' + end + + it 'has default core plugin' do + expect(chef_run).to render_file(file.name).with_content( + /^core_plugin = neutron.plugins.ml2.plugin.Ml2Plugin/) + end + + it 'has bind_host set' do + node.set['openstack']['endpoints']['network-api-bind']['host'] = '1.1.1.1' + expect(chef_run).to render_file(file.name).with_content('bind_host = 1.1.1.1') + end + + it 'has bind_port set' do + node.set['openstack']['endpoints']['network-api-bind']['port'] = '9999' + expect(chef_run).to render_file(file.name).with_content('bind_port = 9999') + end + + it 'templates misc_neutron array correctly' do + node.set['openstack']['network']['misc_neutron'] = ['MISC1=OPTION1', 'MISC2=OPTION2'] + expect(chef_run).to render_file(file.name).with_content( + /^MISC1=OPTION1$/) + expect(chef_run).to render_file(file.name).with_content( + /^MISC2=OPTION2$/) + end + + # TODO: flush out rest of template attributes + end + describe 'policy file' do + it 'does not manage policy file unless specified' do + expect(chef_run).not_to create_remote_file('/etc/neutron/policy.json') + end + describe 'policy file specified' do + before { node.set['openstack']['network']['policyfile_url'] = 'http://server/mypolicy.json' } + let(:remote_policy) { chef_run.remote_file('/etc/neutron/policy.json') } + it 'manages policy file when remote file is specified' do + expect(chef_run).to create_remote_file('/etc/neutron/policy.json').with( + user: 'neutron', + group: 'neutron', + mode: 00644) + end + end end end end diff --git a/chef/cookbooks/openstack-network/spec/dhcp_agent-opensuse_spec.rb b/chef/cookbooks/openstack-network/spec/dhcp_agent-opensuse_spec.rb deleted file mode 100644 index fb43e38..0000000 --- a/chef/cookbooks/openstack-network/spec/dhcp_agent-opensuse_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require_relative 'spec_helper' - -describe 'openstack-network::dhcp_agent' do - - describe "opensuse" do - - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS - @chef_run.converge "openstack-network::dhcp_agent" - end - - it "installs quamtum dhcp package" do - expect(@chef_run).to install_package "openstack-quantum-dhcp-agent" - end - - it "installs plugin packages" do - expect(@chef_run).not_to install_package(/openvswitch/) - expect(@chef_run).not_to install_package(/plugin/) - end - - it "starts the dhcp agent on boot" do - expect(@chef_run).to( - set_service_to_start_on_boot "openstack-quantum-dhcp-agent") - end - - it "/etc/quantum/dhcp_agent.ini has the proper owner" do - expect(@chef_run.template "/etc/quantum/dhcp_agent.ini").to( - be_owned_by "openstack-quantum", "openstack-quantum") - end - - it "/etc/quantum/dnsmasq.conf has the proper owner" do - expect(@chef_run.template "/etc/quantum/dnsmasq.conf").to( - be_owned_by "openstack-quantum", "openstack-quantum") - end - end -end diff --git a/chef/cookbooks/openstack-network/spec/dhcp_agent-suse_spec.rb b/chef/cookbooks/openstack-network/spec/dhcp_agent-suse_spec.rb new file mode 100644 index 0000000..1932acd --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/dhcp_agent-suse_spec.rb @@ -0,0 +1,59 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::dhcp_agent' do + + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + it 'does not install openstack-neutron-dhcp-agent when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package('openstack-neutron-dhcp-agent') + end + + it 'upgrades neutron dhcp package' do + expect(chef_run).to upgrade_package('openstack-neutron-dhcp-agent') + end + + it 'upgrades plugin packages' do + expect(chef_run).not_to upgrade_package(/openvswitch/) + expect(chef_run).not_to upgrade_package(/plugin/) + end + + it 'starts the dhcp agent on boot' do + expect(chef_run).to enable_service('openstack-neutron-dhcp-agent') + end + + describe '/etc/neutron/dhcp_agent.ini' do + let(:file) { chef_run.template('/etc/neutron/dhcp_agent.ini') } + + it 'creates dhcp_agent.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'openstack-neutron', + group: 'openstack-neutron', + mode: 0644 + ) + end + end + + describe '/etc/neutron/dnsmasq.conf' do + let(:file) { chef_run.template('/etc/neutron/dnsmasq.conf') } + + it 'creates dnsmasq.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'openstack-neutron', + group: 'openstack-neutron', + mode: 0644 + ) + end + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/dhcp_agent_spec.rb b/chef/cookbooks/openstack-network/spec/dhcp_agent_spec.rb index 61ed837..49e9c58 100644 --- a/chef/cookbooks/openstack-network/spec/dhcp_agent_spec.rb +++ b/chef/cookbooks/openstack-network/spec/dhcp_agent_spec.rb @@ -1,90 +1,132 @@ +# Encoding: utf-8 require_relative 'spec_helper' describe 'openstack-network::dhcp_agent' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + runner.converge(described_recipe) + end - describe "ubuntu" do + include_context 'neutron-stubs' - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-network::dhcp_agent" + it 'does not include recipe openstack-network::comon when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not include_recipe('openstack-network::common') + end + + it 'subscribes the agent service to neutron.conf' do + expect(chef_run.service('neutron-dhcp-agent')).to subscribe_to('template[/etc/neutron/neutron.conf]').delayed end # since our mocked version of ubuntu is precise, our compile # utilities should be installed to build dnsmasq - it "installs dnsmasq build dependencies" do - [ "build-essential", "pkg-config", "libidn11-dev", "libdbus-1-dev", "libnetfilter-conntrack-dev", "gettext" ].each do |pkg| - expect(@chef_run).to install_package pkg + it 'upgrades dnsmasq build dependencies' do + %w(build-essential pkg-config libidn11-dev libdbus-1-dev libnetfilter-conntrack-dev gettext).each do |pkg| + expect(chef_run).to upgrade_package pkg end end - it "installs quamtum dhcp package" do - expect(@chef_run).to install_package "quantum-dhcp-agent" - end + it 'skips dnsmasq build when asked to' do + node.set['openstack']['network']['dhcp']['dnsmasq_compile'] = false - it "installs plugin packages" do - expect(@chef_run).to install_package "quantum-plugin-openvswitch" - end - - it "starts the dhcp agent on boot" do - expect(@chef_run).to set_service_to_start_on_boot "quantum-dhcp-agent" - end - - describe "/etc/quantum/plugins" do - before do - @file = @chef_run.directory "/etc/quantum/plugins" - end - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" - end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "700" + %w(build-essential pkg-config libidn11-dev libdbus-1-dev libnetfilter-conntrack-dev gettext).each do |pkg| + expect(chef_run).to_not upgrade_package pkg end end - describe "/etc/quantum/dhcp_agent.ini" do - before do - @file = @chef_run.template "/etc/quantum/dhcp_agent.ini" - end - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" - end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - it "uses ovs driver" do - expect(@chef_run).to create_file_with_content @file.name, - "interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver" - end - it "uses namespaces" do - expect(@chef_run).to create_file_with_content @file.name, - "use_namespaces = True" - end - it "checks dhcp domain" do - expect(@chef_run).to create_file_with_content @file.name, - /^dhcp_domain = openstacklocal$/ + it 'upgrades neutron dhcp package' do + expect(chef_run).to upgrade_package 'neutron-dhcp-agent' + end + + it 'upgrades plugin package' do + expect(chef_run).to upgrade_package 'neutron-plugin-ml2' + end + + it 'starts the dhcp agent on boot' do + expect(chef_run).to enable_service 'neutron-dhcp-agent' + end + + describe '/etc/neutron/plugins' do + let(:dir) { chef_run.directory('/etc/neutron/plugins') } + + it 'creates /etc/neutron/plugins' do + expect(chef_run).to create_directory(dir.name).with( + user: 'neutron', + group: 'neutron', + mode: 0700 + ) end end - describe "/etc/quantum/dnsmasq.conf" do - before do - @file = @chef_run.template "/etc/quantum/dnsmasq.conf" + describe '/etc/neutron/dhcp_agent.ini' do + let(:file) { chef_run.template('/etc/neutron/dhcp_agent.ini') } + + it 'creates dhcp_agent.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" + + it 'uses ovs driver' do + expect(chef_run).to render_file(file.name).with_content( + 'interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver') end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + + it 'uses namespaces' do + expect(chef_run).to render_file(file.name).with_content('use_namespaces = True') end - it "overrides dhcp options" do - expect(@chef_run).to create_file_with_content @file.name, - "dhcp-option=26,1454" + + it 'disables ovs_use_veth' do + expect(chef_run).to render_file(file.name).with_content('ovs_use_veth = False') end - it "checks upstream resolvers" do - expect(@chef_run).to create_file_with_content @file.name, - /^server=209.244.0.3$/ - expect(@chef_run).to create_file_with_content @file.name, - /^server=8.8.8.8$/ + + it 'checks dhcp domain' do + expect(chef_run).to render_file(file.name).with_content(/^dhcp_domain = openstacklocal$/) + end + + it 'has default dnsmasq_lease_max setting' do + expect(chef_run).to render_file(file.name).with_content(/^dnsmasq_lease_max = 16777216$/) + end + + it 'has configurable dnsmasq_lease_max setting' do + node.set['openstack']['network']['dhcp']['dnsmasq_lease_max'] = 16777215 + + expect(chef_run).to render_file(file.name).with_content(/^dnsmasq_lease_max = 16777215$/) + end + + it 'notifies the dhcp agent service' do + expect(file).to notify('service[neutron-dhcp-agent]').to(:restart).immediately + end + end + + describe '/etc/neutron/dnsmasq.conf' do + let(:file) { chef_run.template('/etc/neutron/dnsmasq.conf') } + + it 'creates dnsmasq.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) + end + + it 'overrides dhcp options' do + expect(chef_run).to render_file(file.name).with_content('dhcp-option=26,1454') + end + + it 'checks upstream resolvers' do + expect(chef_run).to render_file(file.name).with_content(/^server=209.244.0.3$/) + expect(chef_run).to render_file(file.name).with_content(/^server=8.8.8.8$/) + end + + it 'notifies the dhcp agent service' do + expect(file).to notify('service[neutron-dhcp-agent]').to(:restart).delayed end end end diff --git a/chef/cookbooks/openstack-network/spec/hyperv_spec.rb b/chef/cookbooks/openstack-network/spec/hyperv_spec.rb new file mode 100644 index 0000000..d7c94ae --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/hyperv_spec.rb @@ -0,0 +1,27 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::hyperv' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['network']['core_plugin'] = 'neutron.plugins.hyperv.hyperv_neutron_plugin.HyperVNeutronPlugin' + node.set['openstack']['network']['core_plugin_map'] = { 'hypervneutronplugin' => 'hyperv' } + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + describe 'hyperv_neutron_plugin.ini.erb' do + let(:file) { chef_run.template('/etc/neutron/plugins/hyperv/hyperv_neutron_plugin.ini.erb') } + + it 'uses default firewall_driver' do + expect(chef_run).to render_file(file.name).with_content( + /^firewall_driver = neutron.plugins.hyperv.agent.security_groups_driver.HyperVSecurityGroupsDriver/) + end + + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-network/spec/identity_registration_spec.rb index 920a370..7f2ed53 100644 --- a/chef/cookbooks/openstack-network/spec/identity_registration_spec.rb +++ b/chef/cookbooks/openstack-network/spec/identity_registration_spec.rb @@ -1,89 +1,94 @@ -require_relative "spec_helper" +# Encoding: utf-8 +require_relative 'spec_helper' -describe "openstack-network::identity_registration" do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-network::identity_registration" - end +describe 'openstack-network::identity_registration' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' - it "registers network service" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Network API Service" - ).to_hash + runner.converge(described_recipe) + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_type => "network", - :service_description => "OpenStack Network Service", - :action => [:create_service] - ) - end + include_context 'neutron-stubs' - it "registers network endpoint" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Network Endpoint" - ).to_hash + it 'does not do network service registrations when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :service_type => "network", - :endpoint_region => "RegionOne", - :endpoint_adminurl => "http://127.0.0.1:9696", - :endpoint_internalurl => "http://127.0.0.1:9696", - :endpoint_publicurl => "http://127.0.0.1:9696", - :action => [:create_endpoint] - ) - end + expect(chef_run).not_to create_service_openstack_identity_register( + 'Register Network API Service' + ) + end - it "registers service tenant" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register Service Tenant" - ).to_hash + it 'registers network service' do + expect(chef_run).to create_service_openstack_identity_register( + 'Register Network API Service' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'network', + service_description: 'OpenStack Network Service' + ) + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :tenant_description => "Service Tenant", - :action => [:create_tenant] - ) - end + context 'registers network endpoint' do + it 'with default values' do + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Network Endpoint' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'network', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:9696', + endpoint_internalurl: 'http://127.0.0.1:9696', + endpoint_publicurl: 'http://127.0.0.1:9696' + ) + end - it "registers service user" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Register quantum User" - ).to_hash + it 'with custom region override' do + node.set['openstack']['network']['region'] = 'netRegion' - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :user_name => "quantum", - :user_pass => "quantum-pass", - :action => [:create_user] - ) - end + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Network Endpoint' + ).with(endpoint_region: 'netRegion') + end + end - it "grants admin role to service user for service tenant" do - resource = @chef_run.find_resource( - "openstack-identity_register", - "Grant 'admin' Role to quantum User for service Tenant" - ).to_hash + it 'registers service tenant' do + expect(chef_run).to create_tenant_openstack_identity_register( + 'Register Service Tenant' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + tenant_description: 'Service Tenant' + ) + end - expect(resource).to include( - :auth_uri => "http://127.0.0.1:35357/v2.0", - :bootstrap_token => "bootstrap-token", - :tenant_name => "service", - :role_name => "admin", - :user_name => "quantum", - :action => [:grant_role] - ) + it 'registers service user' do + expect(chef_run).to create_user_openstack_identity_register( + 'Register neutron User' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'neutron', + user_pass: 'neutron-pass' + ) + end + + it 'grants admin role to service user for service tenant' do + expect(chef_run).to grant_role_openstack_identity_register( + "Grant 'admin' Role to neutron User for service Tenant" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + role_name: 'admin', + user_name: 'neutron' + ) + end end end diff --git a/chef/cookbooks/openstack-network/spec/l3_agent_spec.rb b/chef/cookbooks/openstack-network/spec/l3_agent_spec.rb index 7fbc18f..87d7f4c 100644 --- a/chef/cookbooks/openstack-network/spec/l3_agent_spec.rb +++ b/chef/cookbooks/openstack-network/spec/l3_agent_spec.rb @@ -1,51 +1,102 @@ +# Encoding: utf-8 require_relative 'spec_helper' describe 'openstack-network::l3_agent' do - describe "ubuntu" do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['network']['l3']['external_network_bridge_interface'] = 'eth1' - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-network::l3_agent" + runner.converge(described_recipe) end - it "installs quamtum l3 package" do - expect(@chef_run).to install_package "quantum-l3-agent" + include_context 'neutron-stubs' + + it 'starts the l3 agent on boot' do + expect(chef_run).to enable_service('neutron-l3-agent') end - describe "l3_agent.ini" do + it 'subscribes the l3 agent service to neutron.conf' do + expect(chef_run.service('neutron-l3-agent')).to subscribe_to('template[/etc/neutron/neutron.conf]').delayed + end - before do - @file = @chef_run.template "/etc/quantum/l3_agent.ini" + it 'does not install neutron l3 package when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package('neutron-l3-agent') + end + + it 'upgrades neutron l3 package' do + expect(chef_run).to upgrade_package('neutron-l3-agent') + end + + describe 'l3_agent.ini' do + let(:file) { chef_run.template('/etc/neutron/l3_agent.ini') } + + it 'creates l3_agent.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" + it 'it has ovs driver' do + expect(chef_run).to render_file(file.name).with_content( + 'interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver') end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'sets fuzzy delay to default' do + expect(chef_run).to render_file(file.name).with_content( + 'periodic_fuzzy_delay = 5') end - it "it has ovs driver" do - expect(@chef_run).to create_file_with_content @file.name, - "interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver" + it 'it does not set a nil router_id' do + expect(chef_run).not_to render_file(file.name).with_content(/^router_id =/) end - it "sets fuzzy delay to default" do - expect(@chef_run).to create_file_with_content @file.name, - "periodic_fuzzy_delay = 5" + it 'it does not set a nil router_id' do + expect(chef_run).not_to render_file(file.name).with_content( + /^gateway_external_network_id =/) end - it "it does not set a nil router_id" do - expect(@chef_run).not_to create_file_with_content @file.name, - /^router_id =/ + it 'notifies the l3 agent service' do + expect(file).to notify('service[neutron-l3-agent]').to(:restart).immediately + end + end + + describe 'create ovs bridges' do + let(:cmd) { 'ovs-vsctl add-br br-ex && ovs-vsctl add-port br-ex eth1' } + + it "doesn't add the external bridge if it already exists" do + stub_command(/ovs-vsctl br-exists/).and_return(true) + stub_command(/ip link show eth1/).and_return(true) + + expect(chef_run).not_to run_execute(cmd) end - it "it does not set a nil router_id" do - expect(@chef_run).not_to create_file_with_content @file.name, - /^gateway_external_network_id =/ + it "doesn't add the external bridge if the physical interface doesn't exist" do + stub_command(/ovs-vsctl br-exists/).and_return(true) + stub_command(/ip link show eth1/).and_return(false) + + expect(chef_run).not_to run_execute(cmd) + end + + it 'adds the external bridge if it does not yet exist' do + stub_command(/ovs-vsctl br-exists/).and_return(false) + stub_command(/ip link show eth1/).and_return(true) + + expect(chef_run).to run_execute(cmd) + end + + it 'adds the external bridge if the physical interface exists' do + stub_command(/ovs-vsctl br-exists/).and_return(false) + stub_command(/ip link show eth1/).and_return(true) + + expect(chef_run).to run_execute(cmd) end end end diff --git a/chef/cookbooks/openstack-network/spec/linuxbridge-opensuse_spec.rb b/chef/cookbooks/openstack-network/spec/linuxbridge-opensuse_spec.rb deleted file mode 100644 index f8bf4db..0000000 --- a/chef/cookbooks/openstack-network/spec/linuxbridge-opensuse_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require_relative 'spec_helper' - -describe 'openstack-network::linuxbridge' do - - describe "opensuse" do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| - n.set["openstack"]["network"]["interface_driver"] = "quantum.agent.linux.interface.BridgeInterfaceDriver" - end - @chef_run.converge "openstack-network::linuxbridge" - end - - it "installs linuxbridge agent" do - expect(@chef_run).to install_package "openstack-quantum-linuxbridge-agent" - end - - it "sets the linuxbridge service to start on boot" do - expect(@chef_run).to set_service_to_start_on_boot "openstack-quantum-linuxbridge-agent" - end - - end -end diff --git a/chef/cookbooks/openstack-network/spec/linuxbridge-redhat_spec.rb b/chef/cookbooks/openstack-network/spec/linuxbridge-redhat_spec.rb index 5fde89f..e81dc14 100644 --- a/chef/cookbooks/openstack-network/spec/linuxbridge-redhat_spec.rb +++ b/chef/cookbooks/openstack-network/spec/linuxbridge-redhat_spec.rb @@ -1,23 +1,57 @@ +# Encoding: utf-8 require_relative 'spec_helper' describe 'openstack-network::linuxbridge' do - describe "redhat" do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| - n.set["openstack"]["network"]["interface_driver"] = "quantum.agent.linux.interface.BridgeInterfaceDriver" + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['network']['interface_driver'] = 'neutron.agent.linux.interface.BridgeInterfaceDriver' + node.set['openstack']['network']['core_plugin'] = 'neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2' + + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + it 'does not install linuxbridge agent package when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package('openstack-neutron-linuxbridge') + end + + it 'upgrades linuxbridge agent' do + expect(chef_run).to upgrade_package('openstack-neutron-linuxbridge') + end + + it 'creates the /etc/neutron/plugins/linuxbridge agent directory' do + expect(chef_run).to create_directory('/etc/neutron/plugins/linuxbridge').with( + owner: 'neutron', + group: 'neutron', + mode: 0700 + ) + end + + it 'sets the linuxbridge service to start on boot' do + expect(chef_run).to enable_service('neutron-linuxbridge-agent') + end + + describe '/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini' do + let(:file) { chef_run.template('/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini') } + + it 'creates ovs_neutron_plugin.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) end - @chef_run.converge "openstack-network::linuxbridge" - end - it "installs linuxbridge agent" do - expect(@chef_run).to install_package "openstack-quantum-linuxbridge" + it 'notifies to create symbolic link' do + expect(file).to notify('link[/etc/neutron/plugin.ini]').to(:create).immediately + end end - - it "sets the linuxbridge service to start on boot" do - expect(@chef_run).to set_service_to_start_on_boot "quantum-linuxbridge-agent" - end - end end diff --git a/chef/cookbooks/openstack-network/spec/linuxbridge-suse_spec.rb b/chef/cookbooks/openstack-network/spec/linuxbridge-suse_spec.rb new file mode 100644 index 0000000..dc9980a --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/linuxbridge-suse_spec.rb @@ -0,0 +1,32 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::linuxbridge' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['network']['interface_driver'] = 'neutron.agent.linux.interface.BridgeInterfaceDriver' + node.set['openstack']['network']['core_plugin'] = 'neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2' + + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + it 'does not install linuxbridge agent package when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package('openstack-neutron-linuxbridge-agent') + end + + it 'upgrades linuxbridge agent' do + expect(chef_run).to upgrade_package('openstack-neutron-linuxbridge-agent') + end + + it 'sets the linuxbridge service to start on boot' do + expect(chef_run).to enable_service('openstack-neutron-linuxbridge-agent') + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/linuxbridge_spec.rb b/chef/cookbooks/openstack-network/spec/linuxbridge_spec.rb index d2efb05..bc723ea 100644 --- a/chef/cookbooks/openstack-network/spec/linuxbridge_spec.rb +++ b/chef/cookbooks/openstack-network/spec/linuxbridge_spec.rb @@ -1,41 +1,80 @@ +# Encoding: utf-8 require_relative 'spec_helper' describe 'openstack-network::linuxbridge' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['network']['interface_driver'] = 'neutron.agent.linux.interface.BridgeInterfaceDriver' + node.set['openstack']['network']['core_plugin'] = 'neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2' - describe "ubuntu" do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["network"]["interface_driver"] = "quantum.agent.linux.interface.BridgeInterfaceDriver" - end - @chef_run.converge "openstack-network::linuxbridge" + runner.converge(described_recipe) end - it "installs linuxbridge agent" do - expect(@chef_run).to install_package "quantum-plugin-linuxbridge-agent" + include_context 'neutron-stubs' + + it 'does not install linuxbridge agent package when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package('neutron-plugin-linuxbridge-agent') end - it "sets the linuxbridge service to start on boot" do - expect(@chef_run).to set_service_to_start_on_boot "quantum-plugin-linuxbridge-agent" + it 'upgrades linuxbridge agent' do + expect(chef_run).to upgrade_package('neutron-plugin-linuxbridge-agent') end - describe "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini" do - before do - @file = @chef_run.template( - "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini") + it 'creates the /etc/neutron/plugins/linuxbridge agent directory' do + expect(chef_run).to create_directory('/etc/neutron/plugins/linuxbridge').with( + owner: 'neutron', + group: 'neutron', + mode: 0700 + ) + end + + it 'sets the linuxbridge service to start on boot' do + expect(chef_run).to enable_service('neutron-plugin-linuxbridge-agent') + end + + it 'subscribes the linuxbridge agent service to neutron.conf' do + expect(chef_run.service('neutron-plugin-linuxbridge-agent')).to subscribe_to('template[/etc/neutron/neutron.conf]').delayed + end + + describe '/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini' do + let(:file) { chef_run.template('/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini') } + + it 'creates linuxbridge_conf.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" + it 'sets local_ip when bind_interface is not set' do + expect(chef_run).to render_file(file.name).with_content( + 'local_ip = 127.0.0.1') end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "has a correct sql_connection value" do - expect(@chef_run).to create_file_with_content( - @file.name, "mysql://quantum:quantum-pass@127.0.0.1:3306/quantum") + [ + /^tenant_network_type = local$/, + /^network_vlan_ranges = $/, + /^physical_interface_mappings = $/, + /^enable_vxlan = false$/, + /^ttl = $/, + /^tos = $/, + /^vxlan_group = 224.0.0.1$/, + /^local_ip = 127.0.0.1$/, + /^l2_population = false$/, + /^polling_interval = 2$/, + /^rpc_support_old_agents = false$/, + /^firewall_driver = neutron.agent.firewall.NoopFirewallDriver$/, + /^enable_security_group = True$/ + ].each do |content| + it "has #{content.source[1...-1]} line" do + expect(chef_run).to render_file(file.name).with_content(content) + end end end end diff --git a/chef/cookbooks/openstack-network/spec/metadata_agent_spec.rb b/chef/cookbooks/openstack-network/spec/metadata_agent_spec.rb index 2c08975..2853086 100644 --- a/chef/cookbooks/openstack-network/spec/metadata_agent_spec.rb +++ b/chef/cookbooks/openstack-network/spec/metadata_agent_spec.rb @@ -1,64 +1,86 @@ +# Encoding: utf-8 require_relative 'spec_helper' describe 'openstack-network::metadata_agent' do - - describe "ubuntu" do - - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-network::metadata_agent" + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + runner.converge(described_recipe) end - it "installs quamtum metadata agent" do - expect(@chef_run).to install_package "quantum-metadata-agent" + include_context 'neutron-stubs' + + it 'does not install neutron metadata agent when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package 'neutron-metadata-agent' end - describe "metadata_agent.ini" do + it 'upgrades neutron metadata agent' do + expect(chef_run).to upgrade_package 'neutron-metadata-agent' + end - before do - @file = @chef_run.template "/etc/quantum/metadata_agent.ini" + it 'subscribes the metadata agent service to neutron.conf' do + expect(chef_run.service('neutron-metadata-agent')).to subscribe_to('template[/etc/neutron/neutron.conf]').delayed + end + + describe 'metadata_agent.ini' do + let(:file) { chef_run.template('/etc/neutron/metadata_agent.ini') } + + it 'creates metadata_agent.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" + it 'sets auth url correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'auth_url = http://127.0.0.1:5000/v2.0') end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'sets auth region correctly' do + node.set['openstack']['network']['region'] = 'testRegion' + + expect(chef_run).to render_file(file.name).with_content( + 'auth_region = testRegion') end - it "sets auth url correctly" do - expect(@chef_run).to create_file_with_content @file.name, - "auth_url = http://127.0.0.1:5000/v2.0" + it 'sets admin tenant name' do + expect(chef_run).to render_file(file.name).with_content( + 'admin_tenant_name = service') end - it "sets auth region correctly" do - expect(@chef_run).to create_file_with_content @file.name, - "auth_region = RegionOne" + + it 'sets admin user' do + expect(chef_run).to render_file(file.name).with_content( + 'admin_user = neutron') end - it "sets admin tenant name" do - expect(@chef_run).to create_file_with_content @file.name, - "admin_tenant_name = service" + + it 'sets admin password' do + expect(chef_run).to render_file(file.name).with_content( + 'admin_password = neutron-pass') end - it "sets admin user" do - expect(@chef_run).to create_file_with_content @file.name, - "admin_user = quantum" + + it 'sets nova metadata ip correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'nova_metadata_ip = 127.0.0.1') end - it "sets admin password" do - expect(@chef_run).to create_file_with_content @file.name, - "admin_password = quantum-pass" + + it 'sets nova metadata ip correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'nova_metadata_port = 8775') end - it "sets nova metadata ip correctly" do - expect(@chef_run).to create_file_with_content @file.name, - "nova_metadata_ip = 127.0.0.1" + + it 'sets neutron secret correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'metadata_proxy_shared_secret = metadata-secret') end - it "sets nova metadata ip correctly" do - expect(@chef_run).to create_file_with_content @file.name, - "nova_metadata_port = 8775" - end - it "sets quantum secret correctly" do - expect(@chef_run).to create_file_with_content @file.name, - "metadata_proxy_shared_secret = metadata-secret" + + it 'notifies the metadata agent service' do + expect(file).to notify('service[neutron-metadata-agent]').to(:restart).immediately end end end diff --git a/chef/cookbooks/openstack-network/spec/openvswitch-opensuse_spec.rb b/chef/cookbooks/openstack-network/spec/openvswitch-opensuse_spec.rb deleted file mode 100644 index 8bef084..0000000 --- a/chef/cookbooks/openstack-network/spec/openvswitch-opensuse_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require_relative "spec_helper" - -describe 'openstack-network::server' do - describe "opensuse" do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| - n.set["chef_client"]["splay"] = 300 - end - @node = @chef_run.node - @chef_run.converge "openstack-network::openvswitch" - end - - it "installs the openvswitch package" do - expect(@chef_run).to install_package "openvswitch-switch" - end - - it "installs the openvswitch-agent package" do - expect(@chef_run).to install_package "openstack-quantum-openvswitch-agent" - end - - it "starts the openvswitch-switch service" do - expect(@chef_run).to set_service_to_start_on_boot "openvswitch-switch" - end - end -end diff --git a/chef/cookbooks/openstack-network/spec/openvswitch-redhat_spec.rb b/chef/cookbooks/openstack-network/spec/openvswitch-redhat_spec.rb new file mode 100644 index 0000000..71ded21 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/openvswitch-redhat_spec.rb @@ -0,0 +1,38 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::openvswitch' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['network']['core_plugin'] = 'neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2' + + runner.converge(described_recipe) + end + let(:file) { chef_run.template('/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini') } + + include_context 'neutron-stubs' + + it 'notifies to create symbolic link' do + expect(file).to notify('link[/etc/neutron/plugin.ini]').to(:create).immediately + end + + it 'creates the /etc/neutron/plugins/openvswitch agent directory' do + expect(chef_run).to create_directory('/etc/neutron/plugins/openvswitch').with( + owner: 'neutron', + group: 'neutron', + mode: 0700 + ) + end + + it 'creates ovs_neutron_plugin.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/openvswitch-suse_spec.rb b/chef/cookbooks/openstack-network/spec/openvswitch-suse_spec.rb new file mode 100644 index 0000000..67c7c08 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/openvswitch-suse_spec.rb @@ -0,0 +1,34 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::openvswitch' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + it 'does not install openvswitch package when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package 'openvswitch-switch' + end + + it 'upgrades the openvswitch package' do + expect(chef_run).to upgrade_package 'openvswitch-switch' + end + + it 'upgrades the openvswitch-agent package' do + expect(chef_run).to upgrade_package 'openstack-neutron-openvswitch-agent' + end + + it 'starts the openvswitch-switch service' do + expect(chef_run).to enable_service 'openvswitch-switch' + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/openvswitch_spec.rb b/chef/cookbooks/openstack-network/spec/openvswitch_spec.rb index 2cde58f..dd7111a 100644 --- a/chef/cookbooks/openstack-network/spec/openvswitch_spec.rb +++ b/chef/cookbooks/openstack-network/spec/openvswitch_spec.rb @@ -1,82 +1,216 @@ +# Encoding: utf-8 require_relative 'spec_helper' describe 'openstack-network::openvswitch' do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| - n.automatic_attrs["kernel"]["release"] = "1.2.3" - n.set["openstack"]["network"]["local_ip_interface"] = "eth0" - end - @chef_run.converge "openstack-network::openvswitch" - end + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:kmod_command) { '/usr/share/openvswitch/scripts/ovs-ctl force-reload-kmod' } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + node.set['openstack']['endpoints']['network-openvswitch']['bind_interface'] = 'eth0' + node.set['openstack']['network']['openvswitch']['integration_bridge'] = 'br-int' + node.set['openstack']['network']['core_plugin'] = 'neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2' + node.automatic_attrs['kernel']['release'] = '1.2.3' - it "installs openvswitch switch" do - expect(@chef_run).to install_package "openvswitch-switch" - end - it "installs openvswitch datapath dkms" do - expect(@chef_run).to install_package "openvswitch-datapath-dkms" - end - it "installs linux bridge utils" do - expect(@chef_run).to install_package "bridge-utils" - end - it "installs linux linux headers" do - expect(@chef_run).to install_package "linux-headers-1.2.3" - end - it "sets the openvswitch service to start on boot" do - expect(@chef_run).to set_service_to_start_on_boot 'openvswitch-switch' - end - it "installs openvswitch agent" do - expect(@chef_run).to install_package "quantum-plugin-openvswitch-agent" - end - it "sets the openvswitch service to start on boot" do - expect(@chef_run).to set_service_to_start_on_boot "quantum-plugin-openvswitch-agent" - end - - describe "ovs_quantum_plugin.ini" do - - before do - @file = @chef_run.template "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini" + runner.converge(described_recipe) end - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" + include_context 'neutron-stubs' + + it 'does not install openvswitch switch when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package 'openvswitch-switch' end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'upgrades openvswitch switch' do + expect(chef_run).to upgrade_package 'openvswitch-switch' end - it "uses default network_vlan_range" do - expect(@chef_run).not_to create_file_with_content @file.name, - /^network_vlan_ranges =/ + it 'upgrades openvswitch datapath dkms' do + expect(chef_run).to upgrade_package 'openvswitch-datapath-dkms' end - it "uses default tunnel_id_ranges" do - expect(@chef_run).not_to create_file_with_content @file.name, - /^tunnel_id_ranges =/ + + it 'upgrades linux bridge utils' do + expect(chef_run).to upgrade_package 'bridge-utils' end - it "uses default integration_bridge" do - expect(@chef_run).to create_file_with_content @file.name, - "integration_bridge = br-int" + + it 'upgrades linux linux headers' do + expect(chef_run).to upgrade_package 'linux-headers-1.2.3' end - it "uses default tunnel bridge" do - expect(@chef_run).to create_file_with_content @file.name, - "tunnel_bridge = br-tun" + + it 'sets the openvswitch service to start on boot' do + expect(chef_run).to enable_service 'openvswitch-switch' end - it "uses default int_peer_patch_port" do - expect(@chef_run).not_to create_file_with_content @file.name, - /^int_peer_patch_port =/ + + it 'start the openvswitch service' do + expect(chef_run).to start_service 'openvswitch-switch' end - it "uses default tun_peer_patch_port" do - expect(@chef_run).not_to create_file_with_content @file.name, - /^tun_peer_patch_port =/ + + it 'subscribes the openvswitch agent service to neutron.conf' do + expect(chef_run.service('neutron-plugin-openvswitch-agent')).to subscribe_to('template[/etc/neutron/neutron.conf]').delayed end - it "it has firewall driver" do - expect(@chef_run).to create_file_with_content @file.name, - "firewall_driver = quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver" + + it 'upgrades openvswitch agent' do + expect(chef_run).to upgrade_package 'neutron-plugin-openvswitch-agent' end - it "it uses local_ip from eth0 when local_ip_interface is set" do - expect(@chef_run).to create_file_with_content @file.name, - "local_ip = 10.0.0.3" + + it 'creates the /etc/neutron/plugins/openvswitch agent directory' do + expect(chef_run).to create_directory('/etc/neutron/plugins/openvswitch').with( + owner: 'neutron', + group: 'neutron', + mode: 0700 + ) + end + + it 'sets the openvswitch service to start on boot' do + expect(chef_run).to enable_service 'neutron-plugin-openvswitch-agent' + end + + it 'allows overriding the service names' do + node.set['openstack']['network']['platform']['neutron_openvswitch_service'] = 'my-ovs-server' + node.set['openstack']['network']['platform']['neutron_openvswitch_agent_service'] = 'my-ovs-agent' + + %w{my-ovs-server my-ovs-agent}.each do |service| + expect(chef_run).to enable_service service + end + end + + it 'allows overriding package options' do + node.set['openstack']['network']['platform']['package_overrides'] = '--my-override1 --my-override2' + + %w{openvswitch-switch openvswitch-datapath-dkms neutron-plugin-openvswitch neutron-plugin-openvswitch-agent}.each do |pkg| + expect(chef_run).to upgrade_package(pkg).with(options: '--my-override1 --my-override2') + end + end + + it 'allows overriding package names' do + node.set['openstack']['network']['platform']['neutron_openvswitch_packages'] = ['my-openvswitch', 'my-other-openvswitch'] + node.set['openstack']['network']['platform']['neutron_openvswitch_agent_packages'] = ['my-openvswitch-agent', 'my-other-openvswitch-agent'] + + %w{my-openvswitch my-other-openvswitch my-openvswitch-agent my-other-openvswitch-agent}.each do |pkg| + expect(chef_run).to upgrade_package(pkg) + end + end + + it 'creates execute resource when openvswitch-datasource-dkms package is being installed' do + resource = chef_run.find_resource('execute', kmod_command).to_hash + + expect(resource).to include( + action: [:nothing], + command: kmod_command + ) + end + + it 'does not create execute resource when openvswitch-datasource-dkms package is not being installed' do + node.set['openstack']['network']['platform']['neutron_openvswitch_packages'] = ['my-openvswitch', 'my-other-openvswitch'] + chef_run.converge 'openstack-network::openvswitch' + + resource = chef_run.find_resource('execute', kmod_command) + expect(resource).to eq(nil) + end + + it 'notifies :run to the force-reload-kmod execute resource when openvswitch-datapath-dkms is installed' do + expect(chef_run.package('openvswitch-datapath-dkms')).to notify("execute[#{kmod_command}]").to(:run).immediately + end + + describe 'ovs-dpctl-top' do + let(:file) { chef_run.cookbook_file('/usr/bin/ovs-dpctl-top') } + + it 'creates /usr/bin/ovs-dpctl-top' do + expect(chef_run).to create_cookbook_file(file.name).with( + user: 'root', + group: 'root', + mode: 0755 + ) + end + + it 'has the proper interpreter line' do + expect(chef_run).to render_file(file.name).with_content( + %r{^#!\/usr\/bin\/env python} + ) + end + end + + describe 'ovs_neutron_plugin.ini' do + let(:file) { chef_run.template('/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini') } + + it 'creates ovs_neutron_plugin.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) + end + + [ + /^network_vlan_ranges =$/, + /^tunnel_id_ranges =$/, + /^int_peer_patch_port =$/, + /^tun_peer_patch_port =$/, + /^bridge_mappings =$/ + ].each do |content| + it "does not have a #{content.source[1...-1]} line" do + expect(chef_run).not_to render_file(file.name).with_content(content) + end + end + + [ + /^tenant_network_type = local$/, + /^enable_tunneling = False$/, + /^tunnel_type = $/, + /^integration_bridge = br-int$/, + /^local_ip = 10.0.0.2$/, + /^integration_bridge = br-int$/, + /^tunnel_bridge = br-tun$/, + /^polling_interval = 2$/, + /^firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver$/, + /^enable_security_group = True$/ + ].each do |content| + it "has a #{content.source[1...-1]} line" do + expect(chef_run).to render_file(file.name).with_content(content) + end + end + end + + describe 'create ovs data network bridge' do + let(:cmd) { 'ovs-vsctl add-br br-eth1 -- add-port br-eth1 eth1' } + + it 'does not add data network bridge if it already exists' do + node.set['openstack']['network']['openvswitch']['bridge_mapping_interface'] = 'br-eth1:eth1' + stub_command(/ovs-vsctl br-exists br-eth1/).and_return(true) + stub_command(/ip link show eth1/).and_return(true) + expect(chef_run).not_to run_execute(cmd) + end + + it 'does not add data network bridge if the physical interface does not exist' do + node.set['openstack']['network']['openvswitch']['bridge_mapping_interface'] = 'br-eth1:eth1' + stub_command(/ovs-vsctl br-exists br-eth1/).and_return(false) + stub_command(/ip link show eth1/).and_return(false) + expect(chef_run).not_to run_execute(cmd) + end + + it 'adds data network bridge if it does not yet exist and physical interface exists' do + node.set['openstack']['network']['openvswitch']['bridge_mapping_interface'] = 'br-eth1:eth1' + stub_command(/ovs-vsctl br-exists br-eth1/).and_return(false) + stub_command(/ip link show eth1/).and_return(true) + expect(chef_run).to run_execute(cmd) + end + + it 'does not add data network bridge if nil specified for bridge mapping' do + node.set['openstack']['network']['openvswitch']['bridge_mapping_interface'] = nil + stub_command(/ovs-vsctl br-exists br-eth1/).and_return(false) + stub_command(/ip link show eth1/).and_return(true) + expect(chef_run).not_to run_execute(cmd) + end + + it 'does not add data network bridge if emtpy string specified for bridge mapping' do + node.set['openstack']['network']['openvswitch']['bridge_mapping_interface'] = '' + stub_command(/ovs-vsctl br-exists br-eth1/).and_return(false) + stub_command(/ip link show eth1/).and_return(true) + expect(chef_run).not_to run_execute(cmd) + end end end end diff --git a/chef/cookbooks/openstack-network/spec/server-opensuse_spec.rb b/chef/cookbooks/openstack-network/spec/server-opensuse_spec.rb deleted file mode 100644 index b08d564..0000000 --- a/chef/cookbooks/openstack-network/spec/server-opensuse_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -require_relative "spec_helper" - -describe 'openstack-network::server' do - describe "opensuse" do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| - n.set["chef_client"]["splay"] = 300 - end - @node = @chef_run.node - @chef_run.converge "openstack-network::server" - end - - it "installs openstack-quantum packages" do - expect(@chef_run).to install_package "openstack-quantum" - end - - it "enables openstack-quantum service" do - expect(@chef_run).to enable_service "openstack-quantum" - end - - it "does not install openvswitch package" do - opts = ::OPENSUSE_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts do |n| - n.set["chef_client"]["splay"] = 300 - end - chef_run.converge "openstack-network::server" - - expect(chef_run).not_to install_package "openstack-quantum-openvswitch" - end - - describe "/etc/sysconfig/quantum" do - before do - @file = @chef_run.template("/etc/sysconfig/quantum") - end - - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "has the correct plugin config location - ovs by default" do - expect(@chef_run).to create_file_with_content( - @file.name, "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini") - end - - it "uses linuxbridge when configured to use it" do - chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| - n.set["openstack"]["network"]["interface_driver"] = "quantum.agent.linux.interface.BridgeInterfaceDriver" - end - chef_run.converge "openstack-network::server" - - expect(chef_run).to create_file_with_content( - "/etc/sysconfig/quantum", - "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini" - ) - end - end - end -end diff --git a/chef/cookbooks/openstack-network/spec/server-redhat_spec.rb b/chef/cookbooks/openstack-network/spec/server-redhat_spec.rb index 31cced4..675d41c 100644 --- a/chef/cookbooks/openstack-network/spec/server-redhat_spec.rb +++ b/chef/cookbooks/openstack-network/spec/server-redhat_spec.rb @@ -1,28 +1,34 @@ -require_relative "spec_helper" +# Encoding: utf-8 +require_relative 'spec_helper' describe 'openstack-network::server' do - describe "redhat" do - before do - quantum_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS - @node = @chef_run.node - @chef_run.converge "openstack-network::server" + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + runner.converge(described_recipe) end - it "installs openstack-quantum packages" do - expect(@chef_run).to install_package "openstack-quantum" + include_context 'neutron-stubs' + + it 'does not install openstack-neutron when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package 'openstack-neutron' end - it "enables openstack-quantum server service" do - expect(@chef_run).to enable_service "quantum-server" + it 'upgrades openstack-neutron packages' do + expect(chef_run).to upgrade_package 'openstack-neutron' end - it "does not install openvswitch package" do - opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.converge "openstack-network::server" - expect(chef_run).not_to install_package "openvswitch" - expect(chef_run).not_to enable_service "openstack-quantum-openvswitch-agent" + it 'enables openstack-neutron server service' do + expect(chef_run).to enable_service 'neutron-server' + end + + it 'does not upgrade openvswitch package' do + expect(chef_run).not_to upgrade_package 'openvswitch' + expect(chef_run).not_to enable_service 'neutron-openvswitch-agent' end end end diff --git a/chef/cookbooks/openstack-network/spec/server-suse_spec.rb b/chef/cookbooks/openstack-network/spec/server-suse_spec.rb new file mode 100644 index 0000000..ade3605 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/server-suse_spec.rb @@ -0,0 +1,61 @@ +# Encoding: utf-8 +require_relative 'spec_helper' + +describe 'openstack-network::server' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' + runner.converge(described_recipe) + end + + include_context 'neutron-stubs' + + it 'does not install openstack-neutron when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + + expect(chef_run).to_not upgrade_package 'openstack-neutron' + end + + it 'upgrades openstack-neutron packages' do + expect(chef_run).to upgrade_package 'openstack-neutron' + end + + it 'enables openstack-neutron service' do + expect(chef_run).to enable_service 'openstack-neutron' + end + + it 'does not upgrade openvswitch package' do + expect(chef_run).not_to upgrade_package 'openstack-neutron-openvswitch' + end + + describe '/etc/sysconfig/neutron' do + let(:file) { chef_run.template('/etc/sysconfig/neutron') } + + it 'creates /etc/sysconfig/neutron' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 0644 + ) + end + + it 'has the correct plugin config location - ml2 by default' do + expect(chef_run).to render_file(file.name).with_content( + '/etc/neutron/plugins/ml2/ml2_conf.ini') + end + + it 'uses linuxbridge when configured to use it' do + chef_run = ::ChefSpec::Runner.new ::SUSE_OPTS do |n| + n.set['openstack']['network']['core_plugin'] = 'neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2' + n.set['openstack']['compute']['network']['service_type'] = 'neutron' + end + chef_run.converge 'openstack-network::server' + + expect(chef_run).to render_file(file.name).with_content( + '/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini') + end + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/server_spec.rb b/chef/cookbooks/openstack-network/spec/server_spec.rb index 659068a..26a4605 100644 --- a/chef/cookbooks/openstack-network/spec/server_spec.rb +++ b/chef/cookbooks/openstack-network/spec/server_spec.rb @@ -1,208 +1,651 @@ +# Encoding: utf-8 require_relative 'spec_helper' describe 'openstack-network::server' do - before { quantum_stubs } - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| - n.set["openstack"]["mq"] = { - "host" => "127.0.0.1" - } - n.set["chef_client"]["splay"] = 300 - end - @chef_run.converge "openstack-network::server" - end + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do - describe "package and services" do + node.set['openstack']['compute']['network']['service_type'] = 'neutron' - it "installs quantum packages" do - expect(@chef_run).to install_package "quantum-server" + runner.converge(described_recipe) end - it "starts server service" do - expect(@chef_run).to enable_service "quantum-server" + include_context 'neutron-stubs' + + it 'uses release db stamp' do + expect(chef_run).to run_bash('migrate network database').with_code(/stamp icehouse/) end - it "does not install openvswitch package or the agent" do - expect(@chef_run).not_to install_package "openvswitch" - expect(@chef_run).not_to install_package "quantum-plugin-openvswitch-agent" - expect(@chef_run).not_to enable_service "quantum-plugin-openvswitch-agent" + it 'does not install neutron-server when nova networking' do + node.override['openstack']['compute']['network']['service_type'] = 'nova' + expect(chef_run).to_not upgrade_package 'neutron-server' end - end + describe 'package and services' do + it 'upgrades neutron-server packages' do + expect(chef_run).to upgrade_package 'neutron-server' + end - describe "api-paste.ini" do + it 'allows overriding package names' do + cust_pkgs = ['my-neutron', 'my-other-neutron'] + node.set['openstack']['network']['platform']['neutron_server_packages'] = cust_pkgs - before do - @file = @chef_run.template "/etc/quantum/api-paste.ini" - end - - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "has quantum pass" do - expect(@chef_run).to create_file_with_content @file.name, - "admin_password = quantum-pass" - end - - end - - it "should create quantum-ha-tool.py script" do - expect(@chef_run).to create_cookbook_file "/usr/local/bin/quantum-ha-tool.py" - end - - describe "quantum.conf" do - - before do - @file = @chef_run.template "/etc/quantum/quantum.conf" - end - - it "has proper owner" do - expect(@file).to be_owned_by "quantum", "quantum" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" - end - - it "it sets root_helper" do - expect(@chef_run).to create_file_with_content @file.name, - 'root_helper = "sudo quantum-rootwrap /etc/quantum/rootwrap.conf"' - end - - it "binds to appropriate api ip" do - expect(@chef_run).to create_file_with_content @file.name, - "bind_host = 127.0.0.1" - end - - it "binds to appropriate api port" do - expect(@chef_run).to create_file_with_content @file.name, - "bind_port = 9696" - end - - it "has appropriate auth host for agents" do - expect(@chef_run).to create_file_with_content @file.name, - "auth_host = 127.0.0.1" - end - - it "has appropriate auth port for agents" do - expect(@chef_run).to create_file_with_content @file.name, - "auth_port = 5000" - end - - it "has appropriate admin password for agents" do - expect(@chef_run).to create_file_with_content @file.name, - "admin_password = quantum-pass" - end - - it "has rabbit_host" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_host=127.0.0.1" - end - - it "does not have rabbit_hosts" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_hosts=" - end - - it "does not have rabbit_ha_queues" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_ha_queues=" - end - - it "has rabbit_port" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_port=5672" - end - - it "has rabbit_userid" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_userid=guest" - end - - it "has rabbit_password" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_password=rabbit-pass" - end - - it "has rabbit_virtual_host" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_virtual_host=/" - end - - it "it does not allow overlapping ips by default" do - expect(@chef_run).to create_file_with_content @file.name, - "allow_overlapping_ips = False" - end - - it "it has correct default scheduler classes" do - expect(@chef_run).to create_file_with_content @file.name, - "network_scheduler_driver = quantum.scheduler.dhcp_agent_scheduler.ChanceScheduler" - expect(@chef_run).to create_file_with_content @file.name, - "router_scheduler_driver = quantum.scheduler.l3_agent_scheduler.ChanceScheduler" - end - - describe "quantum.conf with rabbit ha" do - - before do - @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| - n.set["openstack"]["network"]["rabbit"]["ha"] = true - n.set["chef_client"]["splay"] = 300 + cust_pkgs.each do |pkg| + expect(chef_run).to upgrade_package(pkg) end - @chef_run.converge "openstack-network::server" end - it "has rabbit_hosts" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_hosts=1.1.1.1:5672,2.2.2.2:5672" + it 'starts neutron-server service' do + expect(chef_run).to enable_service 'neutron-server' end - it "has rabbit_ha_queues" do - expect(@chef_run).to create_file_with_content @file.name, - "rabbit_ha_queues=True" + it 'allows overriding service names' do + node.set['openstack']['network']['platform']['neutron_server_service'] = 'my-neutron-server' + + expect(chef_run).to enable_service 'my-neutron-server' end - it "does not have rabbit_host" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_host=127.0.0.1" + it 'allows overriding package options' do + cust_opts = "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef' --force-yes" + node.set['openstack']['network']['platform']['package_overrides'] = cust_opts + + expect(chef_run).to upgrade_package('neutron-server').with(options: cust_opts) end - it "does not have rabbit_port" do - expect(@chef_run).not_to create_file_with_content @file.name, - "rabbit_port=5672" + it 'does not upgrade openvswitch package or the agent' do + expect(chef_run).not_to upgrade_package 'openvswitch' + expect(chef_run).not_to upgrade_package 'neutron-plugin-openvswitch-agent' + expect(chef_run).not_to enable_service 'neutron-plugin-openvswitch-agent' end end - describe "/etc/default/quantum-server" do + describe 'api-paste.ini' do + let(:file) { chef_run.template('/etc/neutron/api-paste.ini') } + + it 'creates api-paste.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0640 + ) + end + end + + describe 'neutron-ha-tool.py' do + let(:file) { chef_run.cookbook_file('/usr/local/bin/neutron-ha-tool.py') } + + it 'should create neutron-ha-tool.py script' do + expect(chef_run).to create_cookbook_file(file.name) + end + end + + describe 'neutron.conf' do + let(:file) { chef_run.template('/etc/neutron/neutron.conf') } + + it 'creates neutron.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) + end + + it 'it sets rpc_thread_pool_size correctly' do + expect(chef_run).to render_file(file.name).with_content( + /^rpc_thread_pool_size = 64$/) + end + + it 'it sets rpc_conn_pool_size correctly' do + expect(chef_run).to render_file(file.name).with_content( + /^rpc_conn_pool_size = 30$/) + end + + it 'it sets rpc_response_timeout correctly' do + expect(chef_run).to render_file(file.name).with_content( + /^rpc_response_timeout = 60$/) + end + + it 'it sets control_exchange correctly' do + expect(chef_run).to render_file(file.name).with_content( + /^control_exchange = neutron$/) + end + + it 'has default amqp_* queue options set' do + [/^amqp_durable_queues=false$/, + /^amqp_auto_delete=false$/].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + + it 'it sets agent_down_time correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'agent_down_time = 75') + end + + it 'writes the quota driver properly' do + node.set['openstack']['network']['quota']['driver'] = 'my.quota.Driver' + + expect(chef_run).to render_file(file.name).with_content( + 'quota_driver = my.quota.Driver') + end + + it 'it sets auth_strategy correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'auth_strategy = keystone') + end + + it 'it sets state_path correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'state_path = /var/lib/neutron') + end + + it 'it sets lock_path correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'lock_path = $state_path/lock') + end + + it 'it sets log_dir correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'log_dir = /var/log/neutron') + end + + it 'it sets agent report interval correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'report_interval = 30') + end + + it 'it does not allow overlapping ips by default' do + expect(chef_run).to render_file(file.name).with_content( + /^allow_overlapping_ips = False$/) + end + + it 'it has correct default scheduler classes' do + expect(chef_run).to render_file(file.name).with_content( + 'network_scheduler_driver = neutron.scheduler.dhcp_agent_scheduler.ChanceScheduler') + expect(chef_run).to render_file(file.name).with_content( + 'router_scheduler_driver = neutron.scheduler.l3_agent_scheduler.ChanceScheduler') + end + + it 'has the overridable default quota values' do + expect(chef_run).to render_file(file.name).with_content( + /^quota_items = network,subnet,port/) + expect(chef_run).to render_file(file.name).with_content( + /^default_quota = -1/) + expect(chef_run).to render_file(file.name).with_content( + /^quota_network = 10/) + expect(chef_run).to render_file(file.name).with_content( + /^quota_subnet = 10/) + expect(chef_run).to render_file(file.name).with_content( + /^quota_port = 50/) + expect(chef_run).to render_file(file.name).with_content( + /^quota_security_group = 10/) + expect(chef_run).to render_file(file.name).with_content( + /^quota_security_group_rule = 100/) + end + + it 'sets rpc_backend correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'rpc_backend=neutron.openstack.common.rpc.impl_kombu') + expect(chef_run).not_to render_file(file.name).with_content( + 'rpc_backend=neutron.openstack.common.rpc.impl_qpid') + end + + it 'it sets root_helper' do + expect(chef_run).to render_file(file.name).with_content( + 'root_helper = "sudo neutron-rootwrap /etc/neutron/rootwrap.conf"') + end + + it 'binds to appropriate api ip' do + expect(chef_run).to render_file(file.name).with_content( + 'bind_host = 127.0.0.1') + end + + it 'binds to appropriate api port' do + expect(chef_run).to render_file(file.name).with_content( + 'bind_port = 9696') + end + + it 'has rabbit_host' do + expect(chef_run).to render_file(file.name).with_content( + 'rabbit_host=127.0.0.1') + end + + it 'does not have rabbit_hosts' do + expect(chef_run).not_to render_file(file.name).with_content( + 'rabbit_hosts=') + end + + it 'does not have rabbit_ha_queues' do + expect(chef_run).not_to render_file(file.name).with_content( + 'rabbit_ha_queues=') + end + + it 'has rabbit_port' do + expect(chef_run).to render_file(file.name).with_content( + 'rabbit_port=5672') + end + + it 'has rabbit_userid' do + expect(chef_run).to render_file(file.name).with_content( + 'rabbit_userid=guest') + end + + it 'has rabbit_password' do + expect(chef_run).to render_file(file.name).with_content( + 'rabbit_password=mq-pass') + end + + it 'has rabbit_virtual_host' do + expect(chef_run).to render_file(file.name).with_content( + 'rabbit_virtual_host=/') + end + + it 'has default dhcp_lease_duration setting' do + expect(chef_run).to render_file(file.name).with_content( + 'dhcp_lease_duration = 86400') + end + + it 'has configurable dhcp_lease_duration setting' do + node.set['openstack']['network']['dhcp_lease_duration'] = 3600 + + expect(chef_run).to render_file(file.name).with_content( + 'dhcp_lease_duration = 3600') + end + + it 'does not set service_plugins when attribute is []' do + expect(chef_run).not_to render_file(file.name).with_content( + /^service_plugins =/) + end + + it 'has default notification_driver setting' do + expect(chef_run).to render_file(file.name).with_content( + 'notification_driver = neutron.openstack.common.notifier.rpc_notifier') + end + + it 'has configurable notification_driver setting' do + driver = 'neutron.openstack.common.notifier.no_op_notifier' + node.set['openstack']['network']['notification_driver'] = driver + + expect(chef_run).to render_file(file.name).with_content( + "notification_driver = #{driver}") + end + + it 'has default notification_topics setting' do + expect(chef_run).to render_file(file.name).with_content( + 'notification_topics = notifications') + end + + it 'has configurable notification_topics setting' do + topics = 'notifications1,notifications2' + node.set['openstack']['mq']['network']['notification_topics'] = topics + + expect(chef_run).to render_file(file.name).with_content( + "notification_topics = #{topics}") + end + + it 'sets service_plugins' do + node.set['openstack']['network']['service_plugins'] = %w{ + neutron.foo + neutron.bar + } + + expect(chef_run).to render_file(file.name).with_content( + 'service_plugins = neutron.foo,neutron.bar') + end + + it 'has neutron pass' do + expect(chef_run).to render_file(file.name).with_content( + 'admin_password = neutron-pass') + end + + it 'has auth_uri' do + expect(chef_run).to render_file(file.name).with_content( + 'auth_uri = http://127.0.0.1:5000/v2.0') + end + + it 'has auth_host' do + expect(chef_run).to render_file(file.name).with_content( + 'auth_host = 127.0.0.1') + end + + it 'has auth_port' do + expect(chef_run).to render_file(file.name).with_content( + 'auth_port = 35357') + end + + it 'has auth_protocol' do + expect(chef_run).to render_file(file.name).with_content( + 'auth_protocol = http') + end + + it 'has signing_dir' do + expect(chef_run).to render_file(file.name).with_content( + 'signing_dir = /var/lib/neutron/keystone-signing') + end + + it 'has correct auth_version' do + expect(chef_run).not_to render_file(file.name).with_content( + 'auth_version = v2.0') + end + + describe 'qpid' do + before do + node.set['openstack']['mq']['network']['service_type'] = 'qpid' + node.set['openstack']['mq']['network']['qpid']['username'] = 'guest' + end + + it 'sets rpc_backend correctly' do + expect(chef_run).to render_file(file.name).with_content( + 'rpc_backend=neutron.openstack.common.rpc.impl_qpid') + expect(chef_run).not_to render_file(file.name).with_content( + 'rpc_backend=neutron.openstack.common.rpc.impl_kombu') + end + + it 'has qpid_hostname' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_hostname=127.0.0.1') + end + + it 'has qpid_port' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_port=5672') + end + + it 'has qpid_username' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_username=guest') + end + + it 'has qpid_password' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_password=mq-pass') + end + + it 'has qpid_sasl_mechanisms' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_sasl_mechanisms=') + end + + it 'has qpid_reconnect' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_reconnect=true') + end + + it 'has qpid_reconnect_timeout' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_reconnect_timeout=0') + end + + it 'has qpid_reconnect_limit' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_reconnect_limit=0') + end + + it 'has qpid_reconnect_interval_min' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_reconnect_interval_min=0') + end + + it 'has qpid_reconnect_interval_max' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_reconnect_interval_max=0') + end + + it 'has qpid_reconnect_interval' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_reconnect_interval=0') + end + + it 'has qpid_heartbeat' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_heartbeat=60') + end + + it 'has qpid_protocol' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_protocol=tcp') + end + + it 'has qpid_tcp_nodelay' do + expect(chef_run).to render_file(file.name).with_content( + 'qpid_tcp_nodelay=true') + end + + it 'has qpid_topology_version set' do + expect(chef_run).to render_file(file.name).with_content( + /^qpid_topology_version=1$/) + end + end + + describe 'neutron.conf with rabbit ha' do + before do + node.set['openstack']['mq']['network']['rabbit']['ha'] = true + node.set['chef_client']['splay'] = 300 + end + + it 'has rabbit_hosts' do + expect(chef_run).to render_file(file.name).with_content( + 'rabbit_hosts=1.1.1.1:5672,2.2.2.2:5672') + end + + it 'has rabbit_ha_queues' do + expect(chef_run).to render_file(file.name).with_content( + 'rabbit_ha_queues=True') + end + + it 'does not have rabbit_host' do + expect(chef_run).not_to render_file(file.name).with_content( + 'rabbit_host=127.0.0.1') + end + + it 'does not have rabbit_port' do + expect(chef_run).not_to render_file(file.name).with_content( + 'rabbit_port=5672') + end + end + + it 'does not create sysconfig template' do + stub_command(/python/).and_return(true) + expect(chef_run).not_to create_file('/etc/sysconfig/neutron') + end + + describe 'database' do + it 'has a correct sql_connection value' do + expect(chef_run).to render_file(file.name).with_content( + 'mysql://neutron:neutron@127.0.0.1:3306/neutron') + end + + it 'sets sqlalchemy attributes' do + expect(chef_run).to render_file(file.name).with_content( + 'slave_connection =') + expect(chef_run).to render_file(file.name).with_content( + 'max_retries = 10') + expect(chef_run).to render_file(file.name).with_content( + 'retry_interval = 10') + expect(chef_run).to render_file(file.name).with_content( + 'min_pool_size = 1') + expect(chef_run).to render_file(file.name).with_content( + 'max_pool_size = 10') + expect(chef_run).to render_file(file.name).with_content( + 'idle_timeout = 3600') + expect(chef_run).to render_file(file.name).with_content( + 'max_overflow = 20') + expect(chef_run).to render_file(file.name).with_content( + 'connection_debug = 0') + expect(chef_run).to render_file(file.name).with_content( + 'connection_trace = false') + expect(chef_run).to render_file(file.name).with_content( + 'pool_timeout = 10') + end + end + + it 'sets service_provider attributes' do + node.set['openstack']['network']['service_provider'] = ['provider1', 'provider2'] + + expect(chef_run).to render_file(file.name).with_content( + 'service_provider = provider1') + expect(chef_run).to render_file(file.name).with_content( + 'service_provider = provider2') + end + + it 'has the overridable default nova interaction values' do + expect(chef_run).to render_file(file.name).with_content( + 'notify_nova_on_port_status_changes = True') + expect(chef_run).to render_file(file.name).with_content( + 'notify_nova_on_port_data_changes = True') + expect(chef_run).to render_file(file.name).with_content( + 'nova_url = http://127.0.0.1:8774/v2') + expect(chef_run).to render_file(file.name).with_content( + 'nova_region_name = RegionOne') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_username = nova') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id =') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_password = nova-pass') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_auth_url = http://127.0.0.1:35357/v2.0') + expect(chef_run).to render_file(file.name).with_content( + 'send_events_interval = 2') + expect(chef_run).to run_ruby_block('query service tenant uuid') + end + + describe 'query service tenant uuid' do + it 'has queried service tenant uuid for nova interactions' do + # run actual ruby_block resource + chef_run.find_resource(:ruby_block, 'query service tenant uuid').old_run_action(:create) + nova_tenant_id = chef_run.node['openstack']['network']['nova']['admin_tenant_id'] + expect(nova_tenant_id).to eq('000-UUID-FROM-CLI') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id = 000-UUID-FROM-CLI') + end + + it 'has status changes for nova interactions disabled without id override' do + chef_run.node.set['openstack']['network']['nova']['notify_nova_on_port_status_changes'] = 'False' + # run actual ruby_block resource + chef_run.find_resource(:ruby_block, 'query service tenant uuid').old_run_action(:create) + nova_tenant_id = chef_run.node['openstack']['network']['nova']['admin_tenant_id'] + expect(nova_tenant_id).to eq('000-UUID-FROM-CLI') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id = 000-UUID-FROM-CLI') + end + + it 'has data changes for nova interactions disabled without id override' do + chef_run.node.set['openstack']['network']['nova']['notify_nova_on_port_data_changes'] = 'False' + # run actual ruby_block resource + chef_run.find_resource(:ruby_block, 'query service tenant uuid').old_run_action(:create) + nova_tenant_id = chef_run.node['openstack']['network']['nova']['admin_tenant_id'] + expect(nova_tenant_id).to eq('000-UUID-FROM-CLI') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id = 000-UUID-FROM-CLI') + end + + it 'has all changes for nova interactions disabled without id override' do + chef_run.node.set['openstack']['network']['nova']['notify_nova_on_port_status_changes'] = 'False' + chef_run.node.set['openstack']['network']['nova']['notify_nova_on_port_data_changes'] = 'False' + # run actual ruby_block resource + chef_run.find_resource(:ruby_block, 'query service tenant uuid').old_run_action(:create) + nova_tenant_id = chef_run.node['openstack']['network']['nova']['admin_tenant_id'] + expect(nova_tenant_id).to eq(nil) + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id =') + end + + it 'has status changes for nova interactions disabled with id override' do + chef_run.node.set['openstack']['network']['nova']['notify_nova_on_port_status_changes'] = 'False' + chef_run.node.set['openstack']['network']['nova']['admin_tenant_id'] = '111-UUID-OVERRIDE' + # run actual ruby_block resource + chef_run.find_resource(:ruby_block, 'query service tenant uuid').old_run_action(:create) + nova_tenant_id = chef_run.node['openstack']['network']['nova']['admin_tenant_id'] + expect(nova_tenant_id).to eq('111-UUID-OVERRIDE') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id = 111-UUID-OVERRIDE') + end + + it 'has data changes for nova interactions disabled with id override' do + chef_run.node.set['openstack']['network']['nova']['notify_nova_on_port_data_changes'] = 'False' + chef_run.node.set['openstack']['network']['nova']['admin_tenant_id'] = '111-UUID-OVERRIDE' + # run actual ruby_block resource + chef_run.find_resource(:ruby_block, 'query service tenant uuid').old_run_action(:create) + nova_tenant_id = chef_run.node['openstack']['network']['nova']['admin_tenant_id'] + expect(nova_tenant_id).to eq('111-UUID-OVERRIDE') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id = 111-UUID-OVERRIDE') + end + + it 'has all changes for nova interactions disabled with id override' do + chef_run.node.set['openstack']['network']['nova']['notify_nova_on_port_status_changes'] = 'False' + chef_run.node.set['openstack']['network']['nova']['notify_nova_on_port_data_changes'] = 'False' + chef_run.node.set['openstack']['network']['nova']['admin_tenant_id'] = '111-UUID-OVERRIDE' + # run actual ruby_block resource + chef_run.find_resource(:ruby_block, 'query service tenant uuid').old_run_action(:create) + nova_tenant_id = chef_run.node['openstack']['network']['nova']['admin_tenant_id'] + expect(nova_tenant_id).to eq('111-UUID-OVERRIDE') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id = 111-UUID-OVERRIDE') + end + + it 'has overriden service tenant uuid for nova interactions' do + chef_run.node.set['openstack']['network']['nova']['admin_tenant_id'] = '111-UUID-OVERRIDE' + # run actual ruby_block resource + chef_run.find_resource(:ruby_block, 'query service tenant uuid').old_run_action(:create) + nova_tenant_id = chef_run.node['openstack']['network']['nova']['admin_tenant_id'] + expect(nova_tenant_id).to eq('111-UUID-OVERRIDE') + expect(chef_run).to render_file(file.name).with_content( + 'nova_admin_tenant_id = 111-UUID-OVERRIDE') + end + end + end + + describe '/etc/default/neutron-server' do + let(:file) { chef_run.template('/etc/default/neutron-server') } + + it 'creates /etc/default/neutron-server' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 0644 + ) + end + + it 'has a correct plugin config path' do + expect(chef_run).to render_file(file.name).with_content( + '/etc/neutron/plugins/ml2/ml2_conf.ini') + end + end + + describe '/etc/neutron/plugins/ml2/ml2_conf.ini' do + let(:file) { chef_run.template('/etc/neutron/plugins/ml2/ml2_conf.ini') } + before do - @file = @chef_run.template( - "/etc/default/quantum-server") + node.set['openstack']['network']['interface_driver'] = 'neutron.agent.linux.interface.Ml2InterfaceDriver' end - it "has proper owner" do - expect(@file).to be_owned_by "root", "root" + it 'creates ml2_conf.ini' do + expect(chef_run).to create_template(file.name).with( + user: 'neutron', + group: 'neutron', + mode: 0644 + ) end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + [ + /^type_drivers = local,flat,vlan,gre,vxlan$/, + /^tenant_network_types = local$/, + /^mechanism_drivers = openvswitch$/, + /^flat_networks = $/, + /^network_vlan_ranges = $/, + /^tunnel_id_ranges = $/, + /^vni_ranges = $/, + /^vxlan_group = $/, + /^enable_security_group = True$/ + ].each do |content| + it "has a #{content.source[1...-1]} line" do + expect(chef_run).to render_file(file.name).with_content(content) + end end - - it "has a correct plugin config path" do - expect(@chef_run).to create_file_with_content( - @file.name, "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini") - end - end - - it "does not install sysconfig template" do - chef_run = ::ChefSpec::ChefRunner.new( - ::UBUNTU_OPTS.merge(:evaluate_guards => true)) - chef_run.stub_command(/python/, true) - chef_run.converge "openstack-network::server" - expect(chef_run).not_to create_file "/etc/sysconfig/quantum" end end end diff --git a/chef/cookbooks/openstack-network/spec/spec_helper.rb b/chef/cookbooks/openstack-network/spec/spec_helper.rb index 2cec4e0..841a71b 100644 --- a/chef/cookbooks/openstack-network/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-network/spec/spec_helper.rb @@ -1,69 +1,93 @@ -require "chefspec" +# Encoding: utf-8 +require 'chefspec' +require 'chefspec/berkshelf' -::LOG_LEVEL = :fatal -::OPENSUSE_OPTS = { - :platform => "opensuse", - :version => "12.3", - :log_level => ::LOG_LEVEL +ChefSpec::Coverage.start! { add_filter 'openstack-network' } + +require 'chef/application' + +LOG_LEVEL = :fatal +SUSE_OPTS = { + platform: 'suse', + version: '11.03', + log_level: LOG_LEVEL } -::REDHAT_OPTS = { - :platform => "redhat", - :version => "6.3", - :log_level => ::LOG_LEVEL +REDHAT_OPTS = { + platform: 'redhat', + version: '6.3', + log_level: LOG_LEVEL } -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } MOCK_NODE_NETWORK_DATA = { - "ipaddress" => '10.0.0.2', - "fqdn" => 'localhost.localdomain', - "hostname" => 'localhost', - "network" => { - "default_interface" => "eth0", - "interfaces" => { - "eth0" => { - "addresses" => { - "fe80::a00:27ff:feca:ab08" => {"scope" => "Link", "prefixlen" => "64", "family" => "inet6"}, - "10.0.0.2" => {"netmask" => "255.255.255.0", "broadcast" => "10.0.0.255", "family" => "inet"}, - "08:00:27:CA:AB:08" => {"family" => "lladdr"} - }, + 'ipaddress' => '10.0.0.2', + 'fqdn' => 'localhost.localdomain', + 'hostname' => 'localhost', + 'network' => { + 'default_interface' => 'eth0', + 'interfaces' => { + 'eth0' => { + 'addresses' => { + 'fe80::a00:27ff:feca:ab08' => { 'scope' => 'Link', 'prefixlen' => '64', 'family' => 'inet6' }, + '10.0.0.2' => { 'netmask' => '255.255.255.0', 'broadcast' => '10.0.0.255', 'family' => 'inet' }, + '08:00:27:CA:AB:08' => { 'family' => 'lladdr' } + } }, - "lo" => { - "addresses" => { - "::1" => {"scope" => "Node", "prefixlen" => "128", "family" => "inet6"}, - "127.0.0.1" => {"netmask" => "255.0.0.0", "family" => "inet"} - }, - }, - }, + 'lo' => { + 'addresses' => { + '::1' => { 'scope' => 'Node', 'prefixlen' => '128', 'family' => 'inet6' }, + '127.0.0.1' => { 'netmask' => '255.0.0.0', 'family' => 'inet' } + } + } + } } } -def quantum_stubs - - ::Chef::Recipe.any_instance.stub(:rabbit_servers). - and_return "1.1.1.1:5672,2.2.2.2:5672" - ::Chef::Recipe.any_instance.stub(:config_by_role). - with("rabbitmq-server", "queue").and_return( - {'host' => 'rabbit-host', 'port' => 'rabbit-port'} - ) - ::Chef::Recipe.any_instance.stub(:config_by_role). - with("glance-api", "glance").and_return [] - ::Chef::Recipe.any_instance.stub(:secret). - with("secrets", "openstack_identity_bootstrap_token"). - and_return "bootstrap-token" - ::Chef::Recipe.any_instance.stub(:db_password).and_return "quantum-pass" - ::Chef::Recipe.any_instance.stub(:secret). - with("secrets", "quantum_metadata_secret"). - and_return "metadata-secret" - ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:service_password).and_return String.new - ::Chef::Recipe.any_instance.stub(:service_password).with("openstack-network"). - and_return "quantum-pass" - ::Chef::Recipe.any_instance.stub(:user_password).with("guest"). - and_return("rabbit-password") +shared_context 'neutron-stubs' do + before do + Chef::Recipe.any_instance.stub(:rabbit_servers) + .and_return('1.1.1.1:5672,2.2.2.2:5672') + Chef::Recipe.any_instance.stub(:config_by_role) + .with('rabbitmq-server', 'queue').and_return( + host: 'rabbit-host', + port: 'rabbit-port' + ) + Chef::Recipe.any_instance.stub(:config_by_role) + .with('glance-api', 'glance').and_return [] + Chef::Recipe.any_instance.stub(:secret) + .with('secrets', 'openstack_identity_bootstrap_token') + .and_return('bootstrap-token') + Chef::Recipe.any_instance.stub(:secret) + .with('secrets', 'neutron_metadata_secret') + .and_return('metadata-secret') + Chef::Recipe.any_instance.stub(:get_password) + .with('db', anything) + .and_return('neutron') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack-network') + .and_return('neutron-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'guest') + .and_return('mq-pass') + Chef::Application.stub(:fatal!) + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack-compute') + .and_return('nova-pass') + Chef::Resource::RubyBlock.any_instance.stub(:openstack_command_env) + .with('admin', 'admin') + .and_return({}) + Chef::Resource::RubyBlock.any_instance.stub(:identity_uuid) + .with('tenant', 'name', 'service', {}) + .and_return('000-UUID-FROM-CLI') + stub_command('dpkg -l | grep openvswitch-switch | grep 1.10.2-1').and_return(true) + stub_command('ovs-vsctl br-exists br-int').and_return(false) + stub_command('ovs-vsctl br-exists br-tun').and_return(false) + stub_command('ip link show eth1').and_return(false) + end end diff --git a/chef/cookbooks/openstack-network/templates/default/api-paste.ini.erb b/chef/cookbooks/openstack-network/templates/default/api-paste.ini.erb index 5899fcc..e6972a1 100644 --- a/chef/cookbooks/openstack-network/templates/default/api-paste.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/api-paste.ini.erb @@ -1,34 +1,33 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[composite:quantum] +[composite:neutron] use = egg:Paste#urlmap -/: quantumversions -/v2.0: quantumapi_v2_0 +/: neutronversions +/v2.0: neutronapi_v2_0 -[composite:quantumapi_v2_0] -use = call:quantum.auth:pipeline_factory -noauth = extensions quantumapiapp_v2_0 -keystone = authtoken keystonecontext extensions quantumapiapp_v2_0 +[composite:neutronapi_v2_0] +use = call:neutron.auth:pipeline_factory +noauth = request_id catch_errors extensions neutronapiapp_v2_0 +keystone = request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 + +[filter:request_id] +paste.filter_factory = neutron.openstack.common.middleware.request_id:RequestIdMiddleware.factory + +[filter:catch_errors] +paste.filter_factory = neutron.openstack.common.middleware.catch_errors:CatchErrorsMiddleware.factory [filter:keystonecontext] -paste.filter_factory = quantum.auth:QuantumKeystoneContext.factory +paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory [filter:extensions] -paste.filter_factory = quantum.api.extensions:plugin_aware_extension_middleware_factory +paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory -[app:quantumversions] -paste.app_factory = quantum.api.versions:Versions.factory +[app:neutronversions] +paste.app_factory = neutron.api.versions:Versions.factory -[app:quantumapiapp_v2_0] -paste.app_factory = quantum.api.v2.router:APIRouter.factory +[app:neutronapiapp_v2_0] +paste.app_factory = neutron.api.v2.router:APIRouter.factory [filter:authtoken] paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory -auth_host = <%= @identity_endpoint.host %> -auth_port = <%= @identity_endpoint.port %> -auth_protocol = <%= @identity_endpoint.scheme %> -admin_tenant_name = <%= @service_tenant_name %> -admin_user = <%=@service_user %> -admin_password = <%= @service_pass %> delay_auth_decision = true -signing_dir = <%= node["openstack"]["network"]["api"]["auth"]["cache_dir"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/dhcp_agent.ini.erb b/chef/cookbooks/openstack-network/templates/default/dhcp_agent.ini.erb index 202ec1f..270a30d 100644 --- a/chef/cookbooks/openstack-network/templates/default/dhcp_agent.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/dhcp_agent.ini.erb @@ -4,7 +4,7 @@ # Show debugging output in log (sets DEBUG log level output) debug = <%= node["openstack"]["network"]["debug"] %> -# The DHCP agent will resync its state with Quantum to recover from any +# The DHCP agent will resync its state with Neutron to recover from any # transient notification or rpc errors. The interval is number of # seconds between attempts. resync_interval = <%= node["openstack"]["network"]["dhcp"]["resync_interval"] %> @@ -13,9 +13,9 @@ resync_interval = <%= node["openstack"]["network"]["dhcp"]["resync_interval"] %> # matches your plugin. # OVS based plugins (OVS, Ryu, NEC, NVP, BigSwitch/Floodlight) -# interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver +# interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver # LinuxBridge -#interface_driver = quantum.agent.linux.interface.BridgeInterfaceDriver +#interface_driver = neutron.agent.linux.interface.BridgeInterfaceDriver interface_driver = <%= node["openstack"]["network"]["interface_driver"] %> # OVS based plugins(Ryu, NEC, NVP, BigSwitch/Floodlight) that use OVS @@ -39,7 +39,7 @@ enable_isolated_metadata = <%= node["openstack"]["network"]["dhcp"]["enable_isol # Allows for serving metadata requests coming from a dedicated metadata # access network whose cidr is 169.254.169.254/16 (or larger prefix), and -# is connected to a Quantum router from which the VMs send metadata +# is connected to a Neutron router from which the VMs send metadata # request. In this case DHCP Option 121 will not be injected in VMs, as # they will be able to reach 169.254.169.254 through a router. # This option requires enable_isolated_metadata = True @@ -51,5 +51,7 @@ dhcp_domain = <%= node["openstack"]["network"]["dhcp"]["default_domain"] %> # Pass a config file to dnsmasq so we can override settings # like the mtu passed to the virtual machine -dnsmasq_config_file = /etc/quantum/dnsmasq.conf +dnsmasq_config_file = /etc/neutron/dnsmasq.conf +# Limit number of leases to prevent a denial-of-service. +dnsmasq_lease_max = <%= node["openstack"]["network"]["dhcp"]["dnsmasq_lease_max"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/l3_agent.ini.erb b/chef/cookbooks/openstack-network/templates/default/l3_agent.ini.erb index da9edfa..62e20f0 100644 --- a/chef/cookbooks/openstack-network/templates/default/l3_agent.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/l3_agent.ini.erb @@ -8,9 +8,9 @@ debug = <%= node["openstack"]["network"]["debug"] %> # matches your plugin. # OVS based plugins (OVS, Ryu, NEC, NVP, BigSwitch/Floodlight) -# interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver +# interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver # LinuxBridge -# interface_driver = quantum.agent.linux.interface.BridgeInterfaceDriver +# interface_driver = neutron.agent.linux.interface.BridgeInterfaceDriver interface_driver = <%= node["openstack"]["network"]["interface_driver"] %> # Allow overlapping IP (Must have kernel build with CONFIG_NET_NS=y and @@ -35,7 +35,7 @@ gateway_external_network_id = <%= node["openstack"]["network"]["l3"]["gateway_ex # Indicates that this L3 agent should also handle routers that do not have # an external network gateway configured. This option should be True only -# for a single agent in a Quantum deployment, and may be False for all agents +# for a single agent in a Neutron deployment, and may be False for all agents # if all routers must have an external network gateway handle_internal_only_routers = <%= node["openstack"]["network"]["l3"]["handle_internal_only_routers"] %> @@ -43,7 +43,7 @@ handle_internal_only_routers = <%= node["openstack"]["network"]["l3"]["handle_in # empty value for the linux bridge external_network_bridge = <%= node["openstack"]["network"]["l3"]["external_network_bridge"] %> -# TCP Port used by Quantum metadata server +# TCP Port used by Neutron metadata server metadata_port = <%= node["openstack"]["network"]["l3"]["metadata_port"] %> # Send this many gratuitous ARPs for HA setup. Set it below or equal to 0 diff --git a/chef/cookbooks/openstack-network/templates/default/lbaas_agent.ini.erb b/chef/cookbooks/openstack-network/templates/default/lbaas_agent.ini.erb index b6e3913..6db7f5a 100644 --- a/chef/cookbooks/openstack-network/templates/default/lbaas_agent.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/lbaas_agent.ini.erb @@ -4,7 +4,7 @@ # Show debugging output in log (sets DEBUG log level output) debug = <%= node["openstack"]["network"]["debug"] %> -# The LBaaS agent will resync its state with Quantum to recover from any +# The LBaaS agent will resync its state with Neutron to recover from any # transient notification or rpc errors. The interval is number of # seconds between attempts. periodic_interval = <%= node["openstack"]["network"]["lbaas"]["periodic_interval"] %> @@ -12,13 +12,13 @@ periodic_interval = <%= node["openstack"]["network"]["lbaas"]["periodic_interval <% case node["openstack"]["network"]["lbaas_plugin"] when "ovs" %> # OVS based plugins(OVS, Ryu, NEC, NVP, BigSwitch/Floodlight) -interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver +interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver # OVS based plugins(Ryu, NEC, NVP, BigSwitch/Floodlight) that use OVS # as OpenFlow switch and check port status #ovs_use_veth = True <% when "linuxbridge" %> # LinuxBridge -interface_driver = quantum.agent.linux.interface.BridgeInterfaceDriver +interface_driver = neutron.agent.linux.interface.BridgeInterfaceDriver <% else %> # LBaaS currently supports openvswitch and linuxbridge drivers only. # Please use one of them. @@ -27,11 +27,11 @@ interface_driver = # The agent requires a driver to manage the loadbalancer. HAProxy is the # opensource version. -device_driver = quantum.plugins.services.agent_loadbalancer.drivers.haproxy.namespace_driver.HaproxyNSDriver +device_driver = <%= node['openstack']['network']['lbaas']['device_driver'] %> -# Allow overlapping IP (Must have kernel build with CONFIG_NET_NS=y and -# iproute2 package that supports namespaces). -# use_namespaces = True +[haproxy] +# Location to store config and state files +# loadbalancer_state_path = $state_path/lbaas # The user group # user_group = nogroup diff --git a/chef/cookbooks/openstack-network/templates/default/metadata_agent.ini.erb b/chef/cookbooks/openstack-network/templates/default/metadata_agent.ini.erb index 429be08..5e31162 100644 --- a/chef/cookbooks/openstack-network/templates/default/metadata_agent.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/metadata_agent.ini.erb @@ -4,17 +4,18 @@ # Show debugging output in log (sets DEBUG log level output) debug = <%= node["openstack"]["network"]["debug"] %> -# The Quantum user information for accessing the Quantum API. +# The Neutron user information for accessing the Neutron API. auth_url = <%= @identity_endpoint.to_s %> auth_region = <%= node["openstack"]["network"]["region"] %> -admin_tenant_name = <%= @service_tenant_name %> -admin_user = <%= @service_user %> +admin_tenant_name = <%= node["openstack"]["network"]["service_tenant_name"] %> +admin_user = <%= node["openstack"]["network"]["service_user"] %> admin_password = <%= @service_pass %> # IP address used by Nova metadata server # Default: nova_metadata_ip = 127.0.0.1 <% if node["openstack"]["network"]["metadata"]["nova_metadata_ip"] -%> -nova_metadata_ip = <%= node["openstack"]["network"]["metadata"]["nova_metadata_ip"] %> +# nova_metadata_ip = <%= node["openstack"]["network"]["metadata"]["nova_metadata_ip"] %> +nova_metadata_ip = <%= @nova_metadata_ip %> <% end -%> # TCP Port used by Nova metadata server @@ -23,8 +24,8 @@ nova_metadata_ip = <%= node["openstack"]["network"]["metadata"]["nova_metadata_i nova_metadata_port = <%= node["openstack"]["network"]["metadata"]["nova_metadata_port"] %> <% end -%> -# When proxying metadata requests, Quantum signs the Instance-ID header with a +# When proxying metadata requests, Neutron signs the Instance-ID header with a # shared secret to prevent spoofing. You may select any string for a secret, # but it must match here and in the configuration used by the Nova Metadata -# Server. NOTE: Nova uses a different key: quantum_metadata_proxy_shared_secret +# Server. NOTE: Nova uses a different key: neutron_metadata_proxy_shared_secret metadata_proxy_shared_secret = <%= @metadata_secret %> diff --git a/chef/cookbooks/openstack-network/templates/default/quantum-server.erb b/chef/cookbooks/openstack-network/templates/default/neutron-server.erb similarity index 59% rename from chef/cookbooks/openstack-network/templates/default/quantum-server.erb rename to chef/cookbooks/openstack-network/templates/default/neutron-server.erb index a23d8d6..1996f21 100644 --- a/chef/cookbooks/openstack-network/templates/default/quantum-server.erb +++ b/chef/cookbooks/openstack-network/templates/default/neutron-server.erb @@ -1,6 +1,6 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -# defaults for quantum-server +# defaults for neutron-server # path to config file corresponding to the core_plugin specified in -# quantum.conf -QUANTUM_PLUGIN_CONFIG=<%= @plugin_config %> +# neutron.conf +NEUTRON_PLUGIN_CONFIG=<%= @plugin_config %> diff --git a/chef/cookbooks/openstack-network/templates/default/neutron.conf.erb b/chef/cookbooks/openstack-network/templates/default/neutron.conf.erb new file mode 100644 index 0000000..91f1191 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/neutron.conf.erb @@ -0,0 +1,443 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DEFAULT] +# Default log level is INFO +# verbose and debug has the same result. +# One of them will set DEBUG log level output +debug = <%= node["openstack"]["network"]["debug"] %> +verbose = <%= node["openstack"]["network"]["verbose"] %> + +# Where to store Neutron state files. This directory must be writable by the +# user executing the agent. +state_path = <%= node["openstack"]["network"]["state_path"] %> + +# Where to store lock files +lock_path = <%= node["openstack"]["network"]["lock_path"] %> + +# log_format = %(asctime)s %(levelname)8s [%(name)s] %(message)s +# log_date_format = %Y-%m-%d %H:%M:%S + +# use_syslog -> syslog +# log_file and log_dir -> log_dir/log_file +<% if node["openstack"]["network"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% end %> +# (not log_file) and log_dir -> log_dir/{binary_name}.log +# use_stderr -> stderr +# (not user_stderr) and (not log_file) -> stdout +# publish_errors -> notification system + +# use_syslog = False +# syslog_log_facility = LOG_USER + +# use_stderr = True +# log_file = +log_dir = <%= node['openstack']['network']['log_dir'] %> + +# publish_errors = False + +# Address to bind the API server +bind_host = <%= @bind_address %> + +# Port the bind the API server to +bind_port = <%= @bind_port %> + +# Path to the extensions. Note that this can be a colon-separated list of +# paths. For example: +# api_extensions_path = extensions:/path/to/more/extensions:/even/more/extensions +# The __path__ of neutron.extensions is appended to this, so if your +# extensions are in there you don't need to specify them here +# api_extensions_path = + +# Neutron plugin provider module +# core_plugin = +core_plugin = <%= @core_plugin %> + +# Advanced service modules +# service_plugins = +<% if node['openstack']['network']['service_plugins'].any? %> +service_plugins = <%= node['openstack']['network']['service_plugins'].join(',') %> +<% end %> + +# Paste configuration file +api_paste_config = api-paste.ini + +# The strategy to be used for auth. +# Supported values are 'keystone'(default), 'noauth'. +# auth_strategy = keystone +auth_strategy = <%= node["openstack"]["network"]['auth_strategy'] %> + +# Base MAC address. The first 3 octets will remain unchanged. If the +# 4h octet is not 00, it will also used. The others will be +# randomly generated. +# 3 octet +# base_mac = fa:16:3e:00:00:00 +# 4 octet +# base_mac = fa:16:3e:4f:00:00 + +# Maximum amount of retries to generate a unique MAC address +# mac_generation_retries = 16 + +# DHCP Lease duration (in seconds) +dhcp_lease_duration = <%= node["openstack"]["network"]["dhcp_lease_duration"] %> + +# Allow sending resource operation notification to DHCP agent +# dhcp_agent_notification = True + +# Enable or disable bulk create/update/delete operations +# allow_bulk = True +# Enable or disable pagination +# allow_pagination = False +# Enable or disable sorting +# allow_sorting = False +# Enable or disable overlapping IPs for subnets +# Attention: the following parameter MUST be set to False if Neutron is +# being used in conjunction with nova security groups and/or metadata service. +# allow_overlapping_ips = False +# Ensure that configured gateway is on subnet +# force_gateway_on_subnet = False + + +# RPC configuration options. Defined in rpc __init__ +# The messaging module to use, defaults to kombu. +# rpc_backend = neutron.openstack.common.rpc.impl_kombu +# Size of RPC thread pool +rpc_thread_pool_size = <%= node['openstack']['network']['rpc_thread_pool_size'] %> +# Size of RPC connection pool +rpc_conn_pool_size = <%= node['openstack']['network']['rpc_conn_pool_size'] %> +# Seconds to wait for a response from call or multicall +rpc_response_timeout = <%= node['openstack']['network']['rpc_response_timeout'] %> +# Seconds to wait before a cast expires (TTL). Only supported by impl_zmq. +rpc_cast_timeout = <%= node['openstack']['network']['rpc_cast_timeout'] %> + +# Modules of exceptions that are permitted to be recreated +# upon receiving exception data from an rpc call. +# allowed_rpc_exception_modules = neutron.openstack.common.exception, nova.exception +# AMQP exchange to connect to if using RabbitMQ or QPID +control_exchange = <%= node["openstack"]["network"]["control_exchange"] %> +# AMQP queue config options +amqp_durable_queues=<%= node['openstack']['mq']['network']['durable_queues'] %> +amqp_auto_delete=<%= node['openstack']['mq']['network']['auto_delete'] %> + +# Configuration options if sending notifications via kombu rpc (these are +# the defaults) +# SSL version to use (valid only if SSL enabled) +# kombu_ssl_version = +# SSL key file (valid only if SSL enabled) +# kombu_ssl_keyfile = +# SSL cert file (valid only if SSL enabled) +# kombu_ssl_certfile = +# SSL certification authority file (valid only if SSL enabled)' +# kombu_ssl_ca_certs = + +# allow_overlapping_ips = False +allow_overlapping_ips = <%= node["openstack"]["network"]["allow_overlapping_ips"] %> + +<% if @mq_service_type == "rabbitmq" %> +##### RABBITMQ ##### +rpc_backend=neutron.openstack.common.rpc.impl_kombu +rabbit_userid=<%= node["openstack"]["mq"]["network"]["rabbit"]["userid"] %> +rabbit_password=<%= @mq_password %> +rabbit_virtual_host=<%= node["openstack"]["mq"]["network"]["rabbit"]["vhost"] %> +<% if node["openstack"]["mq"]["network"]["rabbit"]["ha"] %> +# Use HA queues in RabbitMQ (x-ha-policy: all).You need to +# wipe RabbitMQ database when changing this option. (boolean value) +rabbit_hosts=<%= @rabbit_hosts %> +rabbit_ha_queues=True +<% else %> +rabbit_host=<%= node["openstack"]["mq"]["network"]["rabbit"]["host"] %> +rabbit_port=<%= node["openstack"]["mq"]["network"]["rabbit"]["port"] %> +<% end %> +# Maximum retries with trying to connect to RabbitMQ +# (the default of 0 implies an infinite retry count) +# rabbit_max_retries = 0 +# RabbitMQ connection retry interval +# rabbit_retry_interval = 1 +<% end %> + +<% if @mq_service_type == "qpid" %> +##### QPID ##### +rpc_backend=neutron.openstack.common.rpc.impl_qpid +qpid_hostname=<%= node["openstack"]["mq"]["network"]["qpid"]["host"] %> +qpid_port=<%= node["openstack"]["mq"]["network"]["qpid"]["port"] %> +qpid_password=<%= @mq_password %> +qpid_username=<%= node["openstack"]["mq"]["network"]["qpid"]["username"] %> +qpid_sasl_mechanisms=<%= node["openstack"]["mq"]["network"]["qpid"]["sasl_mechanisms"] %> +qpid_reconnect=<%= node["openstack"]["mq"]["network"]["qpid"]["reconnect"] %> +qpid_reconnect_timeout=<%= node["openstack"]["mq"]["network"]["qpid"]["reconnect_timeout"] %> +qpid_reconnect_limit=<%= node["openstack"]["mq"]["network"]["qpid"]["reconnect_limit"] %> +qpid_reconnect_interval_min=<%= node["openstack"]["mq"]["network"]["qpid"]["reconnect_interval_min"] %> +qpid_reconnect_interval_max=<%= node["openstack"]["mq"]["network"]["qpid"]["reconnect_interval_max"] %> +qpid_reconnect_interval=<%= node["openstack"]["mq"]["network"]["qpid"]["reconnect_interval"] %> +qpid_heartbeat=<%= node["openstack"]["mq"]["network"]["qpid"]["heartbeat"] %> +# qpid protocol. default 'tcp'. set to 'ssl' to enable SSL +qpid_protocol=<%= node["openstack"]["mq"]["network"]["qpid"]["protocol"] %> +qpid_tcp_nodelay=<%= node["openstack"]["mq"]["network"]["qpid"]["tcp_nodelay"] %> +qpid_topology_version=<%= node['openstack']['mq']['network']['qpid']['topology_version'] %> +<% end %> + +# ZMQ +# rpc_backend=neutron.openstack.common.rpc.impl_zmq +# ZeroMQ bind address. Should be a wildcard (*), an ethernet interface, or IP. +# The "host" option should point or resolve to this address. +# rpc_zmq_bind_address = * + +# ============ Notification System Options ===================== + +# Notifications can be sent when network/subnet/port are create, updated or deleted. +# There are three methods of sending notifications: logging (via the +# log_file directive), rpc (via a message queue) and +# noop (no notifications sent, the default) + +# Notification_driver can be defined multiple times +# Do nothing driver +# notification_driver = neutron.openstack.common.notifier.no_op_notifier +# Logging driver +# notification_driver = neutron.openstack.common.notifier.log_notifier +# RPC driver. DHCP agents needs it. +#notification_driver = neutron.openstack.common.notifier.rpc_notifier +notification_driver = <%= node["openstack"]["network"]["notification_driver"] %> + +# default_notification_level is used to form actual topic name(s) or to set logging level +default_notification_level = INFO + +# default_publisher_id is a part of the notification payload +# host = myhost.com +# default_publisher_id = $host + +# Defined in rpc_notifier, can be comma separated values. +# The actual topic names will be %s.%(default_notification_level)s +notification_topics = <%= node["openstack"]["mq"]["network"]["notification_topics"] %> + +# Default maximum number of items returned in a single response, +# value == infinite and value < 0 means no max limit, and value must +# greater than 0. If the number of items requested is greater than +# pagination_max_limit, server will just return pagination_max_limit +# of number of items. +# pagination_max_limit = -1 + +# Maximum number of DNS nameservers per subnet +# max_dns_nameservers = 5 + +# Maximum number of host routes per subnet +# max_subnet_host_routes = 20 + +# Maximum number of fixed ips per port +# max_fixed_ips_per_port = 5 + +# =========== items for agent management extension ============= +# Seconds to regard the agent as down; should be at least twice +# report_interval, to be sure the agent is down for good +agent_down_time = <%= node["openstack"]["network"]["api"]["agent"]["agent_down_time"] %> +# =========== end of items for agent management extension ===== + +# =========== items for agent scheduler extension ============= +# Driver to use for scheduling network to DHCP agent +network_scheduler_driver = <%= node["openstack"]["network"]["dhcp"]["scheduler"] %> +# Driver to use for scheduling router to a default L3 agent +router_scheduler_driver = <%= node["openstack"]["network"]["l3"]["scheduler"] %> + +# Allow auto scheduling networks to DHCP agent. It will schedule non-hosted +# networks to first DHCP agent which sends get_active_networks message to +# neutron server +# network_auto_schedule = True + +# Allow auto scheduling routers to L3 agent. It will schedule non-hosted +# routers to first L3 agent which sends sync_routers message to neutron server +# router_auto_schedule = True +# =========== end of items for agent scheduler extension ===== + +# =========== WSGI parameters related to the API server ============== +# Number of separate worker processes to spawn. The default, 0, runs the +# worker thread in the current process. Greater than 0 launches that number of +# child processes as workers. The parent process manages them. +api_workers = <%= node['openstack']['network']['api_workers'] %> + +# Number of separate RPC worker processes to spawn. The default, 0, runs the +# worker thread in the current process. Greater than 0 launches that number of +# child processes as RPC workers. The parent process manages them. +# This feature is experimental until issues are addressed and testing has been +# enabled for various plugins for compatibility. +rpc_workers = <%= node['openstack']['network']['rpc_workers'] %> + +# Sets the value of TCP_KEEPIDLE in seconds to use for each server socket when +# starting API server. Not supported on OS X. +#tcp_keepidle = 600 + +# Number of seconds to keep retrying to listen +#retry_until_window = 30 + +# Number of backlog requests to configure the socket with. +#backlog = 4096 + +# Enable SSL on the API server +#use_ssl = False + +# Certificate file to use when starting API server securely +#ssl_cert_file = /path/to/certfile + +# Private key file to use when starting API server securely +#ssl_key_file = /path/to/keyfile + +# CA certificate file to use when starting API server securely to +# verify connecting clients. This is an optional parameter only required if +# API clients need to authenticate to the API server using SSL certificates +# signed by a trusted CA +#ssl_ca_file = /path/to/cafile +# ======== end of WSGI parameters related to the API server ========== + +# ======== neutron nova interactions ========== +# Send notification to nova when port status is active. +notify_nova_on_port_status_changes = <%= node["openstack"]["network"]["nova"]["notify_nova_on_port_status_changes"] %> + +# Send notifications to nova when port data (fixed_ips/floatingips) change +# so nova can update it's cache. +notify_nova_on_port_data_changes = <%= node["openstack"]["network"]["nova"]["notify_nova_on_port_data_changes"] %> + +# URL for connection to nova (Only supports one nova region currently). +nova_url = <%= @nova_endpoint %> + +# Name of nova region to use. Useful if keystone manages more than one region +nova_region_name = <%= node["openstack"]["network"]["nova"]["region_name"] %> + +# Username for connection to nova in admin context +nova_admin_username = <%= node["openstack"]["network"]["nova"]["admin_username"] %> + +# The uuid of the admin nova tenant +nova_admin_tenant_id = <%= node["openstack"]["network"]["nova"]["admin_tenant_id"] %> + +# Password for connection to nova in admin context. +nova_admin_password = <%= @nova_admin_pass %> + +# Authorization URL for connection to nova in admin context. +nova_admin_auth_url = <%= @identity_admin_endpoint.to_s %> + +# Number of seconds between sending events to nova if there are any events to send +send_events_interval = <%= node["openstack"]["network"]["nova"]["send_events_interval"] %> + +# ======== end of neutron nova interactions ========== + +# Misc options +<% if node["openstack"]["network"]["misc_neutron"] %> +<% node["openstack"]["network"]["misc_neutron"].each do |m| %> +<%= m %> +<% end %> +<% end %> + +[QUOTAS] +# resource name(s) that are supported in quota features +quota_items = <%= node["openstack"]["network"]["quota"]["items"] %> + +# default number of resource allowed per tenant, minus for unlimited +default_quota = <%= node["openstack"]["network"]["quota"]["default"] %> + +# number of networks allowed per tenant, and minus means unlimited +quota_network = <%= node["openstack"]["network"]["quota"]["network"] %> + +# number of subnets allowed per tenant, and minus means unlimited +quota_subnet = <%= node["openstack"]["network"]["quota"]["subnet"] %> + +# number of ports allowed per tenant, and minus means unlimited +quota_port = <%= node["openstack"]["network"]["quota"]["port"] %> + +# number of security groups allowed per tenant, and minus means unlimited +quota_security_group = <%= node["openstack"]["network"]["quota"]["security_group"] %> + +# number of security group rules allowed per tenant, and minus means unlimited +quota_security_group_rule = <%= node["openstack"]["network"]["quota"]["security_group_rule"] %> + +# default driver to use for quota checks +quota_driver = <%= node["openstack"]["network"]["quota"]["driver"] %> + +[DEFAULT_SERVICETYPE] +# Description of the default service type (optional) +# description = "default service type" +# Enter a service definition line for each advanced service provided +# by the default service type. +# Each service definition should be in the following format: +# :[:driver] + +[AGENT] +# Use "sudo neutron-rootwrap /etc/neutron/rootwrap.conf" to use the real +# root filter facility. +# Change to "sudo" to skip the filtering and just run the comand directly +# root_helper = sudo +<% if node["openstack"]["network"]["use_rootwrap"] %> +root_helper = "sudo neutron-rootwrap /etc/neutron/rootwrap.conf" +<% end %> + +# =========== items for agent management extension ============= +# seconds between nodes reporting state to server, should be less than +# agent_down_time, best if it is half or less than agent_down_time +report_interval = <%= node["openstack"]["network"]["api"]["agent"]["agent_report_interval"] %> + +[keystone_authtoken] +auth_uri = <%= @auth_uri %> +auth_host = <%= @identity_admin_endpoint.host %> +auth_port = <%= @identity_admin_endpoint.port %> +auth_protocol = <%= @identity_admin_endpoint.scheme %> +<% if node['openstack']['network']['api']['auth']['version'] != 'v2.0' %> +auth_version = <%= node['openstack']['network']['api']['auth']['version'] %> +<% end %> +admin_tenant_name = <%= node["openstack"]["network"]["service_tenant_name"] %> +admin_user = <%= node["openstack"]["network"]["service_user"] %> +admin_password = <%= @service_pass %> +signing_dir = <%= node["openstack"]["network"]["api"]["agent"]["signing_dir"] %> + +[database] +# This line MUST be changed to actually run the plugin. +# Example: +# connection = mysql://root:nova@127.0.0.1:3306/neutron_linux_bridge +# Replace 127.0.0.1 above with the IP address of the database used by the +# main neutron server. (Leave it as is if the database runs on this host.) +connection = <%= @sql_connection %> + +# The SQLAlchemy connection string used to connect to the slave database +slave_connection = <%= node['openstack']['db']['network']['slave_connection'] %> + +# Database reconnection retry times - in event connectivity is lost +# set to -1 implies an infinite retry count +max_retries = <%= node['openstack']['db']['network']['max_retries'] %> + +# Database reconnection interval in seconds - if the initial connection to the +# database fails +retry_interval = <%= node['openstack']['db']['network']['retry_interval'] %> + +# Minimum number of SQL connections to keep open in a pool +min_pool_size = <%= node['openstack']['db']['network']['min_pool_size'] %> + +# Maximum number of SQL connections to keep open in a pool +max_pool_size = <%= node['openstack']['db']['network']['max_pool_size'] %> + +# Timeout in seconds before idle sql connections are reaped +idle_timeout = <%= node['openstack']['db']['network']['idle_timeout'] %> + +# If set, use this value for max_overflow with sqlalchemy +max_overflow = <%= node['openstack']['db']['network']['max_overflow'] %> + +# Verbosity of SQL debugging information. 0=None, 100=Everything +connection_debug = <%= node['openstack']['db']['network']['connection_debug'] %> + +# Add python stack traces to SQL as comment strings +connection_trace = <%= node['openstack']['db']['network']['connection_trace'] %> + +# If set, use this value for pool_timeout with sqlalchemy +pool_timeout = <%= node['openstack']['db']['network']['pool_timeout'] %> + +[service_providers] +# Specify service providers (drivers) for advanced services like loadbalancer, VPN, Firewall. +# Must be in form: +# service_provider=::[:default] +# List of allowed service type include LOADBALANCER, FIREWALL, VPN +# Combination of and must be unique; must also be unique +# this is multiline option, example for default provider: +# service_provider=LOADBALANCER:name:lbaas_plugin_driver_path:default +# example of non-default provider: +# service_provider=FIREWALL:name2:firewall_driver_path +# --- Reference implementations --- +<% if node['openstack']['network']['service_provider'].any? %> + <% node['openstack']['network']['service_provider'].each do |provider| %> +service_provider = <%= provider %> + <% end %> +<% end %> + +# =========== end of items for agent management extension ===== diff --git a/chef/cookbooks/openstack-network/templates/default/quantum.sysconfig.erb b/chef/cookbooks/openstack-network/templates/default/neutron.sysconfig.erb similarity index 51% rename from chef/cookbooks/openstack-network/templates/default/quantum.sysconfig.erb rename to chef/cookbooks/openstack-network/templates/default/neutron.sysconfig.erb index f6408ce..8839356 100644 --- a/chef/cookbooks/openstack-network/templates/default/quantum.sysconfig.erb +++ b/chef/cookbooks/openstack-network/templates/default/neutron.sysconfig.erb @@ -2,4 +2,4 @@ # # location of the plugin configuration file -QUANTUM_PLUGIN_CONF="/etc/quantum/plugins/<%= @plugin_conf %>" \ No newline at end of file +NEUTRON_PLUGIN_CONF="/etc/neutron/plugins/<%= @plugin_conf %>" \ No newline at end of file diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/bigswitch/restproxy.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/bigswitch/restproxy.ini.erb index 708f184..b7f2fb5 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/bigswitch/restproxy.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/bigswitch/restproxy.ini.erb @@ -1,27 +1,4 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://root:pass@127.0.0.1:3306/restproxy_quantum -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -sql_connection = <%= @sql_connection %> -# Database reconnection retry times - in event connectivity is lost -# set to -1 implies an infinite retry count -# sql_max_retries = 10 -# Database reconnection interval in seconds - if the initial connection to the -# database fails -reconnect_interval = 2 -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - [RESTPROXY] # All configuration for this plugin is in section '[restproxy]' # diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/brocade/brocade.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/brocade/brocade.ini.erb index cc0f4b3..c4dfebb 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/brocade/brocade.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/brocade/brocade.ini.erb @@ -15,22 +15,6 @@ ostype = <%= node["openstack"]["network"]["brocade"]["switch_ostype"] %> # address = 10.24.84.38 # ostype = NOS -[DATABASE] -# sql_connection = sqlite:// -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 -# -# Example: -# sql_connection = mysql://root:pass@localhost/brcd_quantum?charset=utf8 -sql_connection = <%= @sql_connection %> - [PHYSICAL_INTERFACE] # physical_interface = # @@ -47,7 +31,7 @@ network_vlan_ranges = <%= node["openstack"]["network"]["brocade"]["network_vlan_ [AGENT] # Example: -# root_helper = sudo /usr/local/bin/quantum-rootwrap /etc/quantum/rootwrap.conf +# root_helper = sudo /usr/local/bin/neutron-rootwrap /etc/neutron/rootwrap.conf [LINUX_BRIDGE] # physical_interface_mappings = : diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/cisco/cisco_plugins.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/cisco/cisco_plugins.ini.erb index 656d9de..9afae5b 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/cisco/cisco_plugins.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/cisco/cisco_plugins.ini.erb @@ -24,13 +24,3 @@ username = <%= @info["username"] %> password = <%= @info["password"] %> <%- end -%> - -[DATABASE] -# -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://quantum:password@127.0.0.1:3306/cisco_quantum -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -# -sql_connection = <%= @sql_connection %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_quantum_plugin.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_neutron_plugin.ini.erb similarity index 68% rename from chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_quantum_plugin.ini.erb rename to chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_neutron_plugin.ini.erb index 947e048..2ee3555 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_quantum_plugin.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_neutron_plugin.ini.erb @@ -1,27 +1,4 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://quantum:password@127.0.0.1:3306/hyperv_quantum -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -sql_connection = <%= @sql_connection %> -# Database reconnection retry times - in event connectivity is lost -# set to -1 implies an infinite retry count -# sql_max_retries = 10 -# Database reconnection interval in seconds - if the initial connection to the -# database fails -reconnect_interval = 2 -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - [HYPERV] # (StrOpt) Type of network to allocate for tenant networks. The # default value 'local' is useful only for single-box testing and @@ -66,3 +43,7 @@ physical_network_vswitch_mappings = <%= node["openstack"]["network"]["hyperv"][" # Default: local_network_vswitch = private # Example: local_network_vswitch = custom_vswitch local_network_vswitch = <%= node["openstack"]["network"]["hyperv"]["local_network_vswitch"] %> + +[SECURITYGROUP] +# Firewall driver for realizing neutron security group function +firewall_driver = <%= node["openstack"]["network"]["hyperv"]["firewall_driver"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/linuxbridge/linuxbridge_conf.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/linuxbridge/linuxbridge_conf.ini.erb index f5816f0..9514778 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/linuxbridge/linuxbridge_conf.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/linuxbridge/linuxbridge_conf.ini.erb @@ -1,5 +1,5 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[VLANS] +[vlans] # (StrOpt) Type of network to allocate for tenant networks. The # default value 'local' is useful only for single-box testing and # provides no connectivity between hosts. You MUST change this to @@ -23,30 +23,7 @@ tenant_network_type = <%= node["openstack"]["network"]["linuxbridge"]["tenant_ne # Example: network_vlan_ranges = physnet1:1000:2999 network_vlan_ranges = <%= node["openstack"]["network"]["linuxbridge"]["network_vlan_ranges"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://root:nova@127.0.0.1:3306/quantum_linux_bridge -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -sql_connection = <%= @sql_connection %> -# Database reconnection retry times - in event connectivity is lost -# set to -1 implies an infinite retry count -# sql_max_retries = 10 -# Database reconnection interval in seconds - if the initial connection to the -# database fails -reconnect_interval = 2 -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - -[LINUX_BRIDGE] +[linux_bridge] # (ListOpt) Comma-separated list of # : tuples mapping physical # network names to the agent's node-specific physical network @@ -56,12 +33,49 @@ reconnect_interval = 2 # # Default: physical_interface_mappings = # Example: physical_interface_mappings = physnet1:eth1 -physical_interface_mappings = <%= node["openstack"]["network"]["linuxbridge"]["physical_interface_mappings"] %> +physical_interface_mappings = <%= node['openstack']['network']['linuxbridge']['physical_interface_mappings'] %> -[AGENT] +[vxlan] +# (BoolOpt) enable VXLAN on the agent +# VXLAN support can be enabled when agent is managed by ml2 plugin using +# linuxbridge mechanism driver. Useless if set while using linuxbridge plugin. +enable_vxlan = <%= node['openstack']['network']['linuxbridge']['enable_vxlan'] %> + +# (IntOpt) use specific TTL for vxlan interface protocol packets +ttl = <%= node['openstack']['network']['linuxbridge']['ttl'] %> + +# (IntOpt) use specific TOS for vxlan interface protocol packets +tos = <%= node['openstack']['network']['linuxbridge']['tos'] %> + +# (StrOpt) multicast group to use for broadcast emulation. +# This group must be the same on all the agents. +vxlan_group = <%= node['openstack']['network']['linuxbridge']['vxlan_group'] %> + +# (StrOpt) Local IP address to use for VXLAN endpoints (required) +local_ip = <%= @local_ip %> + +# (BoolOpt) Flag to enable l2population extension. This option should be used +# in conjunction with ml2 plugin l2population mechanism driver (in that case, +# both linuxbridge and l2population mechanism drivers should be loaded). +# It enables plugin to populate VXLAN forwarding table, in order to limit +# the use of broadcast emulation (multicast will be turned off if kernel and +# iproute2 supports unicast flooding - requires 3.11 kernel and iproute2 3.10) +l2_population = <%= node['openstack']['network']['linuxbridge']['l2_population'] %> + +[agent] # Agent's polling interval in seconds -polling_interval = 2 +polling_interval = <%= node['openstack']['network']['linuxbridge']['polling_interval'] %> -[SECURITYGROUP] -# Firewall driver for realizing quantum security group function -firewall_driver = quantum.agent.linux.iptables_firewall.IptablesFirewallDriver +# (BoolOpt) Enable server RPC compatibility with old (pre-havana) +# agents. +# +# rpc_support_old_agents = False +# Example: rpc_support_old_agents = True +rpc_support_old_agents = <%= node['openstack']['network']['linuxbridge']['rpc_support_old_agents'] %> + +[securitygroup] +# Firewall driver for realizing neutron security group function +firewall_driver = <%= node['openstack']['network']['linuxbridge']['firewall_driver'] %> + +# It should be false when you use nova security group. +enable_security_group = <%= node['openstack']['network']['linuxbridge']['enable_security_group'] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/metaplugin/metaplugin.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/metaplugin/metaplugin.ini.erb index 9582a92..3c40477 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/metaplugin/metaplugin.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/metaplugin/metaplugin.ini.erb @@ -1,31 +1,6 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -sql_connection = <%= @sql_connection %> - -# Database reconnection retry times - in event connectivity is lost -# set to -1 implgies an infinite retry count -# sql_max_retries = 10 -# Database reconnection interval in seconds - if the initial connection to the -# database fails -reconnect_interval = 2 - -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - [META] -## This is list of flavor:quantum_plugins +## This is list of flavor:neutron_plugins # extension method is used in the order of this list plugin_list = <%= node["openstack"]["network"]["metaplugin"]["plugin_list"] %> l3_plugin_list = <%= node["openstack"]["network"]["metaplugin"]["l3_plugin_list"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/midonet/midonet.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/midonet/midonet.ini.erb index cc21d04..1b74f90 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/midonet/midonet.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/midonet/midonet.ini.erb @@ -1,27 +1,4 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://root:pass@127.0.0.1:3306/midonet_quantum -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -sql_connection = <%= @sql_connection %> -# Database reconnection retry times - in event connectivity is lost -# set to -1 implies an infinite retry count -# sql_max_retries = 10 -# Database reconnection interval in seconds - if the initial connection to the -# database fails -reconnect_interval = 2 -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - [MIDONET] # MidoNet API server URI # midonet_uri = http://localhost:8080/midonet-api diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/ml2/ml2_conf.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/ml2/ml2_conf.ini.erb new file mode 100644 index 0000000..185e147 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/ml2/ml2_conf.ini.erb @@ -0,0 +1,65 @@ +<%= node['openstack']['network']['custom_template_banner'] %> +[ml2] +# (ListOpt) List of network type driver entrypoints to be loaded from +# the neutron.ml2.type_drivers namespace. +# +# type_drivers = local,flat,vlan,gre,vxlan +# Example: type_drivers = flat,vlan,gre,vxlan +type_drivers = <%= node['openstack']['network']['ml2']['type_drivers'] %> + +# (ListOpt) Ordered list of network_types to allocate as tenant +# networks. The default value 'local' is useful for single-box testing +# but provides no connectivity between hosts. +# +# tenant_network_types = local +# Example: tenant_network_types = vlan,gre,vxlan +tenant_network_types = <%= node['openstack']['network']['ml2']['tenant_network_types'] %> + +# (ListOpt) Ordered list of networking mechanism driver entrypoints +# to be loaded from the neutron.ml2.mechanism_drivers namespace. +# mechanism_drivers = +# Example: mechanism_drivers = arista +# Example: mechanism_drivers = cisco,logger +mechanism_drivers = <%= node['openstack']['network']['ml2']['mechanism_drivers'] %> + +[ml2_type_flat] +# (ListOpt) List of physical_network names with which flat networks +# can be created. Use * to allow flat networks with arbitrary +# physical_network names. +# +# flat_networks = +# Example:flat_networks = physnet1,physnet2 +# Example:flat_networks = * +flat_networks = <%= node['openstack']['network']['ml2']['flat_networks'] %> + +[ml2_type_vlan] +# (ListOpt) List of [::] tuples +# specifying physical_network names usable for VLAN provider and +# tenant networks, as well as ranges of VLAN tags on each +# physical_network available for allocation as tenant networks. +# +# network_vlan_ranges = +# Example: network_vlan_ranges = physnet1:1000:2999,physnet2 +network_vlan_ranges = <%= node['openstack']['network']['ml2']['network_vlan_ranges'] %> + +[ml2_type_gre] +# (ListOpt) Comma-separated list of : tuples enumerating ranges of GRE tunnel IDs that are available for tenant network allocation +tunnel_id_ranges = <%= node['openstack']['network']['ml2']['tunnel_id_ranges'] %> + +[ml2_type_vxlan] +# (ListOpt) Comma-separated list of : tuples enumerating +# ranges of VXLAN VNI IDs that are available for tenant network allocation. +vni_ranges = <%= node['openstack']['network']['ml2']['vni_ranges'] %> + +# (StrOpt) Multicast group for the VXLAN interface. When configured, will +# enable sending all broadcast traffic to this multicast group. When left +# unconfigured, will disable multicast VXLAN mode. +# +# vxlan_group = +# Example: vxlan_group = 239.1.1.1 +vxlan_group = <%= node['openstack']['network']['ml2']['vxlan_group'] %> + +[securitygroup] +# Controls if neutron security group is enabled or not. +# It should be false when you use nova security group. +enable_security_group = <%= node['openstack']['network']['ml2']['enable_security_group'] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/nec/nec.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/nec/nec.ini.erb index 5cebcac..a890bdb 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/nec/nec.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/nec/nec.ini.erb @@ -1,27 +1,4 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -sql_connection = <%= @sql_connection %> -# Database reconnection retry times - in event connectivity is lost -# set to -1 implies an infinite retry count -# sql_max_retries = 10 -# Database reconnection interval in seconds - if the initial connection to the -# database fails -reconnect_interval = 2 -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - [OVS] # Do not change this parameter unless you have a good reason to. # This is the name of the OVS integration bridge. There is one per hypervisor. @@ -34,13 +11,13 @@ integration_bridge = <%= node["openstack"]["network"]["nec"]["integration_bridge # Agent's polling interval in seconds polling_interval = <%= node["openstack"]["network"]["nec"]["polling_interval"] %> -# Use "sudo quantum-rootwrap /etc/quantum/rootwrap.conf" to use the real +# Use "sudo neutron-rootwrap /etc/neutron/rootwrap.conf" to use the real # root filter facility. # Change to "sudo" to skip the filtering and just run the comand directly root_helper = sudo [SECURITYGROUP] -# Firewall driver for realizing quantum security group function +# Firewall driver for realizing neutron security group function firewall_driver = <%= node["openstack"]["network"]["nec"]["firewall_driver"] %> [OFC] @@ -48,7 +25,7 @@ firewall_driver = <%= node["openstack"]["network"]["nec"]["firewall_driver"] %> host = <%= node["openstack"]["network"]["nec"]["ofc_host"] %> port = <%= node["openstack"]["network"]["nec"]["ofc_port"] %> -# Drivers are in quantum/plugins/nec/drivers/ . +# Drivers are in neutron/plugins/nec/drivers/ . driver = <%= node["openstack"]["network"]["nec"]["ofc_driver"] %> # PacketFilter is available when it's enabled in this configuration diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/nicira/nvp.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/nicira/nvp.ini.erb index b1e24a0..d8dd027 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/nicira/nvp.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/nicira/nvp.ini.erb @@ -19,7 +19,7 @@ [DEFAULT] # User name for NVP controller -nvp_user = admin <%= node["openstack"]["network"]["nicira"]["nvp_user"] %> +nvp_user = <%= node["openstack"]["network"]["nicira"]["nvp_user"] %> # Password for NVP controller nvp_password = <%= node["openstack"]["network"]["nicira"]["nvp_password"] %> @@ -42,8 +42,8 @@ redirects = <%= node["openstack"]["network"]["nicira"]["redirects"] %> nvp_controllers = <%= node["openstack"]["network"]["nicira"]["nvp_controllers"] %> # UUID of the pre-existing default NVP Transport zone to be used for creating -# tunneled isolated "Quantum" networks. This option MUST be specified, e.g.: -default_tz_uuid = <%= node["openstack"]["network"]["nicira"]["default_tx_uuid"] %> +# tunneled isolated "Neutron" networks. This option MUST be specified, e.g.: +default_tz_uuid = <%= node["openstack"]["network"]["nicira"]["default_tz_uuid"] %> # (Optional) UUID of the cluster in NVP. It can be retrieved from NVP management # console "admin" section. @@ -63,35 +63,6 @@ default_l2_gw_service_uuid = <%= node["openstack"]["network"]["nicira"]["default default_iface_name = <%= node["openstack"]["network"]["nicira"]["default_iface_name"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://root:quantum@127.0.0.1:3306/nvp_quantum -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -sql_connection = <%= @sql_connection %> - -# Number of reconnection attempts to the DB; Set to -1 to try indefinitely -# sql_max_retries = 10 - -# Period between reconnection attempts to the DB -# reconnect_interval = 2 - -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. - -# sql_dbpool_enable = False - -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 - -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 - -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - - [QUOTAS] # number of network gateways allowed per tenant, -1 means unlimited quota_network_gateway = <%= node["openstack"]["network"]["nicira"]["quota_network_gateway"] %> @@ -109,7 +80,7 @@ concurrent_connections = <%= node["openstack"]["network"]["nicira"]["concurrent_ # Acceptable values for 'metadata_mode' are: # - 'access_network': this enables a dedicated connection to the metadata -# proxy for metadata server access via Quantum router. +# proxy for metadata server access via Neutron router. # - 'dhcp_host_route': this enables host route injection via the dhcp agent. # This option is only useful if running on a host that does not support # namespaces otherwise access_network should be used. diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/openvswitch.erb b/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/openvswitch.erb deleted file mode 100644 index 25960a7..0000000 --- a/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/openvswitch.erb +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/sh -# -# openvswitch -# -# chkconfig: 2345 09 91 -# description: Manage Open vSwitch kernel modules and user-space daemons - -# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc. -# -# 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. -### BEGIN INIT INFO -# Provides: openvswitch-switch -# Required-Start: -# Required-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Open vSwitch switch -### END INIT INFO - -. /usr/share/openvswitch/scripts/ovs-lib || exit 1 -test -e /etc/sysconfig/openvswitch && . /etc/sysconfig/openvswitch - -start () { - service openvswitch status && exit 0 - - set $ovs_ctl ${1-start} - set "$@" --system-id=random - if test X"$FORCE_COREFILES" != X; then - set "$@" --force-corefiles="$FORCE_COREFILES" - fi - if test X"$OVSDB_SERVER_PRIORITY" != X; then - set "$@" --ovsdb-server-priority="$OVSDB_SERVER_PRIORITY" - fi - if test X"$VSWITCHD_PRIORITY" != X; then - set "$@" --ovs-vswitchd-priority="$VSWITCHD_PRIORITY" - fi - if test X"$VSWITCHD_MLOCKALL" != X; then - set "$@" --mlockall="$VSWITCHD_MLOCKALL" - fi - if test X"$BRCOMPAT" = Xyes; then - set "$@" --brcompat - fi - "$@" - -# RHEL6 does not support OVS GRE tunneling yet, do not add iptables GRE rule -# $ovs_ctl --protocol=gre enable-protocol - - touch /var/lock/subsys/openvswitch - - ovs-ofctl del-flows br-tun "table=0" - ovs-ofctl add-flow br-tun "table=0, priority=1, actions=drop" - service quantum-l3-agent status && service quantum-l3-agent restart - service quantum-dhcp-agent status && service quantum-dhcp-agent restart - service quantum-metadata-agent status && service quantum-metadata-agent restart - service quantum-openvswitch-agent restart || exit 0 -} - -stop () { - $ovs_ctl stop - rm -f /var/lock/subsys/openvswitch -} - -ovs_ctl=/usr/share/openvswitch/scripts/ovs-ctl -case $1 in - start) - start - ;; - stop) - stop - ;; - restart) - stop - start - ;; - reload|force-reload) - # Nothing to do. - ;; - status) - $ovs_ctl status - ;; - version) - $ovs_ctl version - ;; - force-reload-kmod) - start force-reload-kmod - ;; - help) - printf "$0 [start|stop|restart|reload|force-reload|status|version|force-reload-kmod]\n" - ;; - *) - printf "Unknown command: $1\n" - exit 1 - ;; -esac diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_quantum_plugin.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_neutron_plugin.ini.erb similarity index 76% rename from chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_quantum_plugin.ini.erb rename to chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_neutron_plugin.ini.erb index 948991c..69a0432 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_quantum_plugin.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_neutron_plugin.ini.erb @@ -1,29 +1,4 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum -# Replace 127.0.0.1 above with the IP address of the database used by the -# main quantum server. (Leave it as is if the database runs on this host.) -sql_connection = <%= @sql_connection %> -# Database reconnection retry times - in event connectivity is lost -# set to -1 implies an infinite retry count -# sql_max_retries = 10 -# Database reconnection interval in seconds - if the initial connection to the -# database fails -reconnect_interval = 2 -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -sql_max_pool_size = 100 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - -sqlalchemy_pool_size = 100 - [OVS] # (StrOpt) Type of network to allocate for tenant networks. The # default value 'local' is useful only for single-box testing and @@ -47,17 +22,23 @@ tenant_network_type = <%= node["openstack"]["network"]["openvswitch"]["tenant_ne # # Default: network_vlan_ranges = # Example: network_vlan_ranges = physnet1:1000:2999 -<% if node["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] != "nil" -%> +<% if node["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] -%> network_vlan_ranges = <%= node["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] %> <% end -%> # (BoolOpt) Set to True in the server and the agents to enable support -# for GRE networks. Requires kernel support for OVS patch ports and -# GRE tunneling. +# for GRE or VXLAN networks. Requires kernel support for OVS patch ports and +# GRE or VXLAN tunneling. # # Default: enable_tunneling = False enable_tunneling = <%= node["openstack"]["network"]["openvswitch"]["enable_tunneling"] %> +# (StrOpt) The type of tunnel network, if any, supported by the plugin. If +# this is set, it will cause tunneling to be enabled. If this is not set and +# the option enable_tunneling is set, this will default to 'gre'. +# 'gre' or 'vxlan' +tunnel_type = <%= node["openstack"]["network"]["openvswitch"]["tunnel_type"] %> + # (ListOpt) Comma-separated list of : tuples # enumerating ranges of GRE tunnel IDs that are available for tenant # network allocation if tenant_network_type is 'gre'. @@ -102,8 +83,6 @@ tun_peer_patch_port = <%= node["openstack"]["network"]["openvswitch"]["tun_peer_ # Uncomment this line for the agent if tunnel_id_ranges (above) is not # empty for the server. Set local-ip to be the local IP address of # this hypervisor. -# -# Default: local_ip = local_ip = <%= @local_ip %> # (ListOpt) Comma-separated list of : tuples @@ -116,28 +95,28 @@ local_ip = <%= @local_ip %> # # Default: bridge_mappings = # Example: bridge_mappings = physnet1:br-eth1 -<% if node["openstack"]["network"]["openvswitch"]["bridge_mappings"] !="nil" -%> +<% if node["openstack"]["network"]["openvswitch"]["bridge_mappings"] -%> bridge_mappings = <%= node["openstack"]["network"]["openvswitch"]["bridge_mappings"] %> <% end -%> [AGENT] # Agent's polling interval in seconds -polling_interval = 2 +polling_interval = <%= node['openstack']['network']['openvswitch']['polling_interval'] %> [SECURITYGROUP] -# Firewall driver for realizing quantum security group function -# Default: firewall_driver = quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver -<% if node["openstack"]["network"]["openvswitch"]["fw_driver"] -%> +# Firewall driver for realizing neutron security group function +# Default: firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver firewall_driver = <%= node["openstack"]["network"]["openvswitch"]["fw_driver"] %> -<% end -%> + +# Controls if neutron security group is enabled or not. +# It should be false when you use nova security group. +enable_security_group = <%= node['openstack']['network']['openvswitch']['enable_security_group'] %> #----------------------------------------------------------------------------- # Sample Configurations. #----------------------------------------------------------------------------- # # 1. With VLANs on eth1. -# [DATABASE] -# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum # [OVS] # network_vlan_ranges = default:2000:3999 # tunnel_id_ranges = @@ -147,8 +126,6 @@ firewall_driver = <%= node["openstack"]["network"]["openvswitch"]["fw_driver"] % # Add the following setting, if you want to log to a file # # 2. With tunneling. -# [DATABASE] -# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum # [OVS] # network_vlan_ranges = # tunnel_id_ranges = 1:1000 diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/plumgrid/plumgrid.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/plumgrid/plumgrid.ini.erb index 4d9945e..f71ba56 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/plumgrid/plumgrid.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/plumgrid/plumgrid.ini.erb @@ -1,27 +1,4 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: -# sql_connection = mysql://:@:3306/plumgrid_quantum -# Replace above with the IP address of the database used by the -# main quantum server. -sql_connection = <%= @sql_connection %> -# Database reconnection retry times - in event connectivity is lost -# set to -1 implies an infinite retry count -# sql_max_retries = 10 -# Database reconnection interval in seconds - if the initial connection to the -# database fails -# reconnect_interval = 2 -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - [PLUMgridNOS] # This line should be pointing to the NOS server, # for the PLUMgrid platform. In other deployments, diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/ryu/ryu.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/ryu/ryu.ini.erb index 8915d79..4415aba 100644 --- a/chef/cookbooks/openstack-network/templates/default/plugins/ryu/ryu.ini.erb +++ b/chef/cookbooks/openstack-network/templates/default/plugins/ryu/ryu.ini.erb @@ -1,18 +1,4 @@ <%= node["openstack"]["network"]["custom_template_banner"] %> -[DATABASE] -# This line MUST be changed to actually run the plugin. -# Example: sql_connection = mysql://root:nova@127.0.0.1:3306/ryu_quantum -sql_connection = <%= @sql_connection %> -# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, -# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. -# sql_dbpool_enable = False -# Minimum number of SQL connections to keep open in a pool -# sql_min_pool_size = 1 -# Maximum number of SQL connections to keep open in a pool -# sql_max_pool_size = 5 -# Timeout in seconds before idle sql connections are reaped -# sql_idle_timeout = 3600 - [OVS] # Do not change this parameter unless you have a good reason to. # This is the name of the OVS integration bridge. There is one per hypervisor. @@ -50,7 +36,7 @@ ovsdb_ip = <%= node["openstack"]["network"]["ryu"]["ovsdb_ip"] %> ovsdb_interface = <%= node["openstack"]["network"]["ryu"]["ovsdb_interface"] %> [SECURITYGROUP] -# Firewall driver for realizing quantum security group function +# Firewall driver for realizing neutron security group function firewall_driver = <%= node["openstack"]["network"]["ryu"]["firewall_driver"] %> [AGENT] diff --git a/chef/cookbooks/openstack-network/templates/default/policy.json.erb b/chef/cookbooks/openstack-network/templates/default/policy.json.erb deleted file mode 100644 index 4262566..0000000 --- a/chef/cookbooks/openstack-network/templates/default/policy.json.erb +++ /dev/null @@ -1,75 +0,0 @@ -{ - "context_is_admin": "role:admin", - "admin_or_owner": "rule:context_is_admin or tenant_id:%(tenant_id)s", - "admin_or_network_owner": "rule:context_is_admin or tenant_id:%(network_tenant_id)s", - "admin_only": "rule:context_is_admin", - "regular_user": "", - "shared": "field:networks:shared=True", - "external": "field:networks:router:external=True", - "default": "rule:admin_or_owner", - - "extension:provider_network:view": "rule:admin_only", - "extension:provider_network:set": "rule:admin_only", - - "extension:router:view": "rule:regular_user", - - "extension:port_binding:view": "rule:admin_only", - "extension:port_binding:set": "rule:admin_only", - - "subnets:private:read": "rule:admin_or_owner", - "subnets:private:write": "rule:admin_or_owner", - "subnets:shared:read": "rule:regular_user", - "subnets:shared:write": "rule:admin_only", - - "create_subnet": "rule:admin_or_network_owner", - "get_subnet": "rule:admin_or_owner or rule:shared", - "update_subnet": "rule:admin_or_network_owner", - "delete_subnet": "rule:admin_or_network_owner", - - "create_network": "", - "get_network": "rule:admin_or_owner or rule:shared or rule:external", - "create_network:shared": "rule:admin_only", - "create_network:router:external": "rule:admin_only", - "create_network:provider:network_type": "rule:admin_only", - "create_network:provider:physical_network": "rule:admin_only", - "create_network:provider:segmentation_id": "rule:admin_only", - "update_network": "rule:admin_or_owner", - "update_network:provider:network_type": "rule:admin_only", - "update_network:provider:physical_network": "rule:admin_only", - "update_network:provider:segmentation_id": "rule:admin_only", - "delete_network": "rule:admin_or_owner", - - "create_port": "", - "create_port:mac_address": "rule:admin_or_network_owner", - "create_port:fixed_ips": "rule:admin_or_network_owner", - "create_port:port_security_enabled": "rule:admin_or_network_owner", - "get_port": "rule:admin_or_owner", - "update_port": "rule:admin_or_owner", - "update_port:fixed_ips": "rule:admin_or_network_owner", - "update_port:port_security_enabled": "rule:admin_or_network_owner", - "delete_port": "rule:admin_or_owner", - - "extension:service_type:view_extended": "rule:admin_only", - "create_service_type": "rule:admin_only", - "update_service_type": "rule:admin_only", - "delete_service_type": "rule:admin_only", - "get_service_type": "rule:regular_user", - - "create_qos_queue": "rule:admin_only", - "get_qos_queue": "rule:admin_only", - "get_qos_queues": "rule:admin_only", - - "update_agent": "rule:admin_only", - "delete_agent": "rule:admin_only", - "get_agent": "rule:admin_only", - "get_agents": "rule:admin_only", - - "create_dhcp-network": "rule:admin_only", - "delete_dhcp-network": "rule:admin_only", - "get_dhcp-networks": "rule:admin_only", - "create_l3-router": "rule:admin_only", - "delete_l3-router": "rule:admin_only", - "get_l3-routers": "rule:admin_only", - "get_dhcp-agents": "rule:admin_only", - "get_l3-agents": "rule:admin_only" -} diff --git a/chef/cookbooks/openstack-network/templates/default/quantum-server.start.erb b/chef/cookbooks/openstack-network/templates/default/quantum-server.start.erb deleted file mode 100644 index 4bcef2f..0000000 --- a/chef/cookbooks/openstack-network/templates/default/quantum-server.start.erb +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/sh -# -# quantum OpenStack Software Defined Networking Service -# -# chkconfig: - 98 02 -# description: quantum provides an API to \ -# * request and configure virtual networks -### END INIT INFO - -. /etc/rc.d/init.d/functions - -prog=quantum -exec="/usr/bin/$prog-server" -config="/etc/$prog/$prog.conf" -pidfile="/var/run/$prog/$prog.pid" -logfile="/var/log/$prog/server.log" - -[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog - -lockfile=/var/lock/subsys/$prog-server - -start() { - [ -x $exec ] || exit 5 - [ -f $config ] || exit 6 - echo -n $"Starting $prog: " - # the default ovs plugin path in the following daemon is not correct, cause the quantum server canot start, this template is going to fix it. - daemon --user quantum --pidfile $pidfile "$exec --config-file $config --config-file /etc/$prog/plugins/openvswitch/ovs_quantum_plugin.ini --log-file $logfile &>/dev/null & echo \$! > $pidfile" - retval=$? - echo - [ $retval -eq 0 ] && touch $lockfile - return $retval -} - -stop() { - echo -n $"Stopping $prog: " - killproc -p $pidfile $prog - retval=$? - echo - [ $retval -eq 0 ] && rm -f $lockfile - return $retval -} - -restart() { - stop - start -} - -reload() { - restart -} - -force_reload() { - restart -} - -rh_status() { - status -p $pidfile $prog -} - -rh_status_q() { - rh_status >/dev/null 2>&1 -} - - -case "$1" in - start) - rh_status_q && exit 0 - $1 - ;; - stop) - rh_status_q || exit 0 - $1 - ;; - restart) - $1 - ;; - reload) - rh_status_q || exit 7 - $1 - ;; - force-reload) - force_reload - ;; - status) - rh_status - ;; - condrestart|try-restart) - rh_status_q || exit 0 - restart - ;; - *) - echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" - exit 2 -esac -exit $? diff --git a/chef/cookbooks/openstack-network/templates/default/quantum.conf.erb b/chef/cookbooks/openstack-network/templates/default/quantum.conf.erb deleted file mode 100644 index a0a1ba7..0000000 --- a/chef/cookbooks/openstack-network/templates/default/quantum.conf.erb +++ /dev/null @@ -1,332 +0,0 @@ -<%= node["openstack"]["network"]["custom_template_banner"] %> -[DEFAULT] -# Default log level is INFO -# verbose and debug has the same result. -# One of them will set DEBUG log level output -debug = <%= node["openstack"]["network"]["debug"] %> -verbose = <%= node["openstack"]["network"]["verbose"] %> - -# Where to store Quantum state files. This directory must be writable by the -# user executing the agent. -state_path = /var/lib/quantum - -# Where to store lock files -lock_path = $state_path/lock - -# log_format = %(asctime)s %(levelname)8s [%(name)s] %(message)s -# log_date_format = %Y-%m-%d %H:%M:%S - -# use_syslog -> syslog -# log_file and log_dir -> log_dir/log_file -<% if node["openstack"]["network"]["syslog"]["use"] %> -log_config = /etc/openstack/logging.conf -<% end %> -# (not log_file) and log_dir -> log_dir/{binary_name}.log -# use_stderr -> stderr -# (not user_stderr) and (not log_file) -> stdout -# publish_errors -> notification system - -# use_syslog = False -# syslog_log_facility = LOG_USER - -# use_stderr = True -# log_file = -# log_dir = - -# publish_errors = False - -# Address to bind the API server -bind_host = <%= @bind_address %> - -# Port the bind the API server to -bind_port = <%= @bind_port %> - -# Path to the extensions. Note that this can be a colon-separated list of -# paths. For example: -# api_extensions_path = extensions:/path/to/more/extensions:/even/more/extensions -# The __path__ of quantum.extensions is appended to this, so if your -# extensions are in there you don't need to specify them here -# api_extensions_path = - -# Quantum plugin provider module -# core_plugin = -core_plugin = <%= @core_plugin %> - -# Advanced service modules -# service_plugins = -<% if node["openstack"]["network"]["quantum_loadbalancer"] %> -# LoadBalancer plugin -service_plugins = quantum.plugins.services.agent_loadbalancer.plugin.LoadBalancerPlugin -<% end %> - -# Paste configuration file -api_paste_config = api-paste.ini - -# The strategy to be used for auth. -# Supported values are 'keystone'(default), 'noauth'. -# auth_strategy = keystone - -# Base MAC address. The first 3 octets will remain unchanged. If the -# 4h octet is not 00, it will also used. The others will be -# randomly generated. -# 3 octet -# base_mac = fa:16:3e:00:00:00 -# 4 octet -# base_mac = fa:16:3e:4f:00:00 - -# Maximum amount of retries to generate a unique MAC address -# mac_generation_retries = 16 - -# DHCP Lease duration (in seconds) -# dhcp_lease_duration = 120 - -# Allow sending resource operation notification to DHCP agent -# dhcp_agent_notification = True - -# Enable or disable bulk create/update/delete operations -# allow_bulk = True -# Enable or disable pagination -# allow_pagination = False -# Enable or disable sorting -# allow_sorting = False -# Enable or disable overlapping IPs for subnets -# Attention: the following parameter MUST be set to False if Quantum is -# being used in conjunction with nova security groups and/or metadata service. -# allow_overlapping_ips = False -# Ensure that configured gateway is on subnet -# force_gateway_on_subnet = False - - -# RPC configuration options. Defined in rpc __init__ -# The messaging module to use, defaults to kombu. -rpc_backend = quantum.openstack.common.rpc.impl_kombu -# Size of RPC thread pool -rpc_thread_pool_size = 64 -# Size of RPC connection pool -rpc_conn_pool_size = 30 -# Seconds to wait for a response from call or multicall -rpc_response_timeout = 60 -# Seconds to wait before a cast expires (TTL). Only supported by impl_zmq. -rpc_cast_timeout = 30 -# Modules of exceptions that are permitted to be recreated -# upon receiving exception data from an rpc call. -# allowed_rpc_exception_modules = quantum.openstack.common.exception, nova.exception -# AMQP exchange to connect to if using RabbitMQ or QPID -control_exchange = quantum - -# Configuration options if sending notifications via kombu rpc (these are -# the defaults) -# SSL version to use (valid only if SSL enabled) -# kombu_ssl_version = -# SSL key file (valid only if SSL enabled) -# kombu_ssl_keyfile = -# SSL cert file (valid only if SSL enabled) -# kombu_ssl_certfile = -# SSL certification authority file (valid only if SSL enabled)' -# kombu_ssl_ca_certs = - -# allow_overlapping_ips = False -allow_overlapping_ips = <%= node["openstack"]["network"]["allow_overlapping_ips"] -%> - -##### RABBITMQ ##### -rabbit_userid=<%= node["openstack"]["network"]["rabbit"]["username"] %> -rabbit_password=<%= @rabbit_pass %> -# rabbit_virtual_host=<%= node["openstack"]["network"]["rabbit"]["vhost"] %> -<% if node["openstack"]["network"]["rabbit"]["ha"] -%> -# Use HA queues in RabbitMQ (x-ha-policy: all).You need to -# wipe RabbitMQ database when changing this option. (boolean value) -rabbit_hosts=<%= @rabbit_hosts %> -rabbit_ha_queues=True -<% else -%> -rabbit_host=<%= node["openstack"]["network"]["rabbit"]["host"] %> -rabbit_port=<%= node["openstack"]["network"]["rabbit"]["port"] %> -<% end -%> -# Maximum retries with trying to connect to RabbitMQ -# (the default of 0 implies an infinite retry count) -# rabbit_max_retries = 0 -# RabbitMQ connection retry interval -# rabbit_retry_interval = 1 - -# QPID -# rpc_backend=quantum.openstack.common.rpc.impl_qpid -# Qpid broker hostname -# qpid_hostname = localhost -# Qpid broker port -# qpid_port = 5672 -# Qpid single or HA cluster (host:port pairs i.e: host1:5672, host2:5672) -# qpid_hosts is defaulted to '$qpid_hostname:$qpid_port' -# qpid_hosts = localhost:5672 -# Username for qpid connection -# qpid_username = '' -# Password for qpid connection -# qpid_password = '' -# Space separated list of SASL mechanisms to use for auth -# qpid_sasl_mechanisms = '' -# Seconds between connection keepalive heartbeats -# qpid_heartbeat = 60 -# Transport to use, either 'tcp' or 'ssl' -# qpid_protocol = tcp -# Disable Nagle algorithm -# qpid_tcp_nodelay = True - -# ZMQ -# rpc_backend=quantum.openstack.common.rpc.impl_zmq -# ZeroMQ bind address. Should be a wildcard (*), an ethernet interface, or IP. -# The "host" option should point or resolve to this address. -# rpc_zmq_bind_address = * - -# ============ Notification System Options ===================== - -# Notifications can be sent when network/subnet/port are create, updated or deleted. -# There are three methods of sending notifications: logging (via the -# log_file directive), rpc (via a message queue) and -# noop (no notifications sent, the default) - -# Notification_driver can be defined multiple times -# Do nothing driver -# notification_driver = quantum.openstack.common.notifier.no_op_notifier -# Logging driver -# notification_driver = quantum.openstack.common.notifier.log_notifier -# RPC driver. DHCP agents needs it. -notification_driver = quantum.openstack.common.notifier.rpc_notifier - -# default_notification_level is used to form actual topic name(s) or to set logging level -default_notification_level = INFO - -# default_publisher_id is a part of the notification payload -# host = myhost.com -# default_publisher_id = $host - -# Defined in rpc_notifier, can be comma separated values. -# The actual topic names will be %s.%(default_notification_level)s -notification_topics = notifications - -# Default maximum number of items returned in a single response, -# value == infinite and value < 0 means no max limit, and value must -# greater than 0. If the number of items requested is greater than -# pagination_max_limit, server will just return pagination_max_limit -# of number of items. -# pagination_max_limit = -1 - -# Maximum number of DNS nameservers per subnet -# max_dns_nameservers = 5 - -# Maximum number of host routes per subnet -# max_subnet_host_routes = 20 - -# Maximum number of fixed ips per port -# max_fixed_ips_per_port = 5 - -# =========== items for agent management extension ============= -# Seconds to regard the agent as down. -agent_down_time = 10 - -# =========== end of items for agent management extension ===== - -# =========== items for agent scheduler extension ============= -# Driver to use for scheduling network to DHCP agent -# network_scheduler_driver = <%= node["openstack"]["network"]["dhcp"]["scheduler"] %> -# Driver to use for scheduling router to a default L3 agent -# router_scheduler_driver = <%= node["openstack"]["network"]["l3"]["scheduler"] %> -# why? - - -# Allow auto scheduling networks to DHCP agent. It will schedule non-hosted -# networks to first DHCP agent which sends get_active_networks message to -# quantum server -# network_auto_schedule = True - -# Allow auto scheduling routers to L3 agent. It will schedule non-hosted -# routers to first L3 agent which sends sync_routers message to quantum server -# router_auto_schedule = True -# =========== end of items for agent scheduler extension ===== - -# =========== WSGI parameters related to the API server ============== -# Sets the value of TCP_KEEPIDLE in seconds to use for each server socket when -# starting API server. Not supported on OS X. -#tcp_keepidle = 600 - -# Number of seconds to keep retrying to listen -#retry_until_window = 30 - -# Number of backlog requests to configure the socket with. -#backlog = 4096 - -# Enable SSL on the API server -#use_ssl = False - -# Certificate file to use when starting API server securely -#ssl_cert_file = /path/to/certfile - -# Private key file to use when starting API server securely -#ssl_key_file = /path/to/keyfile - -# CA certificate file to use when starting API server securely to -# verify connecting clients. This is an optional parameter only required if -# API clients need to authenticate to the API server using SSL certificates -# signed by a trusted CA -#ssl_ca_file = /path/to/cafile -# ======== end of WSGI parameters related to the API server ========== - -[QUOTAS] -# resource name(s) that are supported in quota features -quota_items = network,subnet,port - -# default number of resource allowed per tenant, minus for unlimited -default_quota = -1 - -# number of networks allowed per tenant, and minus means unlimited -quota_network = 100 - -# number of subnets allowed per tenant, and minus means unlimited -quota_subnet = 100 - -# number of ports allowed per tenant, and minus means unlimited -quota_port = 1000 - -# number of security groups allowed per tenant, and minus means unlimited -quota_security_group = 100 - -# number of security group rules allowed per tenant, and minus means unlimited -quota_security_group_rule = 200 - -# default driver to use for quota checks -# quota_driver = quantum.quota.ConfDriver -quota_driver = quantum.db.quota_db.DbQuotaDriver - -[DEFAULT_SERVICETYPE] -# Description of the default service type (optional) -# description = "default service type" -# Enter a service definition line for each advanced service provided -# by the default service type. -# Each service definition should be in the following format: -# :[:driver] - -[AGENT] -# Use "sudo quantum-rootwrap /etc/quantum/rootwrap.conf" to use the real -# root filter facility. -# Change to "sudo" to skip the filtering and just run the comand directly -# root_helper = sudo -<% if node["openstack"]["network"]["use_rootwrap"] %> -root_helper = "sudo quantum-rootwrap /etc/quantum/rootwrap.conf" -<% end -%> - -# =========== items for agent management extension ============= -# seconds between nodes reporting state to server, should be less than -# agent_down_time -report_interval = <%= node["openstack"]["network"]["api"]["agent"]["agent_report_interval"] %> - -[keystone_authtoken] -auth_host = <%= @identity_endpoint.host %> -auth_port = <%= @identity_endpoint.port %> -auth_protocol = <%= @identity_endpoint.scheme %> -admin_tenant_name = <%= @service_tenant_name %> -admin_user = <%= @service_user %> -admin_password = <%= @service_pass %> -signing_dir = <%= node["openstack"]["network"]["api"]["agent"]["signing_dir"] %> - -# =========== end of items for agent management extension ===== - -[database] -#sql_max_pool_size=100 -#sqlalchemy_pool_size = 100 diff --git a/chef/cookbooks/openstack-network/templates/default/rootwrap.conf.erb b/chef/cookbooks/openstack-network/templates/default/rootwrap.conf.erb index 234d281..e278b4a 100644 --- a/chef/cookbooks/openstack-network/templates/default/rootwrap.conf.erb +++ b/chef/cookbooks/openstack-network/templates/default/rootwrap.conf.erb @@ -3,4 +3,4 @@ [DEFAULT] # List of directories to load filter definitions from (separated by ','). # These directories MUST all be only writeable by root ! -filters_path=/etc/quantum/rootwrap.d,/usr/share/quantum/rootwrap +filters_path=/etc/neutron/rootwrap.d,/usr/share/neutron/rootwrap diff --git a/chef/cookbooks/openstack-object-storage/.rubocop.yml b/chef/cookbooks/openstack-object-storage/.rubocop.yml new file mode 100644 index 0000000..fda4268 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-object-storage/Berksfile b/chef/cookbooks/openstack-object-storage/Berksfile index ece8d4f..365ea5c 100644 --- a/chef/cookbooks/openstack-object-storage/Berksfile +++ b/chef/cookbooks/openstack-object-storage/Berksfile @@ -1,4 +1,6 @@ metadata cookbook "statsd", - :git => "git://github.com/att-cloud/cookbook-statsd.git" + git: "git://github.com/att-cloud/cookbook-statsd.git" +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" diff --git a/chef/cookbooks/openstack-object-storage/Berksfile.lock b/chef/cookbooks/openstack-object-storage/Berksfile.lock deleted file mode 100644 index f244cd2..0000000 --- a/chef/cookbooks/openstack-object-storage/Berksfile.lock +++ /dev/null @@ -1,42 +0,0 @@ -{ - "sources": { - "openstack-object-storage": { - "path": "." - }, - "statsd": { - "locked_version": "0.1.3", - "git": "git://github.com/att-cloud/cookbook-statsd.git", - "ref": "f759cd013c0a836f2acb219b3e006ff0a1308878" - }, - "memcached": { - "locked_version": "1.4.0" - }, - "runit": { - "locked_version": "1.1.6" - }, - "build-essential": { - "locked_version": "1.4.0" - }, - "yum": { - "locked_version": "2.3.0" - }, - "sysctl": { - "locked_version": "0.3.3" - }, - "apt": { - "locked_version": "2.1.0" - }, - "git": { - "locked_version": "2.5.2" - }, - "dmg": { - "locked_version": "1.1.0" - }, - "windows": { - "locked_version": "1.10.0" - }, - "chef_handler": { - "locked_version": "1.1.4" - } - } -} diff --git a/chef/cookbooks/openstack-object-storage/CHANGELOG.md b/chef/cookbooks/openstack-object-storage/CHANGELOG.md new file mode 100644 index 0000000..f57d73a --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/CHANGELOG.md @@ -0,0 +1,76 @@ +# CHANGELOG for cookbook-openstack-object-storage + +This file is used to list changes made in each version of cookbook-openstack-object-storage. +## 9.0.3 +* Bugfix run_command exitstatus + +## 9.0.2 +* Fix package action to allow updates + +## 9.0.1 +* Fix package reference, need keystone client not keystone + +## 9.0.0 +* Upgrade to Icehouse + +## 8.3.0: +* Rename all attributes to conform to other cookbooks. +* bugfix in mount lwrp that broke deployment. + +## 8.2.0: +* Add support for several swift modules: + domain_remap, formpost + +## 8.1.0 +* Add client recipe + +## 8.0.1 +* Bugfix ring_script.rb to handle recent swift-ring-builder + output with replication information + +## 8.0.0 +### New version +* Upgrade to upstream Havana release + +## 7.1.0: +* Update apt sources to grizzly to prepare for grizzly + and havana branches + +## 7.0.11: +* Add missing swift-container-sync upstart service which is + not setup by default in ubuntu 12.04 packages + +## 7.0.10: +* Do not role restrict super_admin_key in proxy config +* Case correct swauth_version attribute in proxy recipe +* Treat platform_options["swauth_packages"] as a list + +## 7.0.9: +* Bugfix tempurl role restriction + +## 7.0.8: +* Bugfix allow_override spacing in proxy server template + +## 7.0.7: +* Add flexibility to middleware pipeline + +## 7.0.6: +* Add choice of install python-swauth from git or package + +## 7.0.5: +* Add support for container-sync + +## 7.0.4: +* Allow roles used in searches to be defined by cookbook user + +## 7.0.3: +* Bugfix the swift-ring-builder output scanner + +## 7.0.2: +* Expand statsd support as well as capacity and recon supporting. + +## 7.0.1: +* Support more then 24 disks (/dev/sdaa, /dev/vdab, etc) + +## 7.0.0: +* Initial openstack object storage cookbook diff --git a/chef/cookbooks/openstack-object-storage/Gemfile b/chef/cookbooks/openstack-object-storage/Gemfile index d592402..ccfa456 100644 --- a/chef/cookbooks/openstack-object-storage/Gemfile +++ b/chef/cookbooks/openstack-object-storage/Gemfile @@ -1,9 +1,9 @@ -source "https://rubygems.org" +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 2.0.8" -gem "chefspec", "~> 1.3.0" -gem "foodcritic" -gem "strainer" -gem "webmock", "~> 1.11.0" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' diff --git a/chef/cookbooks/openstack-object-storage/Gemfile.lock b/chef/cookbooks/openstack-object-storage/Gemfile.lock index f69004d..2954927 100644 --- a/chef/cookbooks/openstack-object-storage/Gemfile.lock +++ b/chef/cookbooks/openstack-object-storage/Gemfile.lock @@ -1,30 +1,36 @@ GEM remote: https://rubygems.org/ specs: - activesupport (3.2.14) + activesupport (3.2.17) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.3.5) - akami (1.2.0) + addressable (2.3.6) + akami (1.2.1) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - berkshelf (2.0.8) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) activesupport (~> 3.2.0) addressable (~> 2.3.4) buff-shell_out (~> 0.1) - celluloid (>= 0.14.0) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) minitar (~> 0.5.4) rbzip2 (~> 0.2.0) retryable (~> 1.3.3) - ridley (~> 1.2.1) + ridley (~> 1.5.0) solve (>= 0.5.0) thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) buff-ruby_engine (0.1.0) - buff-shell_out (0.1.0) + buff-shell_out (0.1.1) buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) @@ -32,112 +38,127 @@ GEM celluloid-io (0.14.1) celluloid (>= 0.14.1) nio4r (>= 0.4.5) - chef (11.4.4) - erubis - highline (>= 1.6.9) - json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) net-ssh (~> 2.6) - net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (1.3.1) - chef (>= 10.0) - erubis - fauxhai (>= 0.1.1, < 2.0) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.9.0) - builder (>= 2.1.2) - crack (0.4.1) - safe_yaml (~> 0.9.0) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.8) + faraday (0.8.9) multipart-post (~> 1.2.0) - fauxhai (1.1.1) - httparty + fauxhai (2.1.0) net-ssh ohai - ffi (1.9.0) - foodcritic (2.2.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.1.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.5) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - mime-types (1.24) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.5) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.2.0) - multi_json (1.7.9) - multi_xml (0.5.5) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) multipart-post (1.2.0) - net-http-persistent (2.9) - net-ssh (2.6.8) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - net-ssh-multi (1.1) - net-ssh (>= 2.1.4) - net-ssh-gateway (>= 0.99.0) - nio4r (0.5.0) - nokogiri (1.5.10) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.18.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) + rainbow (2.0.0) + rake (10.2.2) rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (1.2.5) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) buff-extensions (~> 0.3) + buff-ignore (~> 1.1) buff-shell_out (~> 0.1) celluloid (~> 0.14.0) celluloid-io (~> 0.14.0) @@ -148,6 +169,7 @@ GEM mixlib-authentication (>= 1.3.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) varia_model (~> 0.1) @@ -156,12 +178,16 @@ GEM rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - rspec-core (2.14.5) - rspec-expectations (2.14.2) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.3) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) - safe_yaml (0.9.5) savon (0.9.5) akami (~> 1.0) builder (>= 2.1.2) @@ -170,29 +196,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.8.0) - strainer (3.2.2) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) thor (0.18.1) - timers (1.1.0) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) uuidtools (2.1.4) - varia_model (0.2.0) + varia_model (0.3.2) buff-extensions (~> 0.2) hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - webmock (1.11.0) - addressable (>= 2.2.7) - crack (>= 0.3.2) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -202,10 +228,10 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 2.0.8) - chef (~> 11.4.4) - chefspec (~> 1.3.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - webmock (~> 1.11.0) diff --git a/chef/cookbooks/openstack-object-storage/README.md b/chef/cookbooks/openstack-object-storage/README.md index 5fb48a8..9134c35 100644 --- a/chef/cookbooks/openstack-object-storage/README.md +++ b/chef/cookbooks/openstack-object-storage/README.md @@ -57,12 +57,24 @@ In really really huge environments, it's possible that the storage node will be split into swift-{container,accout}-server nodes and swift-object-server nodes. +Recipes +======= + +client +---- +- Install the swift client packages Attributes ========== * ```default[:swift][:authmode]``` - "swauth" or "keystone" (default "swauth"). Right now, only swauth is supported (defaults to swauth) + * ```default[:swift][:swauth_source]``` - "git" or "package"(default). Selects between installing python-swauth from git or system package + + * ```default[:swift][:swauth_repository]``` - Specifies git repo. Default "https://github.com/gholt/swauth.git" + + * ```default[:swift][:swauth_version]``` - Specifies git repo tagged branch. Default "1.0.8" + * ```default[:swift][:swift_secret_databag_name]``` - this cookbook supports an optional secret databag where we will retrieve the following attributes overriding any default attributes below. (defaults to nil) ``` @@ -145,6 +157,38 @@ on storage nodes: * ```default[:swift][:network][:object-cidr]``` - the CIDR network for your object servers in order to build the ring (defaults to 10.0.0.0/24) +Proxy Plugins +============= + +Formpost +------- + + * ```default[:swift][:formpost][:enabled]``` - optionally enable the formpost proxy plugin (defaults to false) + +TempURL +------- + + * ```default[:swift][:tempurl][:enabled]``` - optionally enable the tempurl proxy plugin (defaults to false) + * ```default[:swift][:tempurl][:incoming_remove_headers]``` - The headers to remove from incoming requests (defaults to x-timestamp) + * ```default[:swift][:tempurl][:incoming_allow_headers]``` - The headers allowed as exceptions to incoming_remove_headers (defaults to empty string) + * ```default[:swift][:tempurl][:incoming_allow_headers]``` - The headers allowed as exceptions to incoming_remove_headers (defaults to empty string) + * ```default[:swift][:tempurl][:outgoing_remove_headers]``` - The headers to remove from outgoing responses (defaults to x-object-meta-*) + * ```default[:swift][:tempurl][:outgoing_allow_headers]``` - The headers allowed as exceptions to outgoing_remove_headers (defaults x-object-meta-public-*) + +Domain Remap +------------ + + * ```default[:swift][:domain_remap][:enabled]``` - optionally enable the domain remap proxy plugin (defaults to false) + * ```default[:swift][:domain_remap][:storage_domain]``` - The domain remap reseller domain (defaults to example.com) + * ```default[:swift][:domain_remap][:root_path]``` - The domain remap root path (defaults to v1) + * ```default[:swift][:domain_remap][:reseller_prefixes]``` - The domain remap reseller prefixes (defaults to AUTH) + +Staticweb +---------- + + * ```default[:swift][:staticweb][:enabled]``` - optionally enable the staticweb proxy plugin (defaults to false) + * ```default[:swift][:staticweb][:cache_timeout]``` - Seconds to cache container x-container-meta-web-* header values (defaults to 300) + Examples ======== @@ -229,32 +273,33 @@ Standalone Proxy Server Testing ======= -This cookbook is using [ChefSpec](https://github.com/acrmp/chefspec) for testing. Run the following before commiting. It will run your tests, and check for lint errors. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. - $ ./run_tests.bash +Berkshelf +===== -There is also a Vagrant test environment that you can launch in order to integration -test this cookbook. See the tests/README.md file for more information on launching the environment. - -Testing -======= - - $ bundle install - $ bundle exec berks install - $ bundle exec strainer test +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== | | | |:---------------------|:---------------------------------------------------| -| **Authors** | Alan Meadows () | -| | Oisin Feely () | -| | Ron Pedde () | -| | Will Kelly () | +| **Author** | Alan Meadows () | +| **Author** | Oisin Feeley () | +| **Author** | Ron Pedde () | +| **Author** | Will Kelly () | +| **Author** | Chen Zhiwei () | +| **Author** | Mark Vanderwiel () | | | | | **Copyright** | Copyright (c) 2013, AT&T, Inc. | -| | Copyright (c) 2012, Rackspace US, Inc. | +| **Copyright** | Copyright (c) 2012, Rackspace US, Inc. | +| **Copyright** | Copyright (c) 2013, IBM, Corp. | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/openstack-object-storage/Strainerfile b/chef/cookbooks/openstack-object-storage/Strainerfile index f23cd14..44e3e14 100644 --- a/chef/cookbooks/openstack-object-storage/Strainerfile +++ b/chef/cookbooks/openstack-object-storage/Strainerfile @@ -1,4 +1,5 @@ # Strainerfile +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK -chefspec: bundle exec rspec $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-object-storage/TESTING.md b/chef/cookbooks/openstack-object-storage/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-object-storage/attributes/default.rb b/chef/cookbooks/openstack-object-storage/attributes/default.rb index a52f152..a435990 100644 --- a/chef/cookbooks/openstack-object-storage/attributes/default.rb +++ b/chef/cookbooks/openstack-object-storage/attributes/default.rb @@ -1,17 +1,19 @@ +# encoding: UTF-8 +# TODO(chrislaco) This file needs the stock chef header/copyright #-------------------- # node/ring settings #-------------------- -default["swift"]["state"] = {} -default["swift"]["swift_hash"] = "107c0568ea84" -default["swift"]["audit_hour"] = "5" -default["swift"]["disk_enum_expr"] = "node[:block_device]" -default["swift"]["auto_rebuild_rings"] = false -default["swift"]["git_builder_ip"] = "127.0.0.1" +default['openstack']['object-storage']['state'] = {} +default['openstack']['object-storage']['swift_hash'] = '107c0568ea84' +default['openstack']['object-storage']['audit_hour'] = '5' +default['openstack']['object-storage']['disk_enum_expr'] = 'node[:block_device]' +default['openstack']['object-storage']['auto_rebuild_rings'] = false +default['openstack']['object-storage']['git_builder_ip'] = '127.0.0.1' # the release only has any effect on ubuntu, and must be # a valid release on http://ubuntu-cloud.archive.canonical.com/ubuntu -default["swift"]["release"] = "folsom" +default['openstack']['object-storage']['release'] = 'icehouse' # we support an optional secret databag where we will retrieve the # following attributes overriding any default attributes here @@ -23,37 +25,80 @@ default["swift"]["release"] = "folsom" # "dispersion_auth_user": "test:test", # "dispersion_auth_key": "test" # } -default["swift"]["swift_secret_databag_name"] = nil +default['openstack']['object-storage']['swift_secret_databag_name'] = nil + +#-------------------- +# roles +#-------------------- + +default['openstack']['object-storage']['setup_chef_role'] = 'swift-setup' +default['openstack']['object-storage']['management_server_chef_role'] = 'swift-management-server' +default['openstack']['object-storage']['proxy_server_chef_role'] = 'swift-proxy-server' +default['openstack']['object-storage']['object_server_chef_role'] = 'swift-object-server' +default['openstack']['object-storage']['account_server_chef_role'] = 'swift-account-server' +default['openstack']['object-storage']['container_server_chef_role'] = 'swift-container-server' #-------------------- # authentication #-------------------- -default["swift"]["authmode"] = "swauth" -default["swift"]["authkey"] = "test" -default["swift"]["swift_url"] = "http://127.0.0.1:8080/v1/" -default["swift"]["swauth_url"] = "http://127.0.0.1:8080/v1/" -default["swift"]["auth_url"] = "http://127.0.0.1:8080/auth/v1.0" +default['openstack']['object-storage']['authmode'] = 'swauth' +default['openstack']['object-storage']['authkey'] = 'test' +default['openstack']['object-storage']['swift_url'] = 'http://127.0.0.1:8080/v1/' +default['openstack']['object-storage']['swauth_url'] = 'http://127.0.0.1:8080/v1/' +default['openstack']['object-storage']['auth_url'] = 'http://127.0.0.1:8080/auth/v1.0' #--------------------- # dispersion settings #--------------------- -default["swift"]["dispersion"]["auth_user"] = "test:test" -default["swift"]["dispersion"]["auth_key"] = "test" - +default['openstack']['object-storage']['dispersion']['auth_user'] = 'test:test' +default['openstack']['object-storage']['dispersion']['auth_key'] = 'test' # settings for the swift ring - these default settings are # a safe setting for testing but part_power should be set to # 26 in production to allow a swift cluster with 50,000 spindles -default["swift"]["ring"]["part_power"] = 18 -default["swift"]["ring"]["min_part_hours"] = 1 -default["swift"]["ring"]["replicas"] = 3 +default['openstack']['object-storage']['ring']['part_power'] = 18 +default['openstack']['object-storage']['ring']['min_part_hours'] = 1 +default['openstack']['object-storage']['ring']['replicas'] = 3 #------------------ # statistics #------------------ -default["swift"]["enable_statistics"] = true +default['openstack']['object-storage']['statistics']['enabled'] = true +default['openstack']['object-storage']['statistics']['sample_rate'] = 1 + +# there are two ways to discover your graphite server ip for +# statsd to periodically publish to. You can directly set +# the ip below, or leave it set to nil and supply chef with +# the role name of your graphite server and the interface +# name to retrieve the appropriate internal ip address from +# +# if no servers with the role below can be found then +# 127.0.0.1 will be used +default['openstack']['object-storage']['statistics']['graphing_ip'] = nil +default['openstack']['object-storage']['statistics']['graphing_role'] = 'graphite-role' +default['openstack']['object-storage']['statistics']['graphing_interface'] = 'eth0' + +# how frequently to run chef instantiated /usr/local/bin/swift_statsd_publish.py +# which publishes dispersion and recon statistics (in minutes) +default['openstack']['object-storage']['statistics']['report_frequency'] = 15 + +# enable or disable specific portions of generated report +default['openstack']['object-storage']['statistics']['enable_dispersion_report'] = true +default['openstack']['object-storage']['statistics']['enable_recon_report'] = true +default['openstack']['object-storage']['statistics']['enable_disk_report'] = true + +# settings for statsd which should be configured to use the local +# statsd daemon that chef will install if statistics are enabled +default['openstack']['object-storage']['statistics']['statsd_host'] = '127.0.0.1' +default['openstack']['object-storage']['statistics']['statsd_port'] = '8125' +default['openstack']['object-storage']['statistics']['statsd_prefix'] = 'openstack.swift' + +# paths to the recon cache files +default['openstack']['object-storage']['statistics']['recon_account_cache'] = '/var/cache/swift/account.recon' +default['openstack']['object-storage']['statistics']['recon_container_cache'] = '/var/cache/swift/container.recon' +default['openstack']['object-storage']['statistics']['recon_object_cache'] = '/var/cache/swift/object.recon' #------------------ # network settings @@ -66,31 +111,31 @@ default["swift"]["enable_statistics"] = true # communication # proxy servers -default["swift"]["network"]["proxy-bind-ip"] = "0.0.0.0" -default["swift"]["network"]["proxy-bind-port"] = "8080" -default["swift"]["network"]["proxy-cidr"] = "10.0.0.0/24" +default['openstack']['object-storage']['network']['proxy-bind-ip'] = '0.0.0.0' +default['openstack']['object-storage']['network']['proxy-bind-port'] = '8080' +default['openstack']['object-storage']['network']['proxy-cidr'] = '10.0.0.0/24' # account servers -default["swift"]["network"]["account-bind-ip"] = "0.0.0.0" -default["swift"]["network"]["account-bind-port"] = "6002" +default['openstack']['object-storage']['network']['account-bind-ip'] = '0.0.0.0' +default['openstack']['object-storage']['network']['account-bind-port'] = '6002' # container servers -default["swift"]["network"]["container-bind-ip"] = "0.0.0.0" -default["swift"]["network"]["container-bind-port"] = "6001" +default['openstack']['object-storage']['network']['container-bind-ip'] = '0.0.0.0' +default['openstack']['object-storage']['network']['container-bind-port'] = '6001' # object servers -default["swift"]["network"]["object-bind-ip"] = "0.0.0.0" -default["swift"]["network"]["object-bind-port"] = "6000" -default["swift"]["network"]["object-cidr"] = "10.0.0.0/24" +default['openstack']['object-storage']['network']['object-bind-ip'] = '0.0.0.0' +default['openstack']['object-storage']['network']['object-bind-port'] = '6000' +default['openstack']['object-storage']['network']['object-cidr'] = '10.0.0.0/24' #------------------ # sysctl #------------------ # set sysctl properties for time waits -default['sysctl']['params']['net']['ipv4']['tcp_tw_recycle'] = 1 -default['sysctl']['params']['net']['ipv4']['tcp_tw_reuse'] = 1 -default['sysctl']['params']['net']['ipv4']['tcp_syncookies'] = 0 +default['openstack']['sysctl']['net.ipv4.tcp_tw_recycle'] = 1 +default['openstack']['sysctl']['net.ipv4.tcp_tw_reuse'] = 1 +default['openstack']['sysctl']['net.ipv4.tcp_syncookies'] = 0 # N.B. conntrack_max may also need to be adjusted if # server is running a stateful firewall @@ -104,91 +149,204 @@ default['sysctl']['params']['net']['ipv4']['tcp_syncookies'] = 0 # Each predicate is evaluated in turn, and a false from the predicate # will result in the disk not being considered as a candidate for # formatting. -default["swift"]["disk_test_filter"] = [ "candidate =~ /(sd|hd|xvd|vd)(?!a$)[a-z]+/", - "File.exist?('/dev/' + candidate)", - "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", - "not info.has_key?('removable') or info['removable'] == 0.to_s" ] +default['openstack']['object-storage']['disk_test_filter'] = [ + 'candidate =~ /(sd|hd|xvd|vd)(?!a$)[a-z]+/', + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] + +#------------------- +# template overrides +#------------------- + +# proxy-server + +# enable or disable formpost +default['openstack']['object-storage']['formpost']['enabled'] = false + +# enable or disable tempurl +default['openstack']['object-storage']['tempurl']['enabled'] = false + +# The headers to remove from incoming requests. Simply a whitespace delimited +# list of header names and names can optionally end with '*' to indicate a +# prefix match. incoming_allow_headers is a list of exceptions to these +# removals. +default['openstack']['object-storage']['tempurl']['incoming_remove_headers'] = 'x-timestamp' + +# The headers allowed as exceptions to incoming_remove_headers. Simply a +# whitespace delimited list of header names and names can optionally end with +# '*' to indicate a prefix match. +default['openstack']['object-storage']['tempurl']['incoming_allow_headers'] = '' + +# The headers to remove from outgoing responses. Simply a whitespace delimited +# list of header names and names can optionally end with '*' to indicate a +# prefix match. outgoing_allow_headers is a list of exceptions to these +# removals. +default['openstack']['object-storage']['tempurl']['outgoing_remove_headers'] = 'x-object-meta-*' + +# The headers allowed as exceptions to outgoing_remove_headers. Simply a +# whitespace delimited list of header names and names can optionally end with +# '*' to indicate a prefix match. +default['openstack']['object-storage']['tempurl']['outgoing_allow_headers'] = 'x-object-meta-public-*' + +# enable or disable domain_remap +default['openstack']['object-storage']['domain_remap']['enabled'] = false + +# enable domain log name +default['openstack']['object-storage']['domain_remap']['log_name'] = 'domain_remap' + +# domain remap log facilty +default['openstack']['object-storage']['domain_remap']['log_facility'] = 'LOG_LOCAL0' + +# domain remap log level +default['openstack']['object-storage']['domain_remap']['log_level'] = 'INFO' + +# domain remap log headers +default['openstack']['object-storage']['domain_remap']['log_headers'] = 'False' + +# domain remap reseller domain +default['openstack']['object-storage']['domain_remap']['storage_domain'] = 'example.com' + +# domain remap root path +default['openstack']['object-storage']['domain_remap']['path_root'] = 'v1' + +# domain remap reseller prefixes +default['openstack']['object-storage']['domain_remap']['reseller_prefixes'] = 'AUTH' + +# whether or not to enable staticweb in the swift proxy +default['openstack']['object-storage']['staticweb']['enabled'] = false + +# Seconds to cache container x-container-meta-web-* header values. +default['openstack']['object-storage']['staticweb']['cache_timeout'] = 300 + +# staticweb logging options +default['openstack']['object-storage']['staticweb']['log_facility'] = 'LOG_LOCAL0' +default['openstack']['object-storage']['staticweb']['log_level'] = 'INFO' +default['openstack']['object-storage']['staticweb']['access_log_name'] = 'staticweb' +default['openstack']['object-storage']['staticweb']['access_log_facility'] = 'LOG_LOCAL0' +default['openstack']['object-storage']['staticweb']['access_log_level'] = 'INFO' +default['openstack']['object-storage']['staticweb']['log_headers'] = 'False' + +# container-server + +# Override this with an allowed list of your various swift clusters if you wish +# to enable container sync for your end-users between clusters. This should +# be an array of fqdn hostnames for the cluster end-points that your end-users +# would access in the format of ['host1', 'host2', 'host3'] +default['openstack']['object-storage']['container-server']['allowed_sync_hosts'] = [] + +# container-sync logging settings +default['openstack']['object-storage']['container-server']['container-sync']['log_name'] = 'container-sync' +default['openstack']['object-storage']['container-server']['container-sync']['log_facility'] = 'LOG_LOCAL0' +default['openstack']['object-storage']['container-server']['container-sync']['log_level'] = 'INFO' + +# If you need to use an HTTP Proxy, set it here; defaults to no proxy. +default['openstack']['object-storage']['container-server']['container-sync']['sync_proxy'] = nil + +# Will sync, at most, each container once per interval (in seconds) +default['openstack']['object-storage']['container-server']['container-sync']['interval'] = 300 + +# Maximum amount of time to spend syncing each container per pass (in seconds) +default['openstack']['object-storage']['container-server']['container-sync']['container_time'] = 60 + +#------------------ +# swauth source +# ----------------- +# Versions of swauth in Ubuntu Cloud Archive PPA can be outdated. This +# allows us to chose to install directly from a tagged branch of +# gholt's repository. +# values: package, git +default['openstack']['object-storage']['swauth_source'] = 'package' +default['openstack']['object-storage']['swauth_repository'] = 'https://github.com/gholt/swauth.git' +default['openstack']['object-storage']['swauth_version'] = '1.0.8' #------------------ # packages #------------------ - # Leveling between distros case platform -when "redhat" - default["swift"]["platform"] = { - "disk_format" => "ext4", - "proxy_packages" => ["openstack-swift-proxy", "sudo", "cronie", "python-memcached"], - "object_packages" => ["openstack-swift-object", "sudo", "cronie"], - "container_packages" => ["openstack-swift-container", "sudo", "cronie"], - "account_packages" => ["openstack-swift-account", "sudo", "cronie"], - "swift_packages" => ["openstack-swift", "sudo", "cronie"], - "swauth_packages" => ["openstack-swauth", "sudo", "cronie"], - "rsync_packages" => ["rsync"], - "git_packages" => ["xinetd", "git", "git-daemon"], - "service_prefix" => "openstack-", - "service_suffix" => "", - "git_dir" => "/var/lib/git", - "git_service" => "git", - "service_provider" => Chef::Provider::Service::Redhat, - "override_options" => "" +when 'redhat' + default['openstack']['object-storage']['platform'] = { + 'disk_format' => 'ext4', + 'proxy_packages' => %w{openstack-swift-proxy sudo cronie python-memcached}, + 'object_packages' => ['openstack-swift-object', 'sudo', 'cronie'], + 'container_packages' => ['openstack-swift-container', 'sudo', 'cronie'], + 'account_packages' => ['openstack-swift-account', 'sudo', 'cronie'], + 'swift_packages' => ['openstack-swift', 'sudo', 'cronie'], + 'swift_client_packages' => ['python-swiftclient'], + 'swauth_packages' => ['openstack-swauth', 'sudo', 'cronie'], + 'rsync_packages' => ['rsync'], + 'git_packages' => ['xinetd', 'git', 'git-daemon'], + 'service_prefix' => 'openstack-', + 'service_suffix' => '', + 'git_dir' => '/var/lib/git', + 'git_service' => 'git', + 'service_provider' => Chef::Provider::Service::Redhat, + 'override_options' => '', + 'swift_statsd_publish' => '/usr/bin/swift-statsd-publish.py' } # # python-iso8601 is a missing dependency for swift. # https://bugzilla.redhat.com/show_bug.cgi?id=875948 -when "centos" - default["swift"]["platform"] = { - "disk_format" => "xfs", - "proxy_packages" => ["openstack-swift-proxy", "sudo", "cronie", "python-iso8601", "python-memcached" ], - "object_packages" => ["openstack-swift-object", "sudo", "cronie", "python-iso8601" ], - "container_packages" => ["openstack-swift-container", "sudo", "cronie", "python-iso8601" ], - "account_packages" => ["openstack-swift-account", "sudo", "cronie", "python-iso8601" ], - "swift_packages" => ["openstack-swift", "sudo", "cronie", "python-iso8601" ], - "swauth_packages" => ["openstack-swauth", "sudo", "cronie", "python-iso8601" ], - "rsync_packages" => ["rsync"], - "git_packages" => ["xinetd", "git", "git-daemon"], - "service_prefix" => "openstack-", - "service_suffix" => "", - "git_dir" => "/var/lib/git", - "git_service" => "git", - "service_provider" => Chef::Provider::Service::Redhat, - "override_options" => "" +when 'centos' + default['openstack']['object-storage']['platform'] = { + 'disk_format' => 'xfs', + 'proxy_packages' => %w{openstack-swift-proxy sudo cronie python-iso8601 python-memcached}, + 'object_packages' => %w{openstack-swift-object sudo cronie python-iso8601}, + 'container_packages' => %w{openstack-swift-container sudo cronie python-iso8601}, + 'account_packages' => %w{openstack-swift-account sudo cronie python-iso8601}, + 'swift_packages' => %w{openstack-swift sudo cronie python-iso8601}, + 'swift_client_packages' => ['python-swiftclient'], + 'swauth_packages' => %w{openstack-swauth sudo cronie python-iso8601}, + 'rsync_packages' => ['rsync'], + 'git_packages' => ['xinetd', 'git', 'git-daemon'], + 'service_prefix' => 'openstack-', + 'service_suffix' => '', + 'git_dir' => '/var/lib/git', + 'git_service' => 'git', + 'service_provider' => Chef::Provider::Service::Redhat, + 'override_options' => '', + 'swift_statsd_publish' => '/usr/bin/swift-statsd-publish.py' } -when "fedora" - default["swift"]["platform"] = { - "disk_format" => "xfs", - "proxy_packages" => ["openstack-swift-proxy", "python-memcached"], - "object_packages" => ["openstack-swift-object"], - "container_packages" => ["openstack-swift-container"], - "account_packages" => ["openstack-swift-account"], - "swift_packages" => ["openstack-swift"], - "swauth_packages" => ["openstack-swauth"], - "rsync_packages" => ["rsync"], - "git_packages" => ["git", "git-daemon"], - "service_prefix" => "openstack-", - "service_suffix" => ".service", - "git_dir" => "/var/lib/git", - "git_service" => "git", - "service_provider" => Chef::Provider::Service::Systemd, - "override_options" => "" +when 'fedora' + default['openstack']['object-storage']['platform'] = { + 'disk_format' => 'xfs', + 'proxy_packages' => ['openstack-swift-proxy', 'python-memcached'], + 'object_packages' => ['openstack-swift-object'], + 'container_packages' => ['openstack-swift-container'], + 'account_packages' => ['openstack-swift-account'], + 'swift_packages' => ['openstack-swift'], + 'swift_client_packages' => ['python-swiftclient'], + 'swauth_packages' => ['openstack-swauth'], + 'rsync_packages' => ['rsync'], + 'git_packages' => ['git', 'git-daemon'], + 'service_prefix' => 'openstack-', + 'service_suffix' => '.service', + 'git_dir' => '/var/lib/git', + 'git_service' => 'git', + 'service_provider' => Chef::Provider::Service::Systemd, + 'override_options' => '', + 'swift_statsd_publish' => '/usr/bin/swift-statsd-publish.py' } -when "ubuntu" - default["swift"]["platform"] = { - "disk_format" => "xfs", - "proxy_packages" => ["swift-proxy", "python-memcache"], - "object_packages" => ["swift-object"], - "container_packages" => ["swift-container"], - "account_packages" => ["swift-account", "python-swiftclient"], - "swift_packages" => ["swift"], - "swauth_packages" => ["swauth"], - "rsync_packages" => ["rsync"], - "git_packages" => ["git-daemon-sysvinit"], - "service_prefix" => "", - "service_suffix" => "", - "git_dir" => "/var/cache/git", - "git_service" => "git-daemon", - "service_provider" => Chef::Provider::Service::Upstart, - "override_options" => "-o Dpkg::Options:='--force-confold' -o Dpkg::Option:='--force-confdef'" +when 'ubuntu' + default['openstack']['object-storage']['platform'] = { + 'disk_format' => 'xfs', + 'proxy_packages' => ['swift-proxy', 'python-memcache'], + 'object_packages' => ['swift-object'], + 'container_packages' => ['swift-container'], + 'account_packages' => ['swift-account', 'python-swiftclient'], + 'swift_packages' => ['swift'], + 'swift_client_packages' => ['python-swiftclient'], + 'swauth_packages' => ['swauth'], + 'rsync_packages' => ['rsync'], + 'git_packages' => ['git-daemon-sysvinit'], + 'service_prefix' => '', + 'service_suffix' => '', + 'git_dir' => '/var/cache/git', + 'git_service' => 'git-daemon', + 'service_provider' => Chef::Provider::Service::Upstart, + 'override_options' => "-o Dpkg::Options:='--force-confold' -o Dpkg::Option:='--force-confdef'", + 'swift_statsd_publish' => '/usr/local/bin/swift-statsd-publish.py' } end diff --git a/chef/cookbooks/openstack-object-storage/files/default/git-daemon.default b/chef/cookbooks/openstack-object-storage/files/default/git-daemon.default new file mode 100644 index 0000000..cb97a41 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/files/default/git-daemon.default @@ -0,0 +1,16 @@ +# Defaults for git-daemon initscript +# sourced by /etc/init.d/git-daemon +# installed at /etc/default/git-daemon by the maintainer scripts + +# +# This is a POSIX shell fragment +# + +GIT_DAEMON_ENABLE=true +GIT_DAEMON_USER=gitdaemon +GIT_DAEMON_DIRECTORY=/var/cache/git +GIT_DAEMON_BASE_PATH=/var/cache/git + + +# Additional options that are passed to the Daemon. +GIT_DAEMON_OPTIONS="" diff --git a/chef/cookbooks/openstack-object-storage/files/default/swift-container-sync.conf.upstart b/chef/cookbooks/openstack-object-storage/files/default/swift-container-sync.conf.upstart new file mode 100644 index 0000000..597adb6 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/files/default/swift-container-sync.conf.upstart @@ -0,0 +1,19 @@ +# swift-container-sync - SWIFT Container Sync +# +# The swift container sync. + +description "SWIFT Container Sync" +author "Sergio Rubio " + +start on runlevel [2345] +stop on runlevel [016] + +pre-start script + if [ -f "/etc/swift/container-server.conf" ]; then + exec /usr/bin/swift-init container-sync start + else + exit 1 + fi +end script + +post-stop exec /usr/bin/swift-init container-sync stop diff --git a/chef/cookbooks/openstack-object-storage/libraries/drive_utils.rb b/chef/cookbooks/openstack-object-storage/libraries/drive_utils.rb index aef75d0..d48d380 100644 --- a/chef/cookbooks/openstack-object-storage/libraries/drive_utils.rb +++ b/chef/cookbooks/openstack-object-storage/libraries/drive_utils.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Library:: drive_utils # # Copyright 2012, Rackspace US, Inc. @@ -19,11 +20,14 @@ # Author: Ron Pedde # +# Drive Inspection Related Utilities +# rubocop:disable Eval, UselessAssignment +# TODO(chrislaco) This is a tragedy, and needs refactored module DriveUtils def locate_disks(enum_expression, filter_expressions) candidate_disks = eval(enum_expression) - candidate_expression = "candidate_disks.select{|candidate,info| (" + - filter_expressions.map{|x| "(#{x})"}.join(" and ") + ")}" + candidate_expression = 'candidate_disks.select{|candidate,info| (' + + filter_expressions.map { |x| "(#{x})" }.join(' and ') + ')}' # TODO(mancdaz): fix this properly so the above works in the first place candidate_expression.gsub!(/\[\'removable\'\] = 0/, "['removable'].to_i == 0") drives = Hash[eval(candidate_expression)] @@ -31,4 +35,3 @@ module DriveUtils drives.keys end end - diff --git a/chef/cookbooks/openstack-object-storage/libraries/ip_utils.rb b/chef/cookbooks/openstack-object-storage/libraries/ip_utils.rb index 35c9710..5ef7e4f 100644 --- a/chef/cookbooks/openstack-object-storage/libraries/ip_utils.rb +++ b/chef/cookbooks/openstack-object-storage/libraries/ip_utils.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Library:: ip_utils # # Copyright 2013, ATT Inc. @@ -19,20 +20,20 @@ # Author: Alan Meadows # -require "ipaddr" +require 'ipaddr' +# IPAddress Related Utilities module IPUtils - def locate_ip_in_cidr(network, node) + # TODO(chrislaco) This needs yanked/refactored into common/libraries/network + def locate_ip_in_cidr(network, node) # rubocop:disable MethodLength Chef::Log.debug("Searching for ip within #{network} on node #{node.name}") net = IPAddr.new(network) - node["network"]["interfaces"].each do |interface| - if interface[1].has_key?("addresses") then - interface[1]["addresses"].each do |k,v| - if v["family"] == "inet6" or (v["family"] == "inet" and v["prefixlen"] != "32") then - addr=IPAddr.new(k) - if net.include?(addr) then - return k - end + node['network']['interfaces'].each do |interface| + if interface[1].key?('addresses') + interface[1]['addresses'].each do |k, v| + if v['family'] == 'inet6' || (v['family'] == 'inet' && v['prefixlen'] != '32') + addr = IPAddr.new(k) + return k if net.include?(addr) end end end @@ -40,6 +41,6 @@ module IPUtils error = "Can't find address within network #{network} for node #{node.name}" Chef::Log.error(error) - raise error + fail error end end diff --git a/chef/cookbooks/openstack-object-storage/metadata.rb b/chef/cookbooks/openstack-object-storage/metadata.rb index 1022bc7..e27abf6 100644 --- a/chef/cookbooks/openstack-object-storage/metadata.rb +++ b/chef/cookbooks/openstack-object-storage/metadata.rb @@ -1,19 +1,22 @@ -name "openstack-object-storage" -maintainer "ATT, Inc." -license "Apache 2.0" -description "Installs and configures Openstack Swift" +name 'openstack-object-storage' +maintainer 'ATT, Inc.' +license 'Apache 2.0' +description 'Installs and configures Openstack Swift' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "1.1.0" -recipe "openstack-object-storage::account-server", "Installs the swift account server" -recipe "openstack-object-storage::object-server", "Installs the swift object server" -recipe "openstack-object-storage::proxy-server", "Installs the swift proxy server" -recipe "openstack-object-storage::container-server", "Installs the swift container server" +version '9.0.3' + +recipe 'openstack-object-storage::account-server', 'Installs the swift account server' +recipe 'openstack-object-storage::client', 'Install the switch client' +recipe 'openstack-object-storage::container-server', 'Installs the swift container server' +recipe 'openstack-object-storage::object-server', 'Installs the swift object server' +recipe 'openstack-object-storage::proxy-server', 'Installs the swift proxy server' +recipe 'openstack-object-storage::setup', 'Does initial setup of a swift cluster' %w{ centos ubuntu }.each do |os| supports os end -depends "memcached" -depends "sysctl" -depends "statsd" -depends "apt" +depends 'memcached', '>= 1.7.2' +depends 'statsd', '>= 0.1.5' +depends 'apt', '>= 2.3.8' +depends 'openstack-common', '~> 9.0' diff --git a/chef/cookbooks/openstack-object-storage/providers/disk.rb b/chef/cookbooks/openstack-object-storage/providers/disk.rb index b4c8433..58c2ee8 100644 --- a/chef/cookbooks/openstack-object-storage/providers/disk.rb +++ b/chef/cookbooks/openstack-object-storage/providers/disk.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Copyright 2011, Dell # @@ -19,46 +20,45 @@ require 'chef/mixin/shell_out' include Chef::Mixin::ShellOut +# rubocop:disable CyclomaticComplexity, MethodLength def load_current_resource dev_name = @new_resource.name - @current = Chef::Resource::SwiftDisk.new(dev_name) + @current = Chef::Resource::OpenstackObjectStorageDisk.new(dev_name) parted_partition_parse dev_name - parts = @current.part() + parts = @current.part - if not @current.blocks + unless @current.blocks # parted didn't return anything -- empty disk. # get size from sfdisk sfdisk_get_size(dev_name) end - Chef::Log.info("About to print partition table") + Chef::Log.info('About to print partition table') s = < part_num, - :start => Regexp.last_match(2).to_i / 1024, - :end => Regexp.last_match(3).to_i / 1024, - :size => Regexp.last_match(4).to_i / 1024, - :type => Regexp.last_match(5), - :system => Regexp.last_match(6), - :flags => Regexp.last_match(7) } + num: part_num, + start: Regexp.last_match(2).to_i / 1024, + end: Regexp.last_match(3).to_i / 1024, + size: Regexp.last_match(4).to_i / 1024, + type: Regexp.last_match(5), + system: Regexp.last_match(6), + flags: Regexp.last_match(7) + } part_tab << part_info - } + end end @current.part(part_tab) @@ -123,6 +122,7 @@ end action :list do Chef::Log.info("at some point there'll be a list") + new_resource.updated_by_last_action(update) end #### @@ -138,34 +138,34 @@ end # Plus, then parted doesn't bitch every time you run it. action :ensure_exists do - Chef::Log.info("Entering :ensure_exists") + Chef::Log.info('Entering :ensure_exists') req = @new_resource.part cur = @current.part dev_name = @new_resource.name update = false - recreate, delete_existing = false + recreate = false - disk_blocks = @current.blocks #1k blocks + disk_blocks = @current.blocks # 1k blocks - if (cur.nil?) - recreate = true; + if cur.nil? + recreate = true else idx = 0 - current_block=0 + current_block = 0 Chef::Log.info("Checking partition #{idx}") - req.each { |params| - if (cur[idx].nil?) + req.each do |params| + if cur[idx].nil? recreate = true Chef::Log.info("no current #{idx}") next end req_size = params[:size] # size in Mb - convert to blocks - if (req_size == :remaining) + if req_size == :remaining req_size = disk_blocks - current_block else req_size = req_size * 1024 @@ -173,68 +173,64 @@ action :ensure_exists do cur_size = cur[idx][:size] - cur_min, cur_max = req_size*0.9, req_size*1.1 - if !(cur_size > cur_min and cur_size < cur_max) - recreate = true - end + cur_min, cur_max = (req_size * 0.9), (req_size * 1.1) + recreate = true unless (cur_size > cur_min) && (cur_size < cur_max) current_block += cur[idx][:size] Chef::Log.info("partition #{idx} #{(recreate ? 'differs' : 'is same')}: #{cur_size}/#{req_size}") - idx+=1 - } + idx += 1 + end end if !recreate - Chef::Log.info("partition table matches - not recreating") + Chef::Log.info('partition table matches - not recreating') else ### make sure to ensure that there are no mounted ### filesystems on the device re = /^(#{Regexp.escape(dev_name)}[0-9]+)/ mounted = [] - shell_out!("mount").stdout.each_line { |line| + shell_out!('mount').stdout.each_line do |line| md = re.match(line) next unless md mounted << md[1] - } - mounted.each { |m| + end + mounted.each do |m| Chef::Log.info("unmounting #{m}") shell_out!("umount #{m}") - } + end # Nuke current partition table. - execute "create new partition table" do + execute 'create new partition table' do command "parted -s -m #{dev_name} mktable gpt" end # create new partitions idx = 0 - req.each { | params | + req.each do |params| start_block = 0 - if idx == 0 - start_block = "1M" - end + start_block = '1M' if idx == 0 - if (params[:size] == :remaining) - requested_size = "100%" + if params[:size] == :remaining + requested_size = '100%' else requested_size = "#{params[:size]}M" end s = "parted -m -s #{dev_name} " s << "mkpart #{idx} #{start_block} #{requested_size}" # #{params[:type]} - Chef::Log.info("creating new partition #{idx+1} with:" + s) + Chef::Log.info("creating new partition #{idx + 1} with:" + s) execute "creating partition #{idx}" do command s end - idx+=1 + idx += 1 - } + end update = true end # walk through the partitions and enforce disk format - idx=1 + idx = 1 req.each do |params| device = "#{dev_name}#{idx}" Chef::Log.info("Checking #{device}") @@ -246,13 +242,13 @@ action :ensure_exists do Chef::Log.info("Testing file system on #{device} for type #{params[:type]}") case params[:type] - when "xfs" - if not system("xfs_admin -l #{device}") + when 'xfs' + unless Mixlib::ShellOut.new("xfs_admin -l #{device}").run_command.exitstatus Mixlib::ShellOut.new("mkfs.xfs -f -i size=512 #{device}").run_command update = true end - when "ext4" - if not system("tune2fs -l #{device} | grep \"Filesystem volume name:\" | awk \'{print $4}\' | grep -v \"\"") + when 'ext4' + unless Mixlib::ShellOut.new("tune2fs -l #{device} | grep \"Filesystem volume name:\" | awk \'{print $4}\' | grep -v \"\"").run_command.exitstatus Mixlib::ShellOut.new("mkfs.ext4 #{device}").run_command update = true end @@ -261,4 +257,3 @@ action :ensure_exists do end new_resource.updated_by_last_action(update) end - diff --git a/chef/cookbooks/openstack-object-storage/providers/mounts.rb b/chef/cookbooks/openstack-object-storage/providers/mounts.rb index bf2ed22..d9ec25f 100644 --- a/chef/cookbooks/openstack-object-storage/providers/mounts.rb +++ b/chef/cookbooks/openstack-object-storage/providers/mounts.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Provider:: mounts # # Copyright 2012, Rackspace US, Inc. @@ -19,57 +20,58 @@ # Author: Ron Pedde # -require "chef/util/file_edit" +require 'chef/util/file_edit' action :ensure_exists do proposed_devices = @new_resource.devices path = @new_resource.name dev_info = {} - Chef::Log.info("IN MOUNTS") + Chef::Log.info('IN MOUNTS') new_resource.updated_by_last_action(false) # walk through the devices, gathering information proposed_devices.each do |device| - next if !::File.exists?("/dev/#{device}") + next unless ::File.exists?("/dev/#{device}") info = {} - info["device"] = device - info["ip"] = @new_resource.ip - info["format"] = @new_resource.format - info["uuid"] = `blkid /dev/#{device} -s UUID -o value`.strip - info["mountpoint"] = info["uuid"].split("-").join("") - info["mounted"] = system("mount | grep '#{path}/#{info["mountpoint"]}\'") - info["size"] = `sfdisk -s /dev/#{device}`.to_i / 1024 + info['device'] = device + info['ip'] = @new_resource.ip + info['format'] = @new_resource.format + info['uuid'] = Mixlib::ShellOut.new("blkid /dev/#{device} -s UUID -o value").run_command.stdout.strip + info['mountpoint'] = info['uuid'].split('-').join('') + info['mounted'] = Mixlib::ShellOut.new("mount | grep '#{path}/#{info["mountpoint"]}\'").run_command.status + info['size'] = Mixlib::ShellOut.new("sfdisk -s /dev/#{device}").run_command.stdout.to_i / 1024 - next if (info["uuid"] == '') + next if info['uuid'] == '' - dev_info[info["uuid"]] = info + dev_info[info['uuid']] = info end - Chef::Log.info("Physical Inventory:") - dev_info.each do |_,v| + Chef::Log.info('Physical Inventory:') + dev_info.each do |_, v| Chef::Log.info("Device: #{v['device']}, UUID: #{v['uuid']}, Mounted: #{v['mounted']}, Format: #{v['format']}") end # make sure we have a "path" Directory(path) do - group "swift" - owner "swift" + group 'swift' + owner 'swift' recursive true end.run_action(:create) # find what should be mounted, and what IS mounted - mounts=node["filesystem"].inject({}) { |hsh, (k,v)| hsh.merge(v["mount"] => k) } - valid_mounts = dev_info.inject([]) {|ary, (_,v)| ary << "#{path}/#{v['mountpoint']}"} - mountpoints = Dir.new(path).reject {|x| x[/^\./] }.collect { |d| "#{path}/#{d}" } - inverted_mounts = dev_info.inject({}) {|hsh,(k,v)| hsh.merge({v["mountpoint"] => v.merge("uuid" => k)})} - fstabs=::File.readlines("/etc/fstab").inject({}) do |hash,line| - line = line.split("#")[0].split() + mounts = node['filesystem'].reduce({}) { |hsh, (k, v)| hsh.merge(v['mount'] => k) } + valid_mounts = dev_info.reduce([]) { |ary, (_, v)| ary << "#{path}/#{v['mountpoint']}" } + mountpoints = Dir.new(path).reject { |x| x[/^\./] }.map { |d| "#{path}/#{d}" } + inverted_mounts = dev_info.reduce({}) { |hsh, (k, v)| hsh.merge(v['mountpoint'] => v.merge('uuid' => k)) } + fstabs = ::File.readlines('/etc/fstab').reduce({}) do |hash, line| + line = line.split('#')[0].split Chef::Log.info("#{line[0]} ... #{line[1]}") hash.merge(line[1] => line[0]) - end.reject { |k,v| !k or !v or !k.length or !v.length } + end + fstabs.reject! { |k, v| !k || !v || !k.length || !v.length } Chef::Log.info("Mounts: #{mounts}") Chef::Log.info("Valid Mounts: #{valid_mounts}") @@ -77,16 +79,16 @@ action :ensure_exists do Chef::Log.info("Fstabs: #{fstabs}") # mounts in /srv/node that shouldn't be there - (mounts.keys.select{|x| x and x[/^#{path}/]} - valid_mounts).each do |dev| + (mounts.keys.select { |x| x && x[/^#{path}/] } - valid_mounts).each do |dev| Chef::Log.info("Unmounting #{dev}") - system("umount #{dev}") if system("mount | grep '#{dev}'") + Mixlib::ShellOut.new("umount #{dev}").run_command if Mixlib::ShellOut.new("mount | grep '#{dev}'").run_command.status new_resource.updated_by_last_action(true) end # fstab entries that don't need to be there anymore - (fstabs.keys.select {|k| k.start_with? path} - valid_mounts).each do |dev| - fe = Chef::Util::FileEdit.new("/etc/fstab") - fe.search_file_delete_line(Regexp.new(dev.gsub("/","\/"))) + (fstabs.keys.select { |k| k.start_with?(path) } - valid_mounts).each do |dev| + fe = Chef::Util::FileEdit.new('/etc/fstab') + fe.search_file_delete_line(Regexp.new(dev.gsub('/', '\/'))) fe.write_file new_resource.updated_by_last_action(true) end @@ -104,27 +106,27 @@ action :ensure_exists do # new, unmounted devices (valid_mounts - mounts.keys).each do |mountpoint| - info = inverted_mounts[mountpoint.gsub("#{path}/","")] + info = inverted_mounts[mountpoint.gsub("#{path}/", '')] Chef::Log.info("mounting #{mountpoint} (#{info['device']})") mount_path = "#{path}/#{info['mountpoint']}" Directory(mount_path) do - group "swift" - owner "swift" + group 'swift' + owner 'swift' recursive true end.run_action(:create) case info['format'] when 'ext4' - mount_options = "noatime,nodiratime,nobarrier,user_xattr" + mount_options = 'noatime,nodiratime,nobarrier,user_xattr' when 'xfs' - case node["platform"] - when "ubuntu","debian" - mount_options = "noatime,nodiratime,nobarrier,logbufs=8,nobootwait" - else - mount_options = "noatime,nodiratime,nobarrier,logbufs=8" + case node['platform'] + when 'ubuntu', 'debian' + mount_options = 'noatime,nodiratime,nobarrier,logbufs=8,nobootwait' + else + mount_options = 'noatime,nodiratime,nobarrier,logbufs=8' end end @@ -137,7 +139,7 @@ action :ensure_exists do action :nothing end - if not fstabs.has_key?(mount_path) + unless fstabs.key?(mount_path) # then its a brand-new drive, freshly formatted Chef::Log.info("Mounting new device #{info['mountpoint']}") mt.run_action(:enable) @@ -147,22 +149,21 @@ action :ensure_exists do new_resource.updated_by_last_action(true) end - dev_info.reject { |k,v| v["mounted"] }.keys.each do |uuid| - dev_info[uuid]["mounted"] = system("mount | grep '#{path}/#{dev_info[uuid]["mountpoint"]}\'") + dev_info.reject { |k, v| v['mounted'] }.keys.each do |uuid| + dev_info[uuid]['mounted'] = Mixlib::ShellOut.new("mount | grep '#{path}/#{dev_info[uuid]["mountpoint"]}\'").run_command.status end - if @new_resource.publish_attributes and dev_info != {} - dev_info.each do |k,v| - node.set["swift"]["state"]["devs"][k] = { - :device => v["device"], - :size => v["size"], - :uuid => v["uuid"], - :mounted => v["mounted"], - :format => v["format"], - :mountpoint => v["mountpoint"], - :ip => v["ip"] + if @new_resource.publish_attributes && dev_info != {} + dev_info.each do |k, v| + node.set['openstack']['object-storage']['state']['devs'][k] = { + device: v['device'], + size: v['size'], + uuid: v['uuid'], + mounted: v['mounted'], + format: v['format'], + mountpoint: v['mountpoint'], + ip: v['ip'] } end end end - diff --git a/chef/cookbooks/openstack-object-storage/providers/ring_script.rb b/chef/cookbooks/openstack-object-storage/providers/ring_script.rb index 7218a39..d4eaa01 100644 --- a/chef/cookbooks/openstack-object-storage/providers/ring_script.rb +++ b/chef/cookbooks/openstack-object-storage/providers/ring_script.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Resource:: ring_script # # Copyright 2012, Rackspace US, Inc. @@ -19,19 +20,20 @@ # Author: Ron Pedde # -require "pp" +require 'pp' +# rubocop:disable PerlBackrefs, CyclomaticComplexity, MethodLength def generate_script # need to load and parse the existing rings. - ports = { "object" => "6000", "container" => "6001", "account" => "6002" } + ports = { 'object' => '6000', 'container' => '6001', 'account' => '6002' } must_rebalance = false ring_path = @new_resource.ring_path - ring_data = { :raw => {}, :parsed => {}, :in_use => {} } + ring_data = { raw: {}, parsed: {}, in_use: {} } disk_data = {} dirty_cluster_reasons = [] - [ "account", "container", "object" ].each do |which| + ['account', 'container', 'object'].each do |which| ring_data[:raw][which] = nil if ::File.exist?("#{ring_path}/#{which}.builder") @@ -40,7 +42,7 @@ def generate_script # Chef::Log.debug("#{ which.capitalize } Ring data: #{ring_data[:raw][which]}") ring_data[:parsed][which] = parse_ring_output(ring_data[:raw][which]) - node.set["swift"]["state"]["ring"][which] = ring_data[:parsed][which] + node.set['openstack']['object-storage']['state']['ring'][which] = ring_data[:parsed][which] end else Chef::Log.info("#{which.capitalize} ring builder files do not exist!") @@ -58,40 +60,41 @@ def generate_script end end - Chef::Log.debug("#{which.capitalize} Ring - In use: #{PP.pp(ring_data[:in_use][which],dump='')}") + Chef::Log.debug("#{which.capitalize} Ring - In use: #{PP.pp(ring_data[:in_use][which], '')}") # figure out what's present in the cluster disk_data[which] = {} - disk_state,_,_ = Chef::Search::Query.new.search(:node,"chef_environment:#{node.chef_environment} AND roles:swift-#{which}-server") + role = node['openstack']['object-storage']["#{which}_server_chef_role"] + disk_state, _, _ = Chef::Search::Query.new.search(:node, "chef_environment:#{node.chef_environment} AND roles:#{role}") # for a running track of available disks disk_data[:available] ||= {} disk_data[:available][which] ||= {} disk_state.each do |swiftnode| - if swiftnode[:swift][:state] and swiftnode[:swift][:state][:devs] - swiftnode[:swift][:state][:devs].each do |k,v| + if swiftnode['openstack']['object-storage']['state'] && swiftnode['openstack']['object-storage']['state']['devs'] + swiftnode['openstack']['object-storage']['state']['devs'].each do |k, v| disk_data[which][v[:ip]] = disk_data[which][v[:ip]] || {} disk_data[which][v[:ip]][k] = {} - v.keys.each { |x| disk_data[which][v[:ip]][k].store(x,v[x]) } + v.keys.each { |x| disk_data[which][v[:ip]][k].store(x, v[x]) } - if swiftnode[:swift].has_key?("#{which}-zone") - disk_data[which][v[:ip]][k]["zone"]=swiftnode[:swift]["#{which}-zone"] - elsif swiftnode[:swift].has_key?("zone") - disk_data[which][v[:ip]][k]["zone"]=swiftnode[:swift]["zone"] + if swiftnode['openstack']['object-storage'].key?("#{which}-zone") + disk_data[which][v[:ip]][k]['zone'] = swiftnode['openstack']['object-storage']["#{which}-zone"] + elsif swiftnode['openstack']['object-storage'].key?('zone') + disk_data[which][v[:ip]][k]['zone'] = swiftnode['openstack']['object-storage']['zone'] else - raise "Node #{swiftnode[:hostname]} has no zone assigned" + fail "Node #{swiftnode[:hostname]} has no zone assigned" end disk_data[:available][which][v[:mountpoint]] = v[:ip] - if not v[:mounted] + unless v[:mounted] dirty_cluster_reasons << "Disk #{v[:name]} (#{v[:uuid]}) is not mounted on host #{v[:ip]} (#{swiftnode[:hostname]})" end end end end - Chef::Log.debug("#{which.capitalize} Ring - Avail: #{PP.pp(disk_data[:available][which],dump='')}") + Chef::Log.debug("#{which.capitalize} Ring - Avail: #{PP.pp(disk_data[:available][which], '')}") end # Have the raw data, now bump it together and drop the script @@ -99,7 +102,7 @@ def generate_script s = "#!/bin/bash\n\n# This script is automatically generated.\n" s << "# Running it will likely blow up your system if you don't review it carefully.\n" s << "# You have been warned.\n\n" - if not node["swift"]["auto_rebuild_rings"] + unless node['openstack']['object-storage']['auto_rebuild_rings'] s << "if [ \"$1\" != \"--force\" ]; then\n" s << " echo \"Auto rebuild rings is disabled, so you must use --force to generate rings\"\n" s << " exit 0\n" @@ -112,24 +115,24 @@ def generate_script missing_disks = {} new_servers = [] - [ "account", "container", "object" ].each do |which| + ['account', 'container', 'object'].each do |which| # remove available disks that are already in the ring - new_disks[which] = disk_data[:available][which].reject{ |k,v| ring_data[:in_use][which].has_key?(k) } + new_disks[which] = disk_data[:available][which].reject { |k, v| ring_data[:in_use][which].key?(k) } # find all in-ring disks that are not in the cluster - missing_disks[which] = ring_data[:in_use][which].reject{ |k,v| disk_data[:available][which].has_key?(k) } + missing_disks[which] = ring_data[:in_use][which].reject { |k, v| disk_data[:available][which].key?(k) } - Chef::Log.debug("#{which.capitalize} Ring - Missing: #{PP.pp(missing_disks[which],dump='')}") - Chef::Log.debug("#{which.capitalize} Ring - New: #{PP.pp(new_disks[which],dump='')}") + Chef::Log.debug("#{which.capitalize} Ring - Missing: #{PP.pp(missing_disks[which], '')}") + Chef::Log.debug("#{which.capitalize} Ring - New: #{PP.pp(new_disks[which], '')}") s << "\n# -- #{which.capitalize} Servers --\n\n" disk_data[which].keys.sort.each do |ip| s << "# #{ip}\n" disk_data[which][ip].keys.sort.each do |k| v = disk_data[which][ip][k] - s << "# " + v.keys.sort.select{|x| ["ip", "device", "uuid"].include?(x)}.collect{|x| v[x] }.join(", ") - if new_disks[which].has_key?(v["mountpoint"]) - s << " (NEW!)" + s << '# ' + v.keys.sort.select { |x| ['ip', 'device', 'uuid'].include?(x) }.map { |x| v[x] }.join(', ') + if new_disks[which].key?(v['mountpoint']) + s << ' (NEW!)' new_servers << ip unless new_servers.include?(ip) end s << "\n" @@ -146,7 +149,7 @@ def generate_script disk_data[which].keys.sort.each do |ip| disk_data[which][ip].keys.sort.each do |uuid| v = disk_data[which][ip][uuid] - if new_disks[which].has_key?(v['mountpoint']) + if new_disks[which].key?(v['mountpoint']) s << "swift-ring-builder #{ring_path}/#{which}.builder add z#{v['zone']}-#{v['ip']}:#{ports[which]}/#{v['mountpoint']} #{v['size']}\n" must_rebalance = true end @@ -155,9 +158,9 @@ def generate_script # remove the disks -- sort to ensure consistent order missing_disks[which].keys.sort.each do |mountpoint| - diskinfo=ring_data[:parsed][which][:hosts].select{|k,v| v.has_key?(mountpoint)}.collect{|_,v| v[mountpoint]}[0] - Chef::Log.debug("Missing diskinfo: #{PP.pp(diskinfo,dump='')}") - description = Hash[diskinfo.select{|k,v| [:zone, :ip, :device].include?(k)}].collect{|k,v| "#{k}: #{v}" }.join(", ") + diskinfo = ring_data[:parsed][which][:hosts].select { |k, v| v.key?(mountpoint) }.map { |_, v| v[mountpoint] }[0] + Chef::Log.debug("Missing diskinfo: #{PP.pp(diskinfo, '')}") + description = Hash[diskinfo.select { |k, v| [:zone, :ip, :device].include?(k) }].map { |k, v| "#{k}: #{v}" }.join(', ') s << "# #{description}\n" s << "swift-ring-builder #{ring_path}/#{which}.builder remove d#{missing_disks[which][mountpoint]}\n" must_rebalance = true @@ -165,26 +168,26 @@ def generate_script s << "\n" - if(must_rebalance) + if must_rebalance s << "swift-ring-builder #{ring_path}/#{which}.builder rebalance\n\n\n" else s << "# #{which.capitalize} ring has no outstanding changes!\n\n" end # we'll only rebalance if we meet the minimums for new adds - if node["swift"].has_key?("wait_for") - if node["swift"]["wait_for"] > new_servers.count - Chef::Log.debug("New servers, but not enough to force a rebalance") + if node['openstack']['object-storage'].key?('wait_for') + if node['openstack']['object-storage']['wait_for'] > new_servers.count + Chef::Log.debug('New servers, but not enough to force a rebalance') must_rebalance = false end end end - [ s, must_rebalance ] + [s, must_rebalance] end # Parse the raw output of swift-ring-builder def parse_ring_output(ring_data) - output = { :state => {} } + output = { state: {} } ring_data.each do |line| if line =~ /build version ([0-9]+)/ @@ -193,26 +196,43 @@ def parse_ring_output(ring_data) next elsif line =~ /^Devices:\s+id\s+zone\s+/ next + elsif line =~ /^\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\S+)\s+([0-9.]+)\s+(\d+)\s+([-0-9.]+)\s*$/ + output[:hosts] ||= {} + output[:hosts][$4] ||= {} + + output[:hosts][$4][$8] ||= {} + + output[:hosts][$4][$8][:id] = $1 + output[:hosts][$4][$8][:region] = $2 + output[:hosts][$4][$8][:zone] = $3 + output[:hosts][$4][$8][:ip] = $4 + output[:hosts][$4][$8][:port] = $5 + output[:hosts][$4][$8][:replication_ip] = $6 + output[:hosts][$4][$8][:replication_port] = $7 + output[:hosts][$4][$8][:device] = $8 + output[:hosts][$4][$8][:weight] = $9 + output[:hosts][$4][$8][:partitions] = $10 + output[:hosts][$4][$8][:balance] = $11 elsif line =~ /^\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\S+)\s+([0-9.]+)\s+(\d+)\s+([-0-9.]+)\s*$/ output[:hosts] ||= {} - output[:hosts][$3] ||= {} + output[:hosts][$4] ||= {} - output[:hosts][$3][$5] = {} + output[:hosts][$4][$6] ||= {} - output[:hosts][$3][$5][:id] = $1 - output[:hosts][$3][$5][:region] = $2 - output[:hosts][$3][$5][:zone] = $3 - output[:hosts][$3][$5][:ip] = $4 - output[:hosts][$3][$5][:port] = $5 - output[:hosts][$3][$5][:device] = $6 - output[:hosts][$3][$5][:weight] = $7 - output[:hosts][$3][$5][:partitions] = $8 - output[:hosts][$3][$5][:balance] = $9 + output[:hosts][$4][$6][:id] = $1 + output[:hosts][$4][$6][:region] = $2 + output[:hosts][$4][$6][:zone] = $3 + output[:hosts][$4][$6][:ip] = $4 + output[:hosts][$4][$6][:port] = $5 + output[:hosts][$4][$6][:device] = $6 + output[:hosts][$4][$6][:weight] = $7 + output[:hosts][$4][$6][:partitions] = $8 + output[:hosts][$4][$6][:balance] = $9 elsif line =~ /^\s+(\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\S+)\s+([0-9.]+)\s+(\d+)\s+([-0-9.]+)\s*$/ output[:hosts] ||= {} output[:hosts][$3] ||= {} - output[:hosts][$3][$5] = {} + output[:hosts][$3][$5] ||= {} output[:hosts][$3][$5][:id] = $1 output[:hosts][$3][$5][:zone] = $2 @@ -238,7 +258,7 @@ def parse_ring_output(ring_data) elsif line =~ /^The minimum number of hours before a partition can be reassigned is (\d+)$/ output[:state][:min_part_hours] = $1 else - raise "Cannot parse ring builder output for #{line}" + fail "Cannot parse ring builder output for #{line}" end end @@ -248,7 +268,7 @@ end action :ensure_exists do Chef::Log.debug("Ensuring #{new_resource.name}") new_resource.updated_by_last_action(false) - s,must_update = generate_script + s, must_update = generate_script script_file = File new_resource.name do owner new_resource.owner diff --git a/chef/cookbooks/openstack-object-storage/recipes/account-server.rb b/chef/cookbooks/openstack-object-storage/recipes/account-server.rb index ad7de4e..9b7906f 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/account-server.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/account-server.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: account-server # # Copyright 2012, Rackspace US, Inc. @@ -17,16 +18,16 @@ # limitations under the License. # -include_recipe "openstack-object-storage::common" -include_recipe "openstack-object-storage::storage-common" -include_recipe "openstack-object-storage::disks" +include_recipe 'openstack-object-storage::common' +include_recipe 'openstack-object-storage::storage-common' +include_recipe 'openstack-object-storage::disks' -platform_options = node["swift"]["platform"] +platform_options = node['openstack']['object-storage']['platform'] -platform_options["account_packages"].each.each do |pkg| +platform_options['account_packages'].each.each do |pkg| package pkg do - action :install - options platform_options["override_options"] # retain configs + action :upgrade + options platform_options['override_options'] # retain configs end end @@ -34,17 +35,18 @@ end # https://bugzilla.redhat.com/show_bug.cgi?id=807170 %w{auditor reaper replicator}.each do |svc| template "/etc/systemd/system/openstack-swift-account-#{svc}.service" do - owner "root" - group "root" - mode "0644" - source "simple-systemd-config.erb" - variables({ :description => "OpenStack Object Storage (swift) - " + - "Account #{svc.capitalize}", - :user => "swift", - :exec => "/usr/bin/swift-account-#{svc} " + - "/etc/swift/account-server.conf" - }) - only_if { platform?(%w{fedora}) } + owner 'root' + group 'root' + mode '0644' + source 'simple-systemd-config.erb' + variables( + description: 'OpenStack Object Storage (swift) - ' + + "Account #{svc.capitalize}", + user: 'swift', + exec: "/usr/bin/swift-account-#{svc} " + + '/etc/swift/account-server.conf' + ) + only_if { platform?('fedora') } end end @@ -53,44 +55,43 @@ end # https://bugzilla.redhat.com/show_bug.cgi?id=807170 %w{auditor reaper replicator}.each do |svc| template "/etc/init.d/openstack-swift-account-#{svc}" do - owner "root" - group "root" - mode "0755" - source "simple-redhat-init-config.erb" - variables({ :description => "OpenStack Object Storage (swift) - " + - "Account #{svc.capitalize}", - :exec => "account-#{svc}" - }) + owner 'root' + group 'root' + mode '0755' + source 'simple-redhat-init-config.erb' + variables( + description: 'OpenStack Object Storage (swift) - ' + + "Account #{svc.capitalize}", + exec: "account-#{svc}" + ) only_if { platform?(%w{redhat centos}) } end end %w{swift-account swift-account-auditor swift-account-reaper swift-account-replicator}.each do |svc| - service_name = platform_options["service_prefix"] + svc + platform_options["service_suffix"] + service_name = platform_options['service_prefix'] + svc + platform_options['service_suffix'] service svc do service_name service_name - provider platform_options["service_provider"] - supports :status => true, :restart => true + provider platform_options['service_provider'] + supports status: true, restart: true action [:enable, :start] - only_if "[ -e /etc/swift/account-server.conf ] && [ -e /etc/swift/account.ring.gz ]" + only_if '[ -e /etc/swift/account-server.conf ] && [ -e /etc/swift/account.ring.gz ]' end end -# retrieve bind information from node -bind_ip = node["swift"]["network"]["bind_ip"] -bind_port = node["swift"]["network"]["bind_port"] - # create account server template -template "/etc/swift/account-server.conf" do - source "account-server.conf.erb" - owner "swift" - group "swift" - mode "0600" - variables("bind_ip" => node["swift"]["network"]["account-bind-ip"], - "bind_port" => node["swift"]["network"]["account-bind-port"]) +template '/etc/swift/account-server.conf' do + source 'account-server.conf.erb' + owner 'swift' + group 'swift' + mode 0600 + variables( + 'bind_ip' => node['openstack']['object-storage']['network']['account-bind-ip'], + 'bind_port' => node['openstack']['object-storage']['network']['account-bind-port'] + ) - notifies :restart, "service[swift-account]", :immediately - notifies :restart, "service[swift-account-auditor]", :immediately - notifies :restart, "service[swift-account-reaper]", :immediately - notifies :restart, "service[swift-account-replicator]", :immediately + notifies :restart, 'service[swift-account]', :immediately + notifies :restart, 'service[swift-account-auditor]', :immediately + notifies :restart, 'service[swift-account-reaper]', :immediately + notifies :restart, 'service[swift-account-replicator]', :immediately end diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/client.rb b/chef/cookbooks/openstack-object-storage/recipes/client.rb similarity index 64% rename from chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/client.rb rename to chef/cookbooks/openstack-object-storage/recipes/client.rb index 235a67c..f29d61a 100644 --- a/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/client.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/client.rb @@ -1,8 +1,9 @@ +# encoding: UTF-8 # -# Cookbook Name:: mysql_test +# Cookbook Name:: openstack-object-storage # Recipe:: client # -# Copyright 2012, Opscode, Inc. +# Copyright 2014, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +18,11 @@ # limitations under the License. # -include_recipe "yum::epel" if platform_family?('rhel') +platform_options = node['openstack']['object-storage']['platform'] +platform_options['swift_client_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/common.rb b/chef/cookbooks/openstack-object-storage/recipes/common.rb index 74dcff0..cb61e89 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/common.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/common.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: swift-common # # Copyright 2012, Rackspace US, Inc. @@ -17,94 +18,106 @@ # limitations under the License. # -class Chef::Recipe +class Chef::Recipe # rubocop:disable Documentation include DriveUtils end -include_recipe 'sysctl::default' +include_recipe 'openstack-common::sysctl' + +#------------- +# stats +#------------- # optionally statsd daemon for stats collection -if node["swift"]["enable_statistics"] +if node['openstack']['object-storage']['statistics']['enabled'] + node.set['statsd']['relay_server'] = true include_recipe 'statsd::server' end -platform_options = node["swift"]["platform"] - -# update repository if requested with the ubuntu cloud -case node["platform"] -when "ubuntu" - - Chef::Log.info("Creating apt repository for http://ubuntu-cloud.archive.canonical.com/ubuntu") - Chef::Log.info("chefspec: #{node['lsb']['codename']}-updates/#{node['swift']['release']}") - apt_repository "ubuntu_cloud" do - uri "http://ubuntu-cloud.archive.canonical.com/ubuntu" - distribution "#{node['lsb']['codename']}-updates/#{node['swift']['release']}" - components ["main"] - key "5EDB1B62EC4926EA" - action :add - end +# find graphing server address +if Chef::Config[:solo] && !node['recipes'].include?('chef-solo-search') + Chef::Log.warn('This recipe uses search. Chef Solo does not support search.') + graphite_servers = [] +else + graphite_servers = search(:node, "roles:#{node['openstack']['object-storage']['statistics']['graphing_role']} AND chef_environment:#{node.chef_environment}") +end +graphite_host = '127.0.0.1' +unless graphite_servers.empty? + graphite_host = graphite_servers[0]['network']["ipaddress_#{node['openstack']['object-storage']['statistics']['graphing_interface']}"] end +if node['openstack']['object-storage']['statistics']['graphing_ip'].nil? + node.set['statsd']['graphite_host'] = graphite_host +else + node.set['statsd']['graphite_host'] = node['openstack']['object-storage']['statistics']['graphing_ip'] +end -platform_options["swift_packages"].each do |pkg| +#-------------- +# swift common +#-------------- + +platform_options = node['openstack']['object-storage']['platform'] + +platform_options['swift_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end -directory "/etc/swift" do +directory '/etc/swift' do action :create - owner "swift" - group "swift" - mode "0700" - only_if "/usr/bin/id swift" + owner 'swift' + group 'swift' + mode 0700 + only_if '/usr/bin/id swift' end # determine hash -if node['swift']['swift_secret_databag_name'].nil? - swifthash = node['swift']['swift_hash'] +if node['openstack']['object-storage']['swift_secret_databag_name'].nil? + swifthash = node['openstack']['object-storage']['swift_hash'] else - swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name'] + swift_secrets = Chef::EncryptedDataBagItem.load 'secrets', node['openstack']['object-storage']['swift_secret_databag_name'] swifthash = swift_secrets['swift_hash'] end - -file "/etc/swift/swift.conf" do +file '/etc/swift/swift.conf' do action :create - owner "swift" - group "swift" - mode "0700" + owner 'swift' + group 'swift' + mode 0700 content "[swift-hash]\nswift_hash_path_suffix=#{swifthash}\n" - only_if "/usr/bin/id swift" + only_if '/usr/bin/id swift' end # need a swift user -user "swift" do - shell "/bin/bash" +user 'swift' do + shell '/bin/bash' action :modify - only_if "/usr/bin/id swift" + only_if '/usr/bin/id swift' end -package "git" do - action :install +package 'git' do + options platform_options['package_overrides'] + action :upgrade end # drop a ring puller script # TODO: make this smarter -git_builder_ip = node["swift"]["git_builder_ip"] -template "/etc/swift/pull-rings.sh" do - source "pull-rings.sh.erb" - owner "swift" - group "swift" - mode "0700" - variables({ - :builder_ip => git_builder_ip, - :service_prefix => platform_options["service_prefix"] - }) - only_if "/usr/bin/id swift" +git_builder_ip = node['openstack']['object-storage']['git_builder_ip'] +template '/etc/swift/pull-rings.sh' do + source 'pull-rings.sh.erb' + owner 'swift' + group 'swift' + mode 0700 + variables( + builder_ip: git_builder_ip, + service_prefix: platform_options['service_prefix'] + ) + only_if '/usr/bin/id swift' end -execute "/etc/swift/pull-rings.sh" do - cwd "/etc/swift" - only_if "[ -x /etc/swift/pull-rings.sh ]" +execute '/etc/swift/pull-rings.sh' do + cwd '/etc/swift' + only_if '[ -x /etc/swift/pull-rings.sh ]' end diff --git a/chef/cookbooks/openstack-object-storage/recipes/container-server.rb b/chef/cookbooks/openstack-object-storage/recipes/container-server.rb index e0c0aee..afd4781 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/container-server.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/container-server.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: swift-container-server # # Copyright 2012, Rackspace US, Inc. @@ -17,17 +18,16 @@ # limitations under the License. # -include_recipe "openstack-object-storage::common" -include_recipe "openstack-object-storage::storage-common" -include_recipe "openstack-object-storage::disks" +include_recipe 'openstack-object-storage::common' +include_recipe 'openstack-object-storage::storage-common' +include_recipe 'openstack-object-storage::disks' +platform_options = node['openstack']['object-storage']['platform'] -platform_options = node["swift"]["platform"] - -platform_options["container_packages"].each do |pkg| +platform_options['container_packages'].each do |pkg| package pkg do - action :install - options platform_options["override_options"] + action :upgrade + options platform_options['override_options'] end end @@ -35,17 +35,18 @@ end # https://bugzilla.redhat.com/show_bug.cgi?id=807170 %w{auditor updater replicator}.each do |svc| template "/etc/systemd/system/openstack-swift-container-#{svc}.service" do - owner "root" - group "root" - mode "0644" - source "simple-systemd-config.erb" - variables({ :description => "OpenStack Object Storage (swift) - " + - "Container #{svc.capitalize}", - :user => "swift", - :exec => "/usr/bin/swift-container-#{svc} " + - "/etc/swift/container-server.conf" - }) - only_if { platform?(%w{fedora}) } + owner 'root' + group 'root' + mode '0644' + source 'simple-systemd-config.erb' + variables( + description: 'OpenStack Object Storage (swift) - ' + + "Container #{svc.capitalize}", + user: 'swift', + exec: "/usr/bin/swift-container-#{svc} " + + '/etc/swift/container-server.conf' + ) + only_if { platform?('fedora') } end end @@ -54,40 +55,71 @@ end # https://bugzilla.redhat.com/show_bug.cgi?id=807170 %w{auditor updater replicator}.each do |svc| template "/etc/init.d/openstack-swift-container-#{svc}" do - owner "root" - group "root" - mode "0755" - source "simple-redhat-init-config.erb" - variables({ :description => "OpenStack Object Storage (swift) - " + - "Container #{svc.capitalize}", - :exec => "container-#{svc}" - }) - only_if { platform?(%w{redhat centos}) } + owner 'root' + group 'root' + mode '0755' + source 'simple-redhat-init-config.erb' + variables( + description: 'OpenStack Object Storage (swift) - ' + + "Container #{svc.capitalize}", + exec: "container-#{svc}" + ) + only_if { platform?('redhat', 'centos') } end end %w{swift-container swift-container-auditor swift-container-replicator swift-container-updater}.each do |svc| - service_name=platform_options["service_prefix"] + svc + platform_options["service_suffix"] + service_name = platform_options['service_prefix'] + svc + platform_options['service_suffix'] service svc do service_name service_name - provider platform_options["service_provider"] - supports :status => true, :restart => true + provider platform_options['service_provider'] + supports status: true, restart: true action [:enable, :start] - only_if "[ -e /etc/swift/container-server.conf ] && [ -e /etc/swift/container.ring.gz ]" + only_if '[ -e /etc/swift/container-server.conf ] && [ -e /etc/swift/container.ring.gz ]' end end -template "/etc/swift/container-server.conf" do - source "container-server.conf.erb" - owner "swift" - group "swift" - mode "0600" - variables("bind_ip" => node["swift"]["network"]["container-bind-ip"], - "bind_port" => node["swift"]["network"]["container-bind-port"]) +template '/etc/swift/container-server.conf' do + source 'container-server.conf.erb' + owner 'swift' + group 'swift' + mode 0600 + variables( + 'bind_ip' => node['openstack']['object-storage']['network']['container-bind-ip'], + 'bind_port' => node['openstack']['object-storage']['network']['container-bind-port'] + ) - notifies :restart, "service[swift-container]", :immediately - notifies :restart, "service[swift-container-replicator]", :immediately - notifies :restart, "service[swift-container-updater]", :immediately - notifies :restart, "service[swift-container-auditor]", :immediately + notifies :restart, 'service[swift-container]', :immediately + notifies :restart, 'service[swift-container-replicator]', :immediately + notifies :restart, 'service[swift-container-updater]', :immediately + notifies :restart, 'service[swift-container-auditor]', :immediately +end + +# Ubuntu 12.04 packages are missing the swift-container-sync service scripts +# See https://bugs.launchpad.net/cloud-archive/+bug/1250171 +if platform?('ubuntu') + cookbook_file '/etc/init/swift-container-sync.conf' do + owner 'root' + group 'root' + mode 0755 + source 'swift-container-sync.conf.upstart' + action :create + not_if '[ -e /etc/init/swift-container-sync.conf ]' + end + link '/etc/init.d/swift-container-sync' do + to '/lib/init/upstart-job' + not_if '[ -e /etc/init.d/swift-container-sync ]' + end +end + +service_name = platform_options['service_prefix'] + 'swift-container-sync' + platform_options['service_suffix'] +unless node['openstack']['object-storage']['container-server']['allowed_sync_hosts'] == [] + service 'swift-container-sync' do + service_name service_name + provider platform_options['service_provider'] + supports status: false, restart: true + action [:enable, :start] + only_if '[ -e /etc/swift/container-server.conf ] && [ -e /etc/swift/container.ring.gz ]' + end end diff --git a/chef/cookbooks/openstack-object-storage/recipes/disks.rb b/chef/cookbooks/openstack-object-storage/recipes/disks.rb index 89c573f..bff8efc 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/disks.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/disks.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: disks # # Copyright 2012, Rackspace US, Inc. @@ -19,32 +20,34 @@ # Author: Ron Pedde # Inspired by: Andi Abes @ Dell -class Chef::Recipe +class Chef::Recipe # rubocop:disable Documentation include IPUtils + include DriveUtils end +platform_options = node['openstack']['object-storage']['platform'] -platform_options = node["swift"]["platform"] - -package "xfsprogs" do - action :install +package 'xfsprogs' do + options platform_options['package_overrides'] + action :upgrade only_if { platform?(%w{ubuntu debian fedora centos}) } end %w(parted util-linux).each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end -disk_enum_expr = node["swift"]["disk_enum_expr"] -disk_test_filter = node["swift"]["disk_test_filter"] +disk_enum_expr = node['openstack']['object-storage']['disk_enum_expr'] +disk_test_filter = node['openstack']['object-storage']['disk_test_filter'] disks = locate_disks(disk_enum_expr, disk_test_filter) disks.each do |disk| openstack_object_storage_disk "/dev/#{disk}" do - part [{:type => platform_options["disk_format"] , :size => :remaining}] + part [{ type: platform_options['disk_format'] , size: :remaining }] action :ensure_exists end end @@ -54,13 +57,12 @@ end # # additionally, there is an implicit assumption that bind ports # for all object/container/account services are on the same net -disk_ip = locate_ip_in_cidr(node["swift"]["network"]["object-cidr"], node) +disk_ip = locate_ip_in_cidr(node['openstack']['object-storage']['network']['object-cidr'], node) -openstack_object_storage_mounts "/srv/node" do +openstack_object_storage_mounts '/srv/node' do action :ensure_exists - publish_attributes "swift/state/devs" - devices disks.collect { |x| "#{x}1" } + publish_attributes 'swift/state/devs' + devices disks.map { |x| "#{x}1" } ip disk_ip - format platform_options["disk_format"] + format platform_options['disk_format'] end - diff --git a/chef/cookbooks/openstack-object-storage/recipes/management-server.rb b/chef/cookbooks/openstack-object-storage/recipes/management-server.rb index db29e1d..0df8c60 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/management-server.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/management-server.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: management-server # # Copyright 2012, Rackspace US, Inc. @@ -17,39 +18,73 @@ # limitations under the License. # -include_recipe "openstack-object-storage::common" +include_recipe 'openstack-object-storage::common' # FIXME: This should probably be a role (ring-builder?), so you don't end up # with multiple repos! -include_recipe "openstack-object-storage::ring-repo" +include_recipe 'openstack-object-storage::ring-repo' -platform_options = node["swift"]["platform"] +platform_options = node['openstack']['object-storage']['platform'] -if node["swift"]["authmode"] == "swauth" - platform_options["swauth_packages"].each.each do |pkg| - package pkg do - action :install - options platform_options["override_options"] # retain configs +if node['openstack']['object-storage']['authmode'] == 'swauth' + case node['openstack']['object-storage']['swauth_source'] + when 'package' + platform_options['swauth_packages'].each do |pkg| + package pkg do + action :upgrade + options platform_options['override_options'] + end + end + when 'git' + git "#{Chef::Config[:file_cache_path]}/swauth" do + repository node['openstack']['object-storage']['swauth_repository'] + revision node['openstack']['object-storage']['swauth_version'] + action :sync + end + + bash 'install_swauth' do + cwd "#{Chef::Config[:file_cache_path]}/swauth" + user 'root' + group 'root' + code <<-EOH + python setup.py install + EOH + environment 'PREFIX' => '/usr/local' end end end # determine where to find dispersion login information -if node['swift']['swift_secret_databag_name'].nil? - auth_user = node["swift"]["dispersion"]["auth_user"] - auth_key = node["swift"]["dispersion"]["auth_key"] +if node['openstack']['object-storage']['swift_secret_databag_name'].nil? + auth_user = node['openstack']['object-storage']['dispersion']['auth_user'] + auth_key = node['openstack']['object-storage']['dispersion']['auth_key'] else - swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name'] + swift_secrets = Chef::EncryptedDataBagItem.load 'secrets', node['openstack']['object-storage']['swift_secret_databag_name'] auth_user = swift_secrets['dispersion_auth_user'] auth_key = swift_secrets['dispersion_auth_key'] end -template "/etc/swift/dispersion.conf" do - source "dispersion.conf.erb" - owner "swift" - group "swift" - mode "0600" - variables("auth_url" => node["swift"]["auth_url"], - "auth_user" => auth_user, - "auth_key" => auth_key) +if node['openstack']['object-storage']['statistics']['enabled'] + template platform_options['swift_statsd_publish'] do + source 'swift-statsd-publish.py.erb' + owner 'root' + group 'root' + mode 0755 + end + cron 'cron_swift_statsd_publish' do + command "#{platform_options['swift_statsd_publish']} > /dev/null 2>&1" + minute "*/#{node['openstack']['object-storage']['statistics']['report_frequency']}" + end +end + +template '/etc/swift/dispersion.conf' do + source 'dispersion.conf.erb' + owner 'swift' + group 'swift' + mode 0600 + variables( + 'auth_url' => node['openstack']['object-storage']['auth_url'], + 'auth_user' => auth_user, + 'auth_key' => auth_key + ) end diff --git a/chef/cookbooks/openstack-object-storage/recipes/memcached.rb b/chef/cookbooks/openstack-object-storage/recipes/memcached.rb index 9cc533a..1093749 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/memcached.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/memcached.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: memcached # # Copyright 2012, Rackspace US, Inc. @@ -17,4 +18,4 @@ # limitations under the License. # -include_recipe "memcached" +include_recipe 'memcached' diff --git a/chef/cookbooks/openstack-object-storage/recipes/object-server.rb b/chef/cookbooks/openstack-object-storage/recipes/object-server.rb index 01bb152..c849357 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/object-server.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/object-server.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: swift-object-server # # Copyright 2012, Rackspace US, Inc. @@ -17,16 +18,16 @@ # limitations under the License. # -include_recipe "openstack-object-storage::common" -include_recipe "openstack-object-storage::storage-common" -include_recipe "openstack-object-storage::disks" +include_recipe 'openstack-object-storage::common' +include_recipe 'openstack-object-storage::storage-common' +include_recipe 'openstack-object-storage::disks' -platform_options = node["swift"]["platform"] +platform_options = node['openstack']['object-storage']['platform'] -platform_options["object_packages"].each do |pkg| +platform_options['object_packages'].each do |pkg| package pkg do - action :install - options platform_options["override_options"] # retain configs + action :upgrade + options platform_options['override_options'] # retain configs end end @@ -34,17 +35,18 @@ end # https://bugzilla.redhat.com/show_bug.cgi?id=807170 %w{auditor updater replicator}.each do |svc| template "/etc/systemd/system/openstack-swift-object-#{svc}.service" do - owner "root" - group "root" - mode "0644" - source "simple-systemd-config.erb" - variables({ :description => "OpenStack Object Storage (swift) - " + - "Object #{svc.capitalize}", - :user => "swift", - :exec => "/usr/bin/swift-object-#{svc} " + - "/etc/swift/object-server.conf" - }) - only_if { platform?(%w{fedora})} + owner 'root' + group 'root' + mode '0644' + source 'simple-systemd-config.erb' + variables( + description: 'OpenStack Object Storage (swift) - ' + + "Object #{svc.capitalize}", + user: 'swift', + exec: "/usr/bin/swift-object-#{svc} " + + '/etc/swift/object-server.conf' + ) + only_if { platform?('fedora') } end end @@ -53,49 +55,52 @@ end # https://bugzilla.redhat.com/show_bug.cgi?id=807170 %w{auditor updater replicator}.each do |svc| template "/etc/init.d/openstack-swift-object-#{svc}" do - owner "root" - group "root" - mode "0755" - source "simple-redhat-init-config.erb" - variables({ :description => "OpenStack Object Storage (swift) - " + - "Object #{svc.capitalize}", - :exec => "object-#{svc}" - }) - only_if { platform?(%w{redhat centos}) } + owner 'root' + group 'root' + mode '0755' + source 'simple-redhat-init-config.erb' + variables( + description: 'OpenStack Object Storage (swift) - ' + + "Object #{svc.capitalize}", + exec: "object-#{svc}" + ) + only_if { platform?('redhat', 'centos') } end end %w{swift-object swift-object-replicator swift-object-auditor swift-object-updater}.each do |svc| - service_name=platform_options["service_prefix"] + svc + platform_options["service_suffix"] + service_name = platform_options['service_prefix'] + svc + platform_options['service_suffix'] service svc do service_name service_name - provider platform_options["service_provider"] + provider platform_options['service_provider'] # the default ubuntu provider uses invoke-rc.d, which apparently is # status-illy broken in ubuntu - supports :status => false, :restart => true + supports status: false, restart: true action [:enable, :start] - only_if "[ -e /etc/swift/object-server.conf ] && [ -e /etc/swift/object.ring.gz ]" + only_if '[ -e /etc/swift/object-server.conf ] && [ -e /etc/swift/object.ring.gz ]' end end -template "/etc/swift/object-server.conf" do - source "object-server.conf.erb" - owner "swift" - group "swift" - mode "0600" - variables("bind_ip" => node["swift"]["network"]["object-bind-ip"], - "bind_port" => node["swift"]["network"]["object-bind-port"]) +template '/etc/swift/object-server.conf' do + source 'object-server.conf.erb' + owner 'swift' + group 'swift' + mode 0600 + variables( + 'bind_ip' => node['openstack']['object-storage']['network']['object-bind-ip'], + 'bind_port' => node['openstack']['object-storage']['network']['object-bind-port'] + ) - notifies :restart, "service[swift-object]", :immediately - notifies :restart, "service[swift-object-replicator]", :immediately - notifies :restart, "service[swift-object-updater]", :immediately - notifies :restart, "service[swift-object-auditor]", :immediately + notifies :restart, 'service[swift-object]', :immediately + notifies :restart, 'service[swift-object-replicator]', :immediately + notifies :restart, 'service[swift-object-updater]', :immediately + notifies :restart, 'service[swift-object-auditor]', :immediately end -cron "swift-recon" do - minute "*/5" - command "swift-recon-cron /etc/swift/object-server.conf" - user "swift" +cron 'swift-recon' do + minute '*/5' + command 'swift-recon-cron /etc/swift/object-server.conf' + user 'swift' end diff --git a/chef/cookbooks/openstack-object-storage/recipes/proxy-server.rb b/chef/cookbooks/openstack-object-storage/recipes/proxy-server.rb index 374e3c2..8fb622e 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/proxy-server.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/proxy-server.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: proxy-server # # Copyright 2012, Rackspace US, Inc. @@ -16,101 +17,128 @@ # See the License for the specific language governing permissions and # limitations under the License. -include_recipe "openstack-object-storage::common" -include_recipe "openstack-object-storage::memcached" +include_recipe 'openstack-object-storage::common' +include_recipe 'openstack-object-storage::memcached' -class Chef::Recipe +class Chef::Recipe # rubocop:disable Documentation include IPUtils end -if node.run_list.expand(node.chef_environment).recipes.include?("swift::setup") - Chef::Log.info("I ran the swift::setup so I will use my own swift passwords") +if node.run_list.expand(node.chef_environment).recipes.include?('openstack-object-storage::setup') + Chef::Log.info('I ran the openstack-object-storage::setup so I will use my own swift passwords') else - setup = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-setup") + setup_role = node['openstack']['object-storage']['setup_chef_role'] + setup = search(:node, "chef_environment:#{node.chef_environment} AND roles:#{setup_role}") if setup.length == 0 - Chef::Application.fatal! "You must have run the swift::setup recipe (on this or another node) before running the swift::proxy recipe on this node" + Chef::Application.fatal! 'You must have run the openstack-object-storage::setup recipe (on this or another node) before running the swift::proxy recipe on this node' elsif setup.length == 1 - Chef::Log.info "Found swift::setup node: #{setup[0].name}" - node.set["swift"]["service_pass"] = setup[0]["swift"]["service_pass"] - elsif setup.length >1 - Chef::Application.fatal! "You have multiple nodes in your environment that have run swift-setup, and that is not allowed" + Chef::Log.info "Found openstack-object-storage::setup node: #{setup[0].name}" + node.set['openstack']['object-storage']['service_pass'] = setup[0]['swift']['service_pass'] + elsif setup.length > 1 + Chef::Application.fatal! 'You have multiple nodes in your environment that have run swift-setup, and that is not allowed' end end -platform_options = node["swift"]["platform"] +platform_options = node['openstack']['object-storage']['platform'] -# install platform-specific packages -platform_options["proxy_packages"].each do |pkg| +# upgrade platform-specific packages +platform_options['proxy_packages'].each do |pkg| package pkg do - action :install - options platform_options["override_options"] + action :upgrade + options platform_options['override_options'] end end -package "python-swauth" do - action :install - only_if { node["swift"]["authmode"] == "swauth" } +if node['openstack']['object-storage']['authmode'] == 'swauth' + case node['openstack']['object-storage']['swauth_source'] + when 'package' + platform_options['swauth_packages'].each do |pkg| + package pkg do + action :upgrade + options platform_options['override_options'] + end + end + when 'git' + git "#{Chef::Config[:file_cache_path]}/swauth" do + repository node['openstack']['object-storage']['swauth_repository'] + revision node['openstack']['object-storage']['swauth_version'] + action :sync + end + + bash 'install_swauth' do + cwd "#{Chef::Config[:file_cache_path]}/swauth" + user 'root' + group 'root' + code <<-EOH + python setup.py install + EOH + environment 'PREFIX' => '/usr/local' + end + end end -package "python-swift-informant" do - action :install - only_if { node["swift"]["use_informant"] } +package 'python-swift-informant' do + action :upgrade + only_if { node['openstack']['object-storage']['use_informant'] } end -package "python-keystone" do - action :install - only_if { node["swift"]["authmode"] == "keystone" } +package 'python-keystoneclient' do + action :upgrade + only_if { node['openstack']['object-storage']['authmode'] == 'keystone' } end -directory "/var/cache/swift" do - owner "swift" - group "swift" +directory '/var/cache/swift' do + owner 'swift' + group 'swift' mode 00700 end -swift_proxy_service = platform_options["service_prefix"] + "swift-proxy" + platform_options["service_suffix"] -service "swift-proxy" do +swift_proxy_service = platform_options['service_prefix'] + 'swift-proxy' + platform_options['service_suffix'] +service 'swift-proxy' do # openstack-swift-proxy.service on fedora-17, swift-proxy on ubuntu service_name swift_proxy_service - provider platform_options["service_provider"] - supports :status => true, :restart => true - action [ :enable, :start ] - only_if "[ -e /etc/swift/proxy-server.conf ] && [ -e /etc/swift/object.ring.gz ]" + provider platform_options['service_provider'] + supports status: true, restart: true + action [:enable, :start] + only_if '[ -e /etc/swift/proxy-server.conf ] && [ -e /etc/swift/object.ring.gz ]' end # use localhost when using chef solo otherwise, include all memcache # servers from all known proxies if Chef::Config[:solo] - memcache_servers = [ "127.0.0.1:11211" ] + memcache_servers = ['127.0.0.1:11211'] else - memcache_servers = [] - proxy_nodes = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-proxy-server") - proxy_nodes.each do |proxy| - proxy_ip = locate_ip_in_cidr(node["swift"]["network"]["proxy-cidr"], proxy) - next if not proxy_ip # skip nil ips so we dont break the config - server_str = "#{proxy_ip}:11211" - memcache_servers << server_str unless memcache_servers.include?(server_str) + memcache_servers = [] + proxy_role = node['openstack']['object-storage']['proxy_server_chef_role'] + proxy_nodes = search(:node, "chef_environment:#{node.chef_environment} AND roles:#{proxy_role}") + proxy_nodes.each do |proxy| + proxy_ip = locate_ip_in_cidr(node['openstack']['object-storage']['network']['proxy-cidr'], proxy) + next unless proxy_ip # skip nil ips so we dont break the config + server_str = "#{proxy_ip}:11211" + memcache_servers << server_str unless memcache_servers.include?(server_str) end end # determine authkey to use -if node['swift']['swift_secret_databag_name'].nil? - authkey = node['swift']['authkey'] +if node['openstack']['object-storage']['swift_secret_databag_name'].nil? + authkey = node['openstack']['object-storage']['authkey'] else - swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name'] + swift_secrets = Chef::EncryptedDataBagItem.load 'secrets', node['openstack']['object-storage']['swift_secret_databag_name'] authkey = swift_secrets['swift_authkey'] end # create proxy config file -template "/etc/swift/proxy-server.conf" do - source "proxy-server.conf.erb" - owner "swift" - group "swift" - mode "0600" - variables("authmode" => node["swift"]["authmode"], - "bind_host" => node["swift"]["network"]["proxy-bind-ip"], - "bind_port" => node["swift"]["network"]["proxy-bind-port"], - "authkey" => authkey, - "memcache_servers" => memcache_servers) - notifies :restart, "service[swift-proxy]", :immediately +template '/etc/swift/proxy-server.conf' do + source 'proxy-server.conf.erb' + owner 'swift' + group 'swift' + mode 0600 + variables( + 'authmode' => node['openstack']['object-storage']['authmode'], + 'bind_host' => node['openstack']['object-storage']['network']['proxy-bind-ip'], + 'bind_port' => node['openstack']['object-storage']['network']['proxy-bind-port'], + 'authkey' => authkey, + 'memcache_servers' => memcache_servers + ) + notifies :restart, 'service[swift-proxy]', :immediately end diff --git a/chef/cookbooks/openstack-object-storage/recipes/ring-repo.rb b/chef/cookbooks/openstack-object-storage/recipes/ring-repo.rb index 4131132..d1ef165 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/ring-repo.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/ring-repo.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: ring-repo # # Copyright 2012, Rackspace US, Inc. @@ -21,129 +22,131 @@ # for purposes of ring synchronization # -platform_options = node["swift"]["platform"] -ring_options = node["swift"]["ring"] +platform_options = node['openstack']['object-storage']['platform'] +ring_options = node['openstack']['object-storage']['ring'] -platform_options["git_packages"].each do |pkg| +platform_options['git_packages'].each do |pkg| package pkg do - action :install + options platform_options['package_overrides'] + action :upgrade end end -service "xinetd" do - supports :status => false, :restart => true - action [ :enable, :start ] - only_if { platform?(%w{centos redhat fedora}) } +service 'xinetd' do + supports status: false, restart: true + action [:enable, :start] + only_if { platform?('centos', 'redhat', 'fedora') } end -execute "create empty git repo" do - cwd "/tmp" +execute 'create empty git repo' do + cwd '/tmp' umask 022 command "mkdir $$; cd $$; git init; echo \"backups\" \> .gitignore; git add .gitignore; git commit -m 'initial commit' --author='chef '; git push file:///#{platform_options["git_dir"]}/rings master" - user "swift" + user 'swift' action :nothing end -directory "git-directory" do +directory 'git-directory' do path "#{platform_options["git_dir"]}/rings" - owner "swift" - group "swift" - mode "0755" + owner 'swift' + group 'swift' + mode '0755' recursive true action :create end -execute "initialize git repo" do +execute 'initialize git repo' do cwd "#{platform_options["git_dir"]}/rings" umask 022 - user "swift" - command "git init --bare && touch git-daemon-export-ok" + user 'swift' + command 'git init --bare && touch git-daemon-export-ok' creates "#{platform_options["git_dir"]}/rings/config" action :run - notifies :run, "execute[create empty git repo]", :immediately + notifies :run, 'execute[create empty git repo]', :immediately end # epel/f-17 missing systemd-ified inits # https://bugzilla.redhat.com/show_bug.cgi?id=737183 -template "/etc/systemd/system/git.service" do - owner "root" - group "root" - mode "0644" - source "simple-systemd-config.erb" - variables({ :description => "Git daemon service", - :user => "nobody", - :exec => "/usr/libexec/git-core/git-daemon " + - "--base-path=/var/lib/git --export-all --user-path=public_git" + - "--syslog --verbose" - }) - only_if { platform?(%w{fedora}) } +template '/etc/systemd/system/git.service' do + owner 'root' + group 'root' + mode '0644' + source 'simple-systemd-config.erb' + variables( + description: 'Git daemon service', + user: 'nobody', + exec: '/usr/libexec/git-core/git-daemon ' \ + '--base-path=/var/lib/git --export-all --user-path=public_git' \ + '--syslog --verbose' + ) + only_if { platform?('fedora') } end -case node["platform"] -when "centos","redhat","fedora" - service "git-daemon" do - service_name platform_options["git_service"] - action [ :enable ] +case node['platform'] +when 'centos', 'redhat', 'fedora' + service 'git-daemon' do + service_name platform_options['git_service'] + action [:enable] end -when "ubuntu","debian" - service "git-daemon" do - service_name platform_options["git_service"] - action [ :enable, :start ] +when 'ubuntu', 'debian' + service 'git-daemon' do + service_name platform_options['git_service'] + action [:enable, :start] end end -cookbook_file "/etc/default/git-daemon" do - owner "root" - group "root" - mode "644" - source "git-daemon.default" +cookbook_file '/etc/default/git-daemon' do + owner 'root' + group 'root' + mode '644' + source 'git-daemon.default' action :create - notifies :restart, "service[git-daemon]", :immediately - not_if { platform?(%w{fedora centos redhat}) } + notifies :restart, 'service[git-daemon]', :immediately + not_if { platform?('fedora', 'centos', 'redhat') } end -directory "/etc/swift/ring-workspace" do - owner "swift" - group "swift" - mode "0755" +directory '/etc/swift/ring-workspace' do + owner 'swift' + group 'swift' + mode '0755' action :create end -execute "checkout-rings" do - cwd "/etc/swift/ring-workspace" +execute 'checkout-rings' do + cwd '/etc/swift/ring-workspace' command "git clone file://#{platform_options["git_dir"]}/rings" - user "swift" - creates "/etc/swift/ring-workspace/rings" + user 'swift' + creates '/etc/swift/ring-workspace/rings' end -[ "account", "container", "object" ].each do |ring_type| +['account', 'container', 'object'].each do |ring_type| - part_power = ring_options["part_power"] - min_part_hours = ring_options["min_part_hours"] - replicas = ring_options["replicas"] + part_power = ring_options['part_power'] + min_part_hours = ring_options['min_part_hours'] + replicas = ring_options['replicas'] Chef::Log.info("Building initial ring #{ring_type} using part_power=#{part_power}, " + "min_part_hours=#{min_part_hours}, replicas=#{replicas}") execute "add #{ring_type}.builder" do - cwd "/etc/swift/ring-workspace/rings" + cwd '/etc/swift/ring-workspace/rings' command "git add #{ring_type}.builder && git commit -m 'initial ring builders' --author='chef '" - user "swift" + user 'swift' action :nothing end execute "create #{ring_type} builder" do - cwd "/etc/swift/ring-workspace/rings" + cwd '/etc/swift/ring-workspace/rings' command "swift-ring-builder #{ring_type}.builder create #{part_power} #{replicas} #{min_part_hours}" - user "swift" + user 'swift' creates "/etc/swift/ring-workspace/rings/#{ring_type}.builder" notifies :run, "execute[add #{ring_type}.builder]", :immediate end end -bash "rebuild-rings" do +bash 'rebuild-rings' do action :nothing - cwd "/etc/swift/ring-workspace/rings" - user "swift" + cwd '/etc/swift/ring-workspace/rings' + user 'swift' code <<-EOF set -x @@ -172,12 +175,11 @@ bash "rebuild-rings" do EOF end -openstack_object_storage_ring_script "/etc/swift/ring-workspace/generate-rings.sh" do - owner "swift" - group "swift" - mode "0700" - ring_path "/etc/swift/ring-workspace/rings" +openstack_object_storage_ring_script '/etc/swift/ring-workspace/generate-rings.sh' do + owner 'swift' + group 'swift' + mode '0700' + ring_path '/etc/swift/ring-workspace/rings' action :ensure_exists - notifies :run, "bash[rebuild-rings]", :immediate + notifies :run, 'bash[rebuild-rings]', :immediate end - diff --git a/chef/cookbooks/openstack-object-storage/recipes/rsync.rb b/chef/cookbooks/openstack-object-storage/recipes/rsync.rb index 4f0fbf4..ef8d2ef 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/rsync.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/rsync.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: rsync # # Copyright 2012, Rackspace US, Inc. @@ -17,69 +18,70 @@ # limitations under the License. # -platform_options = node["swift"]["platform"] +platform_options = node['openstack']['object-storage']['platform'] -platform_options["rsync_packages"].each do |pkg| +platform_options['rsync_packages'].each do |pkg| package pkg do - action :install - options platform_options["override_options"] + action :upgrade + options platform_options['override_options'] end end # epel/f-17 broken: https://bugzilla.redhat.com/show_bug.cgi?id=737710 -cookbook_file "/etc/systemd/system/rsync.service" do - owner "root" - group "root" - mode "0644" - source "rsync.service" +cookbook_file '/etc/systemd/system/rsync.service' do + owner 'root' + group 'root' + mode '0644' + source 'rsync.service' action :create - only_if { platform?(%w{fedora}) } + only_if { platform?('fedora') } end # rhel based systems install rsync and run it with rsync. We don't want to do that -cookbook_file "/etc/init.d/rsyncd" do - owner "root" - group "root" - mode "0755" - source "rsync.init" +cookbook_file '/etc/init.d/rsyncd' do + owner 'root' + group 'root' + mode '0755' + source 'rsync.init' action :create - only_if { platform?(%w{centos redhat scientific}) } + only_if { platform?('centos', 'redhat', 'scientific') } end # FIXME: chicken and egg -case node["platform"] -when "centos","redhat","fedora" +case node['platform'] +when 'centos', 'redhat', 'fedora' # enable rsyncd - rsync_servicename = "rsyncd" - service "rsyncd" do - supports :status => false, :restart => true, :start => true, :stop => true - action [ :enable, :start ] - only_if "[ -f /etc/rsyncd.conf ]" + rsync_servicename = 'rsyncd' + service 'rsyncd' do + supports status: false, restart: true, start: true, stop: true + action [:enable, :start] + only_if '[ -f /etc/rsyncd.conf ]' end # disable rsync (the one via xinetd) - service "rsync" do - supports :status => false, :restart => false, :start => false, :stop => false - action [ :disable ] + service 'rsync' do + supports status: false, restart: false, start: false, stop: false + action [:disable] end -when "ubuntu","debian" - rsync_servicename = "rsync" - service "rsync" do - supports :status => false, :restart => true - action [ :enable, :start ] - only_if "[ -f /etc/rsyncd.conf ]" +when 'ubuntu', 'debian' + rsync_servicename = 'rsync' + service 'rsync' do + supports status: false, restart: true + action [:enable, :start] + only_if '[ -f /etc/rsyncd.conf ]' end end -template "/etc/rsyncd.conf" do - source "rsyncd.conf.erb" - mode "0644" +template '/etc/rsyncd.conf' do + source 'rsyncd.conf.erb' + mode 0644 notifies :restart, "service[#{rsync_servicename}]", :immediately end -execute "enable rsync" do +execute 'enable rsync' do command "sed -i 's/RSYNC_ENABLE=false/RSYNC_ENABLE=true/' /etc/default/rsync" only_if "grep -q 'RSYNC_ENABLE=false' /etc/default/rsync" - notifies :restart, "service[rsync]", :immediately + notifies :restart, 'service[rsync]', :immediately action :run - not_if { platform?(%w{fedora centos redhat scientific}) } + # TODO(chrislaco) Convert these to platform_family? + not_if { platform?('fedora', 'centos', 'redhat', 'scientific') } end diff --git a/chef/cookbooks/openstack-object-storage/recipes/setup.rb b/chef/cookbooks/openstack-object-storage/recipes/setup.rb index 7558914..daae735 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/setup.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/setup.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: setup # # Copyright 2012, Rackspace US, Inc. @@ -16,43 +17,67 @@ # See the License for the specific language governing permissions and # limitations under the License. -include_recipe "openstack-object-storage::common" +include_recipe 'openstack-object-storage::common' # make sure we die if there are multiple swift-setups if Chef::Config[:solo] - Chef::Application.fatal! "This recipe uses search. Chef Solo does not support search." + Chef::Application.fatal! 'This recipe uses search. Chef Solo does not support search.' else - setup_role_count = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-setup").length + setup_role = node['openstack']['object-storage']['setup_chef_role'] + setup_role_count = search(:node, "chef_environment:#{node.chef_environment} AND roles:#{setup_role}").length if setup_role_count > 1 - Chef::Application.fatal! "You can only have one node with the swift-setup role" + Chef::Application.fatal! 'You can only have one node with the swift-setup role' end end -unless node["swift"]["service_pass"] - Chef::Log.info("Running swift setup - setting swift passwords") +unless node['openstack']['object-storage']['service_pass'] + Chef::Log.info('Running swift setup - setting swift passwords') end -platform_options = node["swift"]["platform"] +platform_options = node['openstack']['object-storage']['platform'] # install platform-specific packages -platform_options["proxy_packages"].each do |pkg| +platform_options['proxy_packages'].each do |pkg| package pkg do action :upgrade - options platform_options["override_options"] + options platform_options['override_options'] end end -package "python-swauth" do - action :upgrade - only_if { node["swift"]["authmode"] == "swauth" } +if node['openstack']['object-storage']['authmode'] == 'swauth' + case node['openstack']['object-storage']['swauth_source'] + when 'package' + platform_options['swauth_packages'].each do |pkg| + package pkg do + action :upgrade + options platform_options['override_options'] + end + end + when 'git' + git "#{Chef::Config[:file_cache_path]}/swauth" do + repository node['openstack']['object-storage']['swauth_repository'] + revision node['openstack']['object-storage']['swauth_version'] + action :sync + end + + bash 'install_swauth' do + cwd "#{Chef::Config[:file_cache_path]}/swauth" + user 'root' + group 'root' + code <<-EOH + python setup.py install + EOH + environment 'PREFIX' => '/usr/local' + end + end end -package "python-swift-informant" do +package 'python-swift-informant' do action :upgrade - only_if { node["swift"]["use_informant"] } + only_if { node['openstack']['object-storage']['use_informant'] } end -package "python-keystone" do +package 'python-keystoneclient' do action :upgrade - only_if { node["swift"]["authmode"] == "keystone" } + only_if { node['openstack']['object-storage']['authmode'] == 'keystone' } end diff --git a/chef/cookbooks/openstack-object-storage/recipes/storage-common.rb b/chef/cookbooks/openstack-object-storage/recipes/storage-common.rb index 4fb834f..0ac8b74 100644 --- a/chef/cookbooks/openstack-object-storage/recipes/storage-common.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/storage-common.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Recipe:: storage-common # # Copyright 2012, Rackspace US, Inc. @@ -17,24 +18,24 @@ # limitations under the License. # -include_recipe "openstack-object-storage::rsync" +include_recipe 'openstack-object-storage::rsync' -template "/etc/swift/drive-audit.conf" do - source "drive-audit.conf.erb" - owner "swift" - group "swift" - mode "0600" +template '/etc/swift/drive-audit.conf' do + source 'drive-audit.conf.erb' + owner 'swift' + group 'swift' + mode 0600 end -cron "drive-audit" do - hour node["swift"]["audit_hour"] - minute "10" - command "swift-drive-audit /etc/swift/drive-audit.conf" +cron 'drive-audit' do + hour node['openstack']['object-storage']['audit_hour'] + minute '10' + command 'swift-drive-audit /etc/swift/drive-audit.conf' end -directory "/var/cache/swift" do - group "swift" - owner "swift" +directory '/var/cache/swift' do + group 'swift' + owner 'swift' recursive true action :create mode 00700 diff --git a/chef/cookbooks/openstack-object-storage/recipes/swift-config-ceph.rb b/chef/cookbooks/openstack-object-storage/recipes/swift-config-ceph.rb new file mode 100644 index 0000000..174303c --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/swift-config-ceph.rb @@ -0,0 +1,100 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-object-storage +# Recipe:: swift-config-ceph +# +# Copyright 2014, Liucheng. +# +# 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. +# + +#create swift endpoint to ceph + +require 'uri' + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +identity_admin_endpoint = endpoint 'identity-admin' + +token = get_secret 'openstack_identity_bootstrap_token' +auth_url = ::URI.decode identity_admin_endpoint.to_s + +swift_endpoint = "http://#{node['ceph']['radosgw domain']}/swift/v1" + +service_pass = get_password 'service', 'openstack-object-storage' +service_tenant_name = 'service' +service_user = 'swift' +service_role = 'admin' +region = 'RegionOne' + +# Register Image Service +openstack_identity_register 'Register Object Storage Service' do + auth_uri auth_url + bootstrap_token token + service_name 'swift' + service_type 'object-store' + service_description 'Object Storage Service' + + action :create_service +end + +# Register Image Endpoint +openstack_identity_register 'Register Object Storage Endpoint' do + auth_uri auth_url + bootstrap_token token + service_type 'object-store' + endpoint_region region + endpoint_adminurl swift_endpoint + endpoint_internalurl swift_endpoint + endpoint_publicurl swift_endpoint + + action :create_endpoint +end + +# Register Service Tenant +openstack_identity_register 'Register Service Tenant' do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + tenant_description 'Service Tenant' + tenant_enabled true # Not required as this is the default + + action :create_tenant +end + +# Register Service User +openstack_identity_register "Register #{service_user} User" do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + user_name service_user + user_pass service_pass + # String until https://review.openstack.org/#/c/29498/ merged + user_enabled true + + action :create_user +end + +## Grant Admin role to Service User for Service Tenant ## +openstack_identity_register "Grant '#{service_role}' Role to #{service_user} User for #{service_tenant_name} Tenant" do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + user_name service_user + role_name service_role + + action :grant_role +end + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_listed_host.rb b/chef/cookbooks/openstack-object-storage/recipes/swiftclient-patch.rb similarity index 57% rename from chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_listed_host.rb rename to chef/cookbooks/openstack-object-storage/recipes/swiftclient-patch.rb index e9ac4f3..cc37208 100644 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_listed_host.rb +++ b/chef/cookbooks/openstack-object-storage/recipes/swiftclient-patch.rb @@ -1,8 +1,9 @@ +# encoding: UTF-8 # -# Cookbook Name:: apache2_test -# Recipe:: mod_authz_listed_host +# Cookbook Name:: openstack-object-storage +# Recipe:: swift-config-ceph # -# Copyright 2012, Opscode, Inc. +# Copyright 2014, Liucheng. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,14 +18,9 @@ # limitations under the License. # -include_recipe "apache2::default" -include_recipe "apache2::mod_authz_host" +#create swift endpoint to ceph -directory "#{node['apache_test']['root_dir']}/secure" do - action :create -end - -web_app "secure" do - template "authz_host.conf.erb" - remote_host_ip node['apache_test']['remote_host_ip'] -end +execute 'modify swiftclient' do + command "sed -i 's/header = header.lower()/#header = header.lower()/g' /usr/lib/python2.6/site-packages/swiftclient/client.py" + not_if "grep '#header = header.lower()' /usr/lib/python2.6/site-packages/swiftclient/client.py" +end \ No newline at end of file diff --git a/chef/cookbooks/openstack-object-storage/resources/disk.rb b/chef/cookbooks/openstack-object-storage/resources/disk.rb index ad9023f..1209fde 100644 --- a/chef/cookbooks/openstack-object-storage/resources/disk.rb +++ b/chef/cookbooks/openstack-object-storage/resources/disk.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Copyright 2011, Dell # @@ -16,18 +17,16 @@ # Author: andi abes # -=begin - Ensure that a disk's partition table matches expectations. - Sample use: - - openstack_object_storage_disk "/dev/sdb" do - part( - {[:type => "xfs", :size =>swift_disk::ONE_GIG*4 ], - [:type => "xfs", :size =>swift_disk::remaining}) - action :ensure_exists - end - -=end +# Ensure that a disk's partition table matches expectations. +# Sample use: +# +# openstack_object_storage_disk '/dev/sdb' do +# part([ +# { type: "xfs", size: swift_disk::ONE_GIG*4 }, +# { type: "xfs", size: swift_disk::remaining } +# ]) +# action :ensure_exists +# end actions :ensure_exists @@ -36,9 +35,9 @@ def initialize(*args) @action = :ensure_exists end -attribute :name, :kind_of => String -attribute :size, :kind_of => Integer -attribute :blocks, :kind_of => Integer -attribute :device, :kind_of => String -attribute :part, :kind_of => Array -attribute :status, :kind_of => Symbol +attribute :name, kind_of: String +attribute :size, kind_of: Integer +attribute :blocks, kind_of: Integer +attribute :device, kind_of: String +attribute :part, kind_of: Array +attribute :status, kind_of: Symbol diff --git a/chef/cookbooks/openstack-object-storage/resources/mounts.rb b/chef/cookbooks/openstack-object-storage/resources/mounts.rb index d8c7443..8ec53f7 100644 --- a/chef/cookbooks/openstack-object-storage/resources/mounts.rb +++ b/chef/cookbooks/openstack-object-storage/resources/mounts.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: swift +# Cookbook Name:: openstack-object-storage # Resource:: mounts # # Copyright 2012, Rackspace US, Inc. @@ -19,43 +20,40 @@ # Author: Ron Pedde # -=begin - Ensure that swift mounts are strongly enforced. This - will ensure specified drives are mounted, and unspecified - drives are not mounted. In addition, if there is a stale - mountpoint (from disk failure, maybe?), then that mountpoint - will try to be unmounted +# Ensure that swift mounts are strongly enforced. This +# will ensure specified drives are mounted, and unspecified +# drives are not mounted. In addition, if there is a stale +# mountpoint (from disk failure, maybe?), then that mountpoint +# will try to be unmounted +# +# Sample use: +# +# openstack_object_storage_mounts '/srv/node' do +# devices [ 'sdb1', 'sdc1' ] +# action :ensure_exists +# ip '10.1.1.1' +# end - Sample use: +# It will force mounts based on fs uuid (mangled to remove +# dashes) and return a structure that describes the disks +# mounted. - openstack_object_storage_mounts "/srv/node" do - devices [ "sdb1", "sdc1" ] - action :ensure_exists - ip "10.1.1.1" - end +# As this is expected to be consumed for the purposes of +# swift, the ip address should be the address that gets +# embedded into the ring (i.e. the listen port of the storage server) - It will force mounts based on fs uuid (mangled to remove - dashes) and return a structure that describes the disks - mounted. +# Example return structure: - As this is expected to be consumed for the purposes of - swift, the ip address should be the address that gets - embedded into the ring (i.e. the listen port of the storage server) - - Example return structure: - - { "2a9452c5-d929-43d9-9631-4340ace45279": { - "device": "sdb1", - "ip": "10.1.1.1", - "mounted": "true", - "mountpoint": "2a9452c5d92943d996314340ace45279", - "size": 1022 (in 1k increments) - "uuid": "2a9452c5-d929-43d9-9631-4340ace45279" - }, - ... - } - -=end +# { '2a9452c5-d929-43d9-9631-4340ace45279': { +# 'device': 'sdb1', +# 'ip': '10.1.1.1', +# 'mounted': 'true', +# 'mountpoint': '2a9452c5d92943d996314340ace45279', +# 'size': 1022 (in 1k increments) +# 'uuid': '2a9452c5-d929-43d9-9631-4340ace45279' +# }, +# ... +# } actions :ensure_exists @@ -64,8 +62,8 @@ def initialize(*args) @action = :ensure_exists end -attribute :name, :kind_of => String -attribute :devices, :kind_of => Array -attribute :ip, :kind_of => String, :default => "127.0.0.1" -attribute :publish_attributes, :kind_of => String, :default => nil -attribute :format, :kind_of => String, :default => "xfs" +attribute :name, kind_of: String +attribute :devices, kind_of: Array +attribute :ip, kind_of: String, default: '127.0.0.1' +attribute :publish_attributes, kind_of: String, default: nil +attribute :format, kind_of: String, default: 'xfs' diff --git a/chef/cookbooks/openstack-object-storage/resources/ring_script.rb b/chef/cookbooks/openstack-object-storage/resources/ring_script.rb index 9a6ad30..66f2d81 100644 --- a/chef/cookbooks/openstack-object-storage/resources/ring_script.rb +++ b/chef/cookbooks/openstack-object-storage/resources/ring_script.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Copyright 2012, Rackspace US, Inc. # @@ -16,19 +17,16 @@ # Author: Ron Pedde # -=begin - Build a proposed ring-building script - Sample use: - - openstack_object_storage_ring_script "/tmp/build-rings.sh" do - owner "root" - group "swift" - mode "0700" - ring_path "/etc/swift/ring-workspace" - action :ensure_exists - end - -=end +# Build a proposed ring-building script +# Sample use: +# +# openstack_object_storage_ring_script '/tmp/build-rings.sh' do +# owner 'root' +# group 'swift' +# mode '0700' +# ring_path '/etc/swift/ring-workspace' +# action :ensure_exists +# end actions :ensure_exists @@ -37,8 +35,8 @@ def initialize(*args) @action = :ensure_exists end -attribute :name, :kind_of => String -attribute :owner, :kind_of => String, :default => "root" -attribute :group, :kind_of => String, :default => "root" -attribute :mode, :kind_of => String, :default => "0600" -attribute :ring_path, :kind_of => String, :default => "/etc/swift" +attribute :name, kind_of: String +attribute :owner, kind_of: String, default: 'root' +attribute :group, kind_of: String, default: 'root' +attribute :mode, kind_of: String, default: '0600' +attribute :ring_path, kind_of: String, default: '/etc/swift' diff --git a/chef/cookbooks/openstack-object-storage/spec/account_spec.rb b/chef/cookbooks/openstack-object-storage/spec/account_spec.rb index 6a796a5..85360b8 100644 --- a/chef/cookbooks/openstack-object-storage/spec/account_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/account_spec.rb @@ -1,67 +1,58 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::account-server' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['lsb']['code'] = 'precise' - @node.set['swift']['authmode'] = 'swauth' - @node.set['swift']['network']['account-bind-ip'] = '10.0.0.1' - @node.set['swift']['network']['account-bind-port'] = '8080' - @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]" - @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/", - "File.exist?('/dev/' + candidate)", - "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", - "not info.has_key?('removable') or info['removable'] == 0.to_s"] - + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['object-storage']['disk_enum_expr'] = "[{ 'sda' => {}}]" + node.set['openstack']['object-storage']['disk_test_filter'] = [ + 'candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/', + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] # mock out an interface on the storage node - @node.set["network"] = MOCK_NODE_NETWORK_DATA['network'] + node.set['network'] = MOCK_NODE_NETWORK_DATA['network'] - @chef_run.converge "openstack-object-storage::account-server" + runner.converge(described_recipe) end - it "installs swift account packages" do - expect(@chef_run).to install_package "swift-account" + include_context 'swift-stubs' + + it 'upgrades swift account packages' do + expect(chef_run).to upgrade_package('swift-account') end - it "installs swiftclient package" do - expect(@chef_run).to install_package "python-swiftclient" + it 'upgrades swiftclient package' do + expect(chef_run).to upgrade_package('python-swiftclient') end - it "starts swift account services on boot" do + it 'starts swift account services on boot' do %w{swift-account swift-account-auditor swift-account-reaper swift-account-replicator}.each do |svc| - expect(@chef_run).to set_service_to_start_on_boot svc + expect(chef_run).to enable_service(svc) end end - describe "/etc/swift/account-server.conf" do + describe '/etc/swift/account-server.conf' do + let(:file) { chef_run.template('/etc/swift/account-server.conf') } - before do - @file = @chef_run.template "/etc/swift/account-server.conf" + it 'creates account-server.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'swift', + group: 'swift', + mode: 0600 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" + { 'bind_ip' => '0.0.0.0', + 'bind_port' => '6002', + 'log_statsd_default_sample_rate' => '1', + 'log_statsd_metric_prefix' => 'openstack.swift.Fauxhai' }.each do |k, v| + it "sets the #{k}" do + expect(chef_run).to render_file(file.name).with_content(/^#{Regexp.quote("#{k} = #{v}")}$/) + end end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "600" - end - - it "template contents" do - pending "TODO: implement" - end - end - end - end diff --git a/chef/cookbooks/openstack-object-storage/spec/client_spec.rb b/chef/cookbooks/openstack-object-storage/spec/client_spec.rb new file mode 100644 index 0000000..546115d --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/client_spec.rb @@ -0,0 +1,16 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-object-storage::client' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'installs packages' do + expect(chef_run).to upgrade_package('python-swiftclient') + end + end +end diff --git a/chef/cookbooks/openstack-object-storage/spec/common_spec.rb b/chef/cookbooks/openstack-object-storage/spec/common_spec.rb index 27bb021..a332656 100644 --- a/chef/cookbooks/openstack-object-storage/spec/common_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/common_spec.rb @@ -1,92 +1,93 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::common' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['platform_family'] = "debian" - @node.set['lsb']['codename'] = "precise" - @node.set['swift']['release'] = "folsom" - @node.set['swift']['authmode'] = 'swauth' - @node.set['swift']['git_builder_ip'] = '10.0.0.10' - - # TODO: this does not work - # ::Chef::Log.should_receive(:info).with("chefspec: precise-updates/folsom") - - @chef_run.converge "openstack-object-storage::common" + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end + include_context 'swift-stubs' - it 'should set syctl paramaters' do - # N.B. we could examine chef log - pending "TODO: right now theres no way to do lwrp and test for this" + it 'includes openstack-common::sysctl' do + expect(chef_run).to include_recipe('openstack-common::sysctl') end - it 'installs git package for ring management' do - expect(@chef_run).to install_package "git" + describe '60-openstack.conf' do + let(:file) { chef_run.template('/etc/sysctl.d/60-openstack.conf') } + + it 'creates /etc/sysctl.d/60-openstack.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 0644 + ) + end + + it 'sets the net.ipv4.tcp_tw_recycle' do + match = 'net.ipv4.tcp_tw_recycle = 1' + expect(chef_run).to render_file(file.name).with_content(match) + end + + it 'sets the net.ipv4.tcp_tw_reuse' do + match = 'net.ipv4.tcp_tw_reuse = 1' + expect(chef_run).to render_file(file.name).with_content(match) + end + + it 'sets the net.ipv4.tcp_syncookies' do + match = 'net.ipv4.tcp_syncookies = 0' + expect(chef_run).to render_file(file.name).with_content(match) + end end - describe "/etc/swift" do - - before do - @file = @chef_run.directory "/etc/swift" - end - - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "700" - end - + it 'upgrades git package for ring management' do + expect(chef_run).to upgrade_package('git') end - describe "/etc/swift/swift.conf" do + describe '/etc/swift' do + let(:dir) { chef_run.directory('/etc/swift') } - before do - @file = @chef_run.file "/etc/swift/swift.conf" + it 'creates /etc/swift' do + expect(chef_run).to create_directory(dir.name).with( + user: 'swift', + group: 'swift', + mode: 0700 + ) end - - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "700" - end - end - describe "/etc/swift/pull-rings.sh" do + describe '/etc/swift/swift.conf' do + let(:file) { chef_run.file('/etc/swift/swift.conf') } - before do - @file = @chef_run.template "/etc/swift/pull-rings.sh" + it 'creates swift.conf' do + expect(chef_run).to create_file(file.name).with( + user: 'swift', + group: 'swift', + mode: 0700 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" + it 'template contents' do + pending 'TODO: implement' end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "700" - end - - it "template contents" do - pending "TODO: implement" - end - end + describe '/etc/swift/pull-rings.sh' do + let(:file) { chef_run.template('/etc/swift/pull-rings.sh') } + + it 'creates pull-rings.sh' do + expect(chef_run).to create_template(file.name).with( + user: 'swift', + group: 'swift', + mode: 0700 + ) + end + + it 'template contents' do + pending 'TODO: implement' + end + end end - - end diff --git a/chef/cookbooks/openstack-object-storage/spec/container_spec.rb b/chef/cookbooks/openstack-object-storage/spec/container_spec.rb index f56932d..d29f715 100644 --- a/chef/cookbooks/openstack-object-storage/spec/container_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/container_spec.rb @@ -1,63 +1,87 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::container-server' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['lsb']['code'] = 'precise' - @node.set['swift']['authmode'] = 'swauth' - @node.set['swift']['network']['container-bind-ip'] = '10.0.0.1' - @node.set['swift']['network']['container-bind-port'] = '8080' - @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]" - @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/", - "File.exist?('/dev/' + candidate)", - "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", - "not info.has_key?('removable') or info['removable'] == 0.to_s"] - + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['object-storage']['container-server']['allowed_sync_hosts'] = ['host1', 'host2', 'host3'] + node.set['openstack']['object-storage']['disk_enum_expr'] = "[{ 'sda' => {}}]" + node.set['openstack']['object-storage']['disk_test_filter'] = [ + 'candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/', + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] # mock out an interface on the storage node - @node.set["network"] = MOCK_NODE_NETWORK_DATA['network'] - - @chef_run.converge "openstack-object-storage::container-server" + node.set['network'] = MOCK_NODE_NETWORK_DATA['network'] + runner.converge(described_recipe) end - it "installs swift container packages" do - expect(@chef_run).to install_package "swift-container" + include_context 'swift-stubs' + + it 'upgrades swift container packages' do + expect(chef_run).to upgrade_package('swift-container') end - it "starts swift container services on boot" do - %w{swift-container swift-container-auditor swift-container-replicator swift-container-updater}.each do |svc| - expect(@chef_run).to set_service_to_start_on_boot svc + it 'starts swift container services on boot' do + %w{swift-container swift-container-auditor swift-container-replicator swift-container-updater swift-container-sync}.each do |svc| + expect(chef_run).to enable_service(svc) end end - describe "/etc/swift/container-server.conf" do + describe '/etc/swift/container-server.conf' do + let(:file) { chef_run.template('/etc/swift/container-server.conf') } + + it 'creates account-server.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'swift', + group: 'swift', + mode: 0600 + ) + end + + it 'has allowed sync hosts' do + expect(chef_run).to render_file(file.name).with_content('allowed_sync_hosts = host1,host2,host3') + end + + { 'bind_ip' => '0.0.0.0', + 'bind_port' => '6001', + 'log_statsd_default_sample_rate' => '1', + 'log_statsd_metric_prefix' => 'openstack.swift.Fauxhai' }.each do |k, v| + it "sets the #{k}" do + expect(chef_run).to render_file(file.name).with_content(/^#{Regexp.quote("#{k} = #{v}")}$/) + end + end + end + + describe 'container sync' do + let(:file) { chef_run.cookbook_file('/etc/init/swift-container-sync.conf') } + let(:link) { chef_run.link('/etc/init.d/swift-container-sync') } + + it 'creates /etc/init/swift-container-sync.conf' do + expect(chef_run).to create_cookbook_file(file.name).with( + user: 'root', + group: 'root', + mode: 0755 + ) + end + + it 'creates /etc/init.d/swift-container-sync' do + expect(chef_run).to create_link(link.name) + end + end + + describe '/etc/swift/container-server.conf' do + let(:file) { chef_run.template('/etc/swift/container-server.conf') } before do - @file = @chef_run.template "/etc/swift/container-server.conf" + node.set['openstack']['object-storage']['container-server']['allowed_sync_hosts'] = [] end - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" + it 'has no allowed_sync_hosts on empty lists' do + expect(chef_run).not_to render_file(file.name).with_content(/^allowed_sync_hots =/) end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "600" - end - - it "template contents" do - pending "TODO: implement" - end - end - end - end diff --git a/chef/cookbooks/openstack-object-storage/spec/disks_spec.rb b/chef/cookbooks/openstack-object-storage/spec/disks_spec.rb index 03e7ccc..b178088 100644 --- a/chef/cookbooks/openstack-object-storage/spec/disks_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/disks_spec.rb @@ -1,43 +1,33 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::disks' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['platform_family'] = "debian" - @node.set['lsb']['codename'] = "precise" - @node.set['swift']['release'] = "folsom" - @node.set['swift']['authmode'] = 'swauth' - @node.set['swift']['git_builder_ip'] = '10.0.0.10' - @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]" - @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/", - "File.exist?('/dev/' + candidate)", - "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", - "not info.has_key?('removable') or info['removable'] == 0.to_s"] - + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['object-storage']['disk_enum_expr'] = "[{ 'sda' => {}}]" + node.set['openstack']['object-storage']['disk_test_filter'] = [ + 'candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/', + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] # mock out an interface on the storage node - @node.set["network"] = MOCK_NODE_NETWORK_DATA['network'] + node.set['network'] = MOCK_NODE_NETWORK_DATA['network'] - @chef_run.converge "openstack-object-storage::disks" + runner.converge(described_recipe) end - it 'installs xfs progs package' do - expect(@chef_run).to install_package "xfsprogs" + include_context 'swift-stubs' + + it 'upgrades xfs progs package' do + expect(chef_run).to upgrade_package('xfsprogs') end - it 'installs parted package' do - expect(@chef_run).to install_package "parted" + it 'upgrades parted package' do + expect(chef_run).to upgrade_package('parted') end end - end diff --git a/chef/cookbooks/openstack-object-storage/spec/management_spec.rb b/chef/cookbooks/openstack-object-storage/spec/management_spec.rb index c7724b6..9b75f4e 100644 --- a/chef/cookbooks/openstack-object-storage/spec/management_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/management_spec.rb @@ -1,47 +1,52 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::management-server' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['lsb']['code'] = 'precise' - @node.set['swift']['authmode'] = 'swauth' - - @chef_run.converge "openstack-object-storage::management-server" + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it "installs swift swauth package" do - expect(@chef_run).to install_package "swauth" + include_context 'swift-stubs' + + it 'upgrades swift swauth package' do + expect(chef_run).to upgrade_package 'swauth' end - describe "/etc/swift/dispersion.conf" do + describe '/etc/swift/dispersion.conf' do + let(:file) { chef_run.template('/etc/swift/dispersion.conf') } - before do - @file = @chef_run.template "/etc/swift/dispersion.conf" + it 'creates dispersion.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'swift', + group: 'swift', + mode: 0600 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" + it 'template contents' do + pending 'TODO: implement' end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "600" - end - - it "template contents" do - pending "TODO: implement" - end - end + describe '/usr/local/bin/swift-statsd-publish.py' do + let(:file) { chef_run.template('/usr/local/bin/swift-statsd-publish.py') } + + it 'creates /usr/local/bin/swift-statsd-publish.py' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 0755 + ) + end + + it 'has expected statsd host' do + expect(chef_run).to render_file(file.name).with_content( + "self.statsd_host = '127.0.0.1'" + ) + end + end end - end diff --git a/chef/cookbooks/openstack-object-storage/spec/object_spec.rb b/chef/cookbooks/openstack-object-storage/spec/object_spec.rb index b7c75a3..1b37c89 100644 --- a/chef/cookbooks/openstack-object-storage/spec/object_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/object_spec.rb @@ -1,71 +1,60 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::object-server' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['lsb']['code'] = 'precise' - @node.set['swift']['authmode'] = 'swauth' - @node.set['swift']['network']['object-bind-ip'] = '10.0.0.1' - @node.set['swift']['network']['object-bind-port'] = '8080' - @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]" - @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/", - "File.exist?('/dev/' + candidate)", - "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", - "not info.has_key?('removable') or info['removable'] == 0.to_s"] - + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set['openstack']['object-storage']['disk_enum_expr'] = "[{ 'sda' => {}}]" + node.set['openstack']['object-storage']['disk_test_filter'] = [ + 'candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/', + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] # mock out an interface on the storage node - @node.set["network"] = MOCK_NODE_NETWORK_DATA['network'] + node.set['network'] = MOCK_NODE_NETWORK_DATA['network'] - @chef_run.converge "openstack-object-storage::object-server" + runner.converge(described_recipe) end - it "installs swift packages" do - expect(@chef_run).to install_package "swift-object" + include_context 'swift-stubs' + + it 'upgrades swift packages' do + expect(chef_run).to upgrade_package('swift-object') end - it "starts swift object services on boot" do + it 'starts swift object services on boot' do %w{swift-object swift-object-replicator swift-object-auditor swift-object-updater}.each do |svc| - expect(@chef_run).to set_service_to_start_on_boot svc + expect(chef_run).to enable_service(svc) end end - describe "/var/spool/crontab/root" do - - it "template contents" do - pending "TODO: check for recon script" + describe '/var/spool/crontab/root' do + it 'template contents' do + pending 'TODO: check for recon script' end - end - describe "/etc/swift/object-server.conf" do + describe '/etc/swift/object-server.conf' do + let(:file) { chef_run.template('/etc/swift/object-server.conf') } - before do - @file = @chef_run.template "/etc/swift/object-server.conf" + it 'creates object-server.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'swift', + group: 'swift', + mode: 0600 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" + { 'bind_ip' => '0.0.0.0', + 'bind_port' => '6000', + 'log_statsd_default_sample_rate' => '1', + 'log_statsd_metric_prefix' => 'openstack.swift.Fauxhai' }.each do |k, v| + it "sets the #{k}" do + expect(chef_run).to render_file(file.name).with_content(/^#{Regexp.quote("#{k} = #{v}")}$/) + end end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "600" - end - - it "template contents" do - pending "TODO: implement" - end - end - end - end diff --git a/chef/cookbooks/openstack-object-storage/spec/proxy_spec.rb b/chef/cookbooks/openstack-object-storage/spec/proxy_spec.rb index 18d6f1f..56860b2 100644 --- a/chef/cookbooks/openstack-object-storage/spec/proxy_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/proxy_spec.rb @@ -1,60 +1,123 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::proxy-server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.automatic['cpu']['total'] = 6 - #-------------- - # UBUNTU - #-------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['lsb']['code'] = 'precise' - @node.set['swift']['authmode'] = 'swauth' - @node.set['swift']['network']['proxy-bind-ip'] = '10.0.0.1' - @node.set['swift']['network']['proxy-bind-port'] = '8080' - @chef_run.converge "openstack-object-storage::proxy-server" + runner.converge(described_recipe) end - it "installs memcache python packages" do - expect(@chef_run).to install_package "python-memcache" + include_context 'swift-stubs' + include_examples 'keystone-authmode' + + it 'upgrades memcache python packages' do + expect(chef_run).to upgrade_package('python-memcache') end - it "installs swift packages" do - expect(@chef_run).to install_package "swift-proxy" + it 'upgrades swift packages' do + expect(chef_run).to upgrade_package('swift-proxy') end - it "installs swauth package if swauth is selected" do - expect(@chef_run).to install_package "python-swauth" + it 'upgrades swauth package if swauth is selected' do + expect(chef_run).to upgrade_package('swauth') end - it "starts swift-proxy on boot" do - expect(@chef_run).to set_service_to_start_on_boot "swift-proxy" + it 'starts swift-proxy on boot' do + expect(chef_run).to enable_service('swift-proxy') end - describe "/etc/swift/proxy-server.conf" do + describe '/etc/swift/proxy-server.conf' do + let(:file) { chef_run.template('/etc/swift/proxy-server.conf') } - before do - @file = @chef_run.template "/etc/swift/proxy-server.conf" + it 'creates proxy-server.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'swift', + group: 'swift', + mode: 0600 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" + it 'has proper pipeline in template' do + array = [ + /^pipeline = catch_errors healthcheck cache ratelimit swauth proxy-logging proxy-server$/, + /^workers = 5$/ + ] + array.each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "600" + context 'with domain_remap enabled' do + before do + node.set['openstack']['object-storage']['domain_remap']['enabled'] = true + end + + it 'has proper pipeline in template' do + array = [ + /^pipeline = catch_errors healthcheck cache ratelimit domain_remap swauth proxy-logging proxy-server$/, + /^storage_domain = example.com$/, + /^path_root = v1$/, + /^reseller_prefixes = AUTH$/ + ] + array.each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + end end - it "template contents" do - pending "TODO: implement" + context 'with formpost enabled' do + before do + node.set['openstack']['object-storage']['formpost']['enabled'] = true + end + + it 'has proper pipeline in template' do + array = [ + /^pipeline = catch_errors healthcheck cache ratelimit formpost swauth proxy-logging proxy-server$/ + ] + array.each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + end end + context 'with staticweb enabled' do + before do + node.set['openstack']['object-storage']['staticweb']['enabled'] = true + end + + it 'has proper pipeline in template' do + array = [ + /^pipeline = catch_errors healthcheck cache ratelimit swauth staticweb proxy-logging proxy-server$/, + /^cache_timeout = 300$/ + ] + array.each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + end + end + + context 'with tempurl enabled' do + before do + node.set['openstack']['object-storage']['tempurl']['enabled'] = true + end + + it 'has proper pipeline in template' do + array = [ + /^pipeline = catch_errors healthcheck cache ratelimit tempurl swauth proxy-logging proxy-server$/, + /^incoming_remove_headers = x-timestamp$/, + /^incoming_allow_headers = $/, + 'outgoing_remove_headers = x-object-meta-*', + 'outgoing_allow_headers = x-object-meta-public-*' + ] + array.each do |content| + expect(chef_run).to render_file(file.name).with_content(content) + end + end + end end - end - end diff --git a/chef/cookbooks/openstack-object-storage/spec/ring-repo_spec.rb b/chef/cookbooks/openstack-object-storage/spec/ring-repo_spec.rb index 7f75501..0561892 100644 --- a/chef/cookbooks/openstack-object-storage/spec/ring-repo_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/ring-repo_spec.rb @@ -1,43 +1,33 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::ring-repo' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['platform_family'] = "debian" - @node.set['lsb']['codename'] = "precise" - @node.set['swift']['release'] = "folsom" - @node.set['swift']['authmode'] = 'swauth' - @node.set['swift']['git_builder_ip'] = '10.0.0.10' - @chef_run.converge "openstack-object-storage::ring-repo" + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it 'installs git package for ring management' do - expect(@chef_run).to install_package "git-daemon-sysvinit" + include_context 'swift-stubs' + + it 'upgrades git package for ring management' do + expect(chef_run).to upgrade_package('git-daemon-sysvinit') end - it "starts xinetd services on boot" do + it 'should not start xinetd services on boot' do %w{xinetd}.each do |svc| - expect(@chef_run).to set_service_to_start_on_boot svc + expect(chef_run).not_to enable_service(svc) end end - describe "/etc/swift/ring-workspace/generate-rings.sh" do - - it "gets installed" do - pending "TODO: determine some way to ensure this LWRP script gets created" + # FIXME(galstrom21): This spec file should just check that the LWRP + # is called with the appropriate paramaters. It should not be checking + # the file contents. + describe '/etc/swift/ring-workspace/generate-rings.sh' do + it 'gets installed' do + pending 'TODO: determine some way to ensure this LWRP script gets created' end - end - end - end diff --git a/chef/cookbooks/openstack-object-storage/spec/rsync_spec.rb b/chef/cookbooks/openstack-object-storage/spec/rsync_spec.rb index bc894ce..46800f5 100644 --- a/chef/cookbooks/openstack-object-storage/spec/rsync_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/rsync_spec.rb @@ -1,51 +1,38 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::rsync' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['platform_family'] = "debian" - @node.set['lsb']['codename'] = "precise" - @node.set['swift']['release'] = "folsom" - @node.set['swift']['authmode'] = 'swauth' - @node.set['swift']['git_builder_ip'] = '10.0.0.10' - @chef_run.converge "openstack-object-storage::rsync" + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - it 'installs git package for ring management' do - expect(@chef_run).to install_package "rsync" + include_context 'swift-stubs' + + it 'upgrades git package for ring management' do + expect(chef_run).to upgrade_package('rsync') end - it "starts rsync service on boot" do + it 'starts rsync service on boot' do %w{rsync}.each do |svc| - expect(@chef_run).to set_service_to_start_on_boot svc + expect(chef_run).to enable_service(svc) end end - describe "/etc/rsyncd.conf" do + describe '/etc/rsyncd.conf' do + let(:file) { chef_run.template('/etc/rsyncd.conf') } - before do - @file = @chef_run.template "/etc/rsyncd.conf" + it 'creates /etc/rsyncd.conf' do + expect(chef_run).to create_template(file.name).with( + mode: 0644 + ) end - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "644" + it 'template contents' do + pending 'TODO: implement' end - - it "template contents" do - pending "TODO: implement" - end - end - end - end diff --git a/chef/cookbooks/openstack-object-storage/spec/setup_spec.rb b/chef/cookbooks/openstack-object-storage/spec/setup_spec.rb new file mode 100755 index 0000000..091c7c3 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/setup_spec.rb @@ -0,0 +1,17 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-object-storage::setup' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + include_context 'swift-stubs' + include_examples 'keystone-authmode' + + # TODO: flush out rest of this spec + end +end diff --git a/chef/cookbooks/openstack-object-storage/spec/spec_helper.rb b/chef/cookbooks/openstack-object-storage/spec/spec_helper.rb index a70cbcc..8c2d5aa 100644 --- a/chef/cookbooks/openstack-object-storage/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-object-storage/spec/spec_helper.rb @@ -1,49 +1,83 @@ -require "chefspec" +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' +require 'chef/application' -::LOG_LEVEL = :fatal -::REDHAT_OPTS = { - :platform => "redhat", - :log_level => ::LOG_LEVEL +ChefSpec::Coverage.start! { add_filter 'openstack-compute' } + +LOG_LEVEL = :fatal +REDHAT_OPTS = { + platform: 'redhat', + log_level: LOG_LEVEL } -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } MOCK_NODE_NETWORK_DATA = { - "ipaddress" => '10.0.0.2', - "fqdn" => 'localhost.localdomain', - "hostname" => 'localhost', - "network" => { - "default_interface" => "eth0", - "interfaces" => { - "eth0" => { - "addresses" => { - "fe80::a00:27ff:feca:ab08" => {"scope" => "Link", "prefixlen" => "64", "family" => "inet6"}, - "10.0.0.2" => {"netmask" => "255.255.255.0", "broadcast" => "10.0.0.255", "family" => "inet"}, - "08:00:27:CA:AB:08" => {"family" => "lladdr"} - }, + 'ipaddress' => '10.0.0.2', + 'fqdn' => 'localhost.localdomain', + 'hostname' => 'localhost', + 'network' => { + 'default_interface' => 'eth0', + 'interfaces' => { + 'eth0' => { + 'addresses' => { + 'fe80::a00:27ff:feca:ab08' => { 'scope' => 'Link', 'prefixlen' => '64', 'family' => 'inet6' }, + '10.0.0.2' => { 'netmask' => '255.255.255.0', 'broadcast' => '10.0.0.255', 'family' => 'inet' }, + '08:00:27:CA:AB:08' => { 'family' => 'lladdr' } + } }, - "lo" => { - "addresses" => { - "::1" => {"scope" => "Node", "prefixlen" => "128", "family" => "inet6"}, - "127.0.0.1" => {"netmask" => "255.0.0.0", "family" => "inet"} - }, - }, - }, + 'lo' => { + 'addresses' => { + '::1' => { 'scope' => 'Node', 'prefixlen' => '128', 'family' => 'inet6' }, + '127.0.0.1' => { 'netmask' => '255.0.0.0', 'family' => 'inet' } + } + } + } } } -def swift_stubs +shared_context 'swift-stubs' do + before do + stub_command('/usr/bin/id swift').and_return(true) + stub_command('[ -x /etc/swift/pull-rings.sh ]').and_return(true) + stub_command('[ -f /etc/rsyncd.conf ]').and_return(true) + stub_command("grep -q 'RSYNC_ENABLE=false' /etc/default/rsync").and_return(true) + stub_command('[ -e /etc/swift/account-server.conf ] && [ -e /etc/swift/account.ring.gz ]').and_return(true) + stub_command('[ -e /etc/swift/container-server.conf ] && [ -e /etc/swift/container.ring.gz ]').and_return(true) + stub_command('[ -e /etc/init/swift-container-sync.conf ]').and_return(false) + stub_command('[ -e /etc/init.d/swift-container-sync ]').and_return(false) + stub_command('[ -e /etc/swift/object-server.conf ] && [ -e /etc/swift/object.ring.gz ]').and_return(true) + stub_command('[ -e /etc/swift/proxy-server.conf ] && [ -e /etc/swift/object.ring.gz ]').and_return(true) + # create mock cluster - n = Chef::Node.new() + n = Chef::Node.new n.name('manager') n.default_attrs = { - "swift" => { - "service_pass" => "foobar" + 'swift' => { + 'service_pass' => 'foobar' } - } - Chef::Recipe.any_instance.stub(:search).with(:node, 'chef_environment:_default AND roles:swift-setup').and_return([n]) + } + Chef::Recipe.any_instance.stub(:search).with(:node, 'chef_environment:_default AND roles:swift-setup').and_return([n]) + Chef::Application.stub(:fatal!) + end +end + +shared_examples 'keystone-authmode' do + describe 'authorization mode' do + # Default is to use authorization mode of swauth which does not require keystone client. + it 'does not upgrade keystoneclient package' do + expect(chef_run).not_to upgrade_package('python-keystoneclient') + end + describe 'keystone authorization mode' do + before { node.set['openstack']['object-storage']['authmode'] = 'keystone' } + it 'does not upgrade keystoneclient package' do + expect(chef_run).to upgrade_package('python-keystoneclient') + end + end + end end diff --git a/chef/cookbooks/openstack-object-storage/spec/storage-common_spec.rb b/chef/cookbooks/openstack-object-storage/spec/storage-common_spec.rb index 50a3ef8..c5f6111 100644 --- a/chef/cookbooks/openstack-object-storage/spec/storage-common_spec.rb +++ b/chef/cookbooks/openstack-object-storage/spec/storage-common_spec.rb @@ -1,58 +1,42 @@ -require 'spec_helper' +# encoding: UTF-8 +require_relative 'spec_helper' describe 'openstack-object-storage::storage-common' do - - #------------------- - # UBUNTU - #------------------- - - describe "ubuntu" do - - before do - swift_stubs - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @node = @chef_run.node - @node.set['lsb']['code'] = 'precise' - @node.set['swift']['authmode'] = 'swauth' - @chef_run.converge "openstack-object-storage::storage-common" + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) end - describe "/var/cache/swift" do + include_context 'swift-stubs' - before do - @file = @chef_run.directory "/var/cache/swift" + describe '/var/cache/swift' do + let(:dir) { chef_run.directory('/var/cache/swift') } + + it 'creates /var/cache/swift' do + expect(chef_run).to create_directory(dir.name).with( + user: 'swift', + group: 'swift', + mode: 0700 + ) end - - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" - end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "700" - end - end - describe "/etc/swift/drive-audit.conf" do + describe '/etc/swift/drive-audit.conf' do + let(:file) { chef_run.template('/etc/swift/drive-audit.conf') } - before do - @file = @chef_run.template "/etc/swift/drive-audit.conf" + it 'creates drive-audit.conf' do + expect(chef_run).to create_template(file.name).with( + user: 'swift', + group: 'swift', + mode: 0600 + ) end - it "has proper owner" do - expect(@file).to be_owned_by "swift", "swift" + it 'template contents' do + pending 'TODO: implement' end - - it "has proper modes" do - expect(sprintf("%o", @file.mode)).to eq "600" - end - - it "template contents" do - pending "TODO: implement" - end - end - end - end diff --git a/chef/cookbooks/openstack-object-storage/templates/default/account-server.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/account-server.conf.erb index 6ba2498..0f4319d 100644 --- a/chef/cookbooks/openstack-object-storage/templates/default/account-server.conf.erb +++ b/chef/cookbooks/openstack-object-storage/templates/default/account-server.conf.erb @@ -15,11 +15,11 @@ bind_ip = <%= @bind_ip %> bind_port = <%= @bind_port %> workers = 10 -<% if node[:swift][:enable_statistics] -%> +<% if node['openstack']['object-storage']['statistics']['enabled'] -%> log_statsd_host = localhost log_statsd_port = 8125 -log_statsd_default_sample_rate = 1 -log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %> +log_statsd_default_sample_rate = <%= node['openstack']['object-storage']['statistics']['sample_rate'] %> +log_statsd_metric_prefix = <%= node['openstack']['object-storage']['statistics']['statsd_prefix'] %>.<%= node['hostname'] %> <% end %> [pipeline:main] diff --git a/chef/cookbooks/openstack-object-storage/templates/default/container-server.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/container-server.conf.erb index 144214f..227e521 100644 --- a/chef/cookbooks/openstack-object-storage/templates/default/container-server.conf.erb +++ b/chef/cookbooks/openstack-object-storage/templates/default/container-server.conf.erb @@ -18,12 +18,16 @@ bind_ip = <%= @bind_ip %> bind_port = <%= @bind_port %> workers = 10 -<% if node[:swift][:enable_statistics] -%> +<% if node['openstack']['object-storage']['statistics']['enabled'] -%> log_statsd_host = localhost log_statsd_port = 8125 -log_statsd_default_sample_rate = 1 -log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %> -<% end %> +log_statsd_default_sample_rate = <%= node['openstack']['object-storage']['statistics']['sample_rate'] %> +log_statsd_metric_prefix = <%= node['openstack']['object-storage']['statistics']['statsd_prefix'] %>.<%= node['hostname'] %> +<% end -%> + +<% if node['openstack']['object-storage']["container-server"]["allowed_sync_hosts"] -%> +allowed_sync_hosts = <%= node['openstack']['object-storage']["container-server"]["allowed_sync_hosts"].join(",") %> +<% end -%> [pipeline:main] pipeline = container-server @@ -77,12 +81,14 @@ use = egg:swift#container [container-sync] # You can override the default log routing for this app here (don't use set!): -# log_name = container-sync -# log_facility = LOG_LOCAL0 -# log_level = INFO +log_name = <%= node['openstack']['object-storage']["container-server"]["container-sync"]["log_name"] %> +log_facility = <%= node['openstack']['object-storage']["container-server"]["container-sync"]["log_facility"] %> +log_level = <%= node['openstack']['object-storage']["container-server"]["container-sync"]["log_level"] %> # If you need to use an HTTP Proxy, set it here; defaults to no proxy. -# sync_proxy = http://127.0.0.1:8888 +<% if node['openstack']['object-storage']["container-server"]["container-sync"]["sync_proxy"] -%> +sync_proxy = <%= node['openstack']['object-storage']["container-server"]["container-sync"]["sync_proxy"] %> +<% end -%> # Will sync, at most, each container once per interval -# interval = 300 +interval = <%= node['openstack']['object-storage']["container-server"]["container-sync"]["interval"] %> # Maximum amount of time to spend syncing each container per pass -# container_time = 60 +container_time = <%= node['openstack']['object-storage']["container-server"]["container-sync"]["container_time"] %> diff --git a/chef/cookbooks/openstack-object-storage/templates/default/object-server.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/object-server.conf.erb index ea55dd8..e9499e6 100644 --- a/chef/cookbooks/openstack-object-storage/templates/default/object-server.conf.erb +++ b/chef/cookbooks/openstack-object-storage/templates/default/object-server.conf.erb @@ -16,11 +16,11 @@ bind_ip = <%= @bind_ip %> bind_port = <%= @bind_port %> workers = 10 -<% if node[:swift][:enable_statistics] -%> +<% if node['openstack']['object-storage']['statistics']['enabled'] -%> log_statsd_host = localhost log_statsd_port = 8125 -log_statsd_default_sample_rate = 1 -log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %> +log_statsd_default_sample_rate = <%= node['openstack']['object-storage']['statistics']['sample_rate'] %> +log_statsd_metric_prefix = <%= node['openstack']['object-storage']['statistics']['statsd_prefix'] %>.<%= node['hostname'] %> <% end %> [pipeline:main] diff --git a/chef/cookbooks/openstack-object-storage/templates/default/proxy-server.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/proxy-server.conf.erb index 9576af4..1b42fdf 100644 --- a/chef/cookbooks/openstack-object-storage/templates/default/proxy-server.conf.erb +++ b/chef/cookbooks/openstack-object-storage/templates/default/proxy-server.conf.erb @@ -1,22 +1,46 @@ <% +pipeline = [] + +# the pipeline ordering is important and certain inclusions +# must either preceed or follow the selected auth module + +if (node['openstack']['object-storage']['authmode'] == "swauth" and node['openstack']['object-storage']['tempurl']['enabled'] == true) + pipeline << "tempurl" +end + +if node['openstack']['object-storage']['domain_remap']['enabled'] + pipeline << "domain_remap" +end + +if node['openstack']['object-storage']['formpost']['enabled'] + pipeline << "formpost" +end + case @authmode when "keystone" - pipeline="authtoken keystoneauth" + pipeline << "authtoken" + pipeline << "keystoneauth" when "swauth" - pipeline="swauth" + pipeline << "swauth" end -account_management=false -if node[:roles].include?("swift-management-server") and node[:swift][:authmode] == "swauth" then - account_management="true" +if node['openstack']['object-storage']['staticweb']['enabled'] + pipeline << "staticweb" end + +if pipeline.include?("swauth") + account_management = true +else + account_management = false +end + -%> # This file is managed by chef. Do not edit it. # # Cluster info: -# Auth mode: <%= node[:swift][:authmode] %> -# Management server: <%= node[:roles].include?("swift-management-server") %> +# Auth mode: <%= node['openstack']['object-storage']['authmode'] %> +# Management server: <%= node['roles'].include?(node['openstack']['object-storage']['management_server_chef_role']) %> # Account management enabled: <%= account_management %> # Auth pipeline: <%= pipeline %> @@ -35,19 +59,19 @@ end # log_facility = LOG_LOCAL0 # log_level = INFO ###### -workers = <%= [ node[:cpu][:total] - 1, 1 ].max %> bind_ip = <%= @bind_host %> bind_port = <%= @bind_port %> -<% if node[:swift][:enable_statistics] -%> +workers = <%= [ node['cpu']['total'] - 1, 1 ].max %> +<% if node['openstack']['object-storage']['statistics']['enabled'] -%> log_statsd_host = localhost log_statsd_port = 8125 -log_statsd_default_sample_rate = 1 -log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %> +log_statsd_default_sample_rate = <%= node['openstack']['object-storage']['statistics']['sample_rate'] %> +log_statsd_metric_prefix = <%= node['openstack']['object-storage']['statistics']['statsd_prefix'] %>.<%= node['hostname'] %> <% end %> [pipeline:main] -pipeline = catch_errors healthcheck cache ratelimit <%= pipeline %> proxy-logging proxy-server +pipeline = catch_errors healthcheck cache ratelimit <%= pipeline.join(" ") %> proxy-logging proxy-server [app:proxy-server] use = egg:swift#proxy @@ -82,29 +106,25 @@ use = egg:swift#proxy # If set to 'true' authorized accounts that do not yet exist within the Swift # cluster will be automatically created. # account_autocreate = false -###### -# -# N.B. ideally allow_account_management would only be set on the -# management server, but swauth will delete using the cluster url -# and not the local url -# allow_account_managemnet = <%= account_management %> -allow_account_management = true +allow_account_management = <%= account_management %> -<% if @authmode == "keystone" -%> +<% if pipeline.include?("keystone") -%> account_autocreate = true <% end %> -<% if @authmode == "swauth" -%> +<% if pipeline.include?("swauth") -%> [filter:swauth] use = egg:swauth#swauth # set log_name = swauth # super_admin_key = ###### -<% if account_management -%> super_admin_key = <%= @authkey %> -default_swift_cluster = local#<%= node[:swift][:swift_url] %>#<%= node[:swift][:swauth_url] %> -<% else %> -default_swift_cluster = local#<%= node[:swift][:swift_url] %> +default_swift_cluster = local#<%= node['openstack']['object-storage']['swift_url'] %>#<%= node['openstack']['object-storage']['swauth_url'] %> +<% if pipeline.include?("tempurl") -%> +allow_overrides = true +<% end %> +<% if node['openstack']['object-storage']['container-server']['allowed_sync_hosts'] -%> +allowed_sync_hosts = <%= node['openstack']['object-storage']['container-server']['allowed_sync_hosts'].join(",") %> <% end %> <% end %> @@ -166,13 +186,13 @@ use = egg:swift#ratelimit [filter:domain_remap] use = egg:swift#domain_remap # You can override the default log routing for this filter here: -# set log_name = domain_remap -# set log_facility = LOG_LOCAL0 -# set log_level = INFO -# set log_headers = False -# storage_domain = example.com -# path_root = v1 -# reseller_prefixes = AUTH +set log_name = <%= node['openstack']['object-storage']['domain_remap']['log_name'] %> +set log_facility = <%= node['openstack']['object-storage']['domain_remap']['log_facility'] %> +set log_level = <%= node['openstack']['object-storage']['domain_remap']['log_level'] %> +set log_headers = <%= node['openstack']['object-storage']['domain_remap']['log_headers'] %> +storage_domain = <%= node['openstack']['object-storage']['domain_remap']['storage_domain'] %> +path_root = <%= node['openstack']['object-storage']['domain_remap']['path_root'] %> +reseller_prefixes = <%= node['openstack']['object-storage']['domain_remap']['reseller_prefixes'] %> [filter:catch_errors] use = egg:swift#catch_errors @@ -197,15 +217,15 @@ use = egg:swift#cname_lookup [filter:staticweb] use = egg:swift#staticweb # Seconds to cache container x-container-meta-web-* header values. -# cache_timeout = 300 +cache_timeout = <%= node['openstack']['object-storage']['staticweb']['cache_timeout'] %> # You can override the default log routing for this filter here: -# set log_name = staticweb -# set log_facility = LOG_LOCAL0 -# set log_level = INFO -# set access_log_name = staticweb -# set access_log_facility = LOG_LOCAL0 -# set access_log_level = INFO -# set log_headers = False +set log_name = <%= node['openstack']['object-storage']['staticweb']['log_name'] %> +set log_facility = <%= node['openstack']['object-storage']['staticweb']['log_facility'] %> +set log_level = <%= node['openstack']['object-storage']['staticweb']['log_level'] %> +set access_log_name = <%= node['openstack']['object-storage']['staticweb']['access_log_name'] %> +set access_log_facility = <%= node['openstack']['object-storage']['staticweb']['access_log_facility'] %> +set access_log_level = <%= node['openstack']['object-storage']['staticweb']['access_log_level'] %> +set log_headers = <%= node['openstack']['object-storage']['staticweb']['log_headers'] %> # Note: Put tempurl just before your auth filter(s) in the pipeline [filter:tempurl] @@ -215,23 +235,23 @@ use = egg:swift#tempurl # list of header names and names can optionally end with '*' to indicate a # prefix match. incoming_allow_headers is a list of exceptions to these # removals. -# incoming_remove_headers = x-timestamp +incoming_remove_headers = <%= node['openstack']['object-storage']['tempurl']['incoming_remove_headers'] %> # # The headers allowed as exceptions to incoming_remove_headers. Simply a # whitespace delimited list of header names and names can optionally end with # '*' to indicate a prefix match. -# incoming_allow_headers = +incoming_allow_headers = <%= node['openstack']['object-storage']['tempurl']['incoming_allow_headers'] %> # # The headers to remove from outgoing responses. Simply a whitespace delimited # list of header names and names can optionally end with '*' to indicate a # prefix match. outgoing_allow_headers is a list of exceptions to these # removals. -# outgoing_remove_headers = x-object-meta-* +outgoing_remove_headers = <%= node['openstack']['object-storage']['tempurl']['outgoing_remove_headers'] %> # # The headers allowed as exceptions to outgoing_remove_headers. Simply a # whitespace delimited list of header names and names can optionally end with # '*' to indicate a prefix match. -# outgoing_allow_headers = x-object-meta-public-* +outgoing_allow_headers = <%= node['openstack']['object-storage']['tempurl']['outgoing_allow_headers'] %> # Note: Put formpost just before your auth filter(s) in the pipeline [filter:formpost] @@ -253,7 +273,7 @@ use = egg:swift#proxy_logging # You can use log_statsd_* from [DEFAULT] or override them here: # access_log_statsd_host = localhost # access_log_statsd_port = 8125 -# access_log_statsd_default_sample_rate = 1 +# access_log_statsd_default_sample_rate = <%= node['openstack']['object-storage']['statistics']['sample_rate'] %> # access_log_statsd_metric_prefix = # access_log_headers = False # What HTTP methods are allowed for StatsD logging (comma-sep); request methods diff --git a/chef/cookbooks/openstack-object-storage/templates/default/swift-statsd-publish.py.erb b/chef/cookbooks/openstack-object-storage/templates/default/swift-statsd-publish.py.erb new file mode 100644 index 0000000..1b88d59 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/swift-statsd-publish.py.erb @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# coding=utf-8 +# +# Author: Alan Meadows +# +# 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. +# +""" +THIS FILE WAS INSTALLED BY CHEF. ANY CHANGES WILL BE OVERWRITTEN. + +Openstack swift collector for recon and dispersion reports. Will send +back dispersion reporting metrics as well as swift recon statistics +to a statsd server for graphite consumption +""" + +from subprocess import Popen, PIPE, check_call +from socket import socket, AF_INET, SOCK_DGRAM +import re +import os + +try: + import json + json # workaround for pyflakes issue #13 +except ImportError: + import simplejson as json + + +class OpenStackSwiftStatisticsCollector(object): + + def __init__(self): + '''Setup some initial values defined by chef''' + + self.statsd_host = '<%= node['openstack']['object-storage']['statistics']['statsd_host'] %>' + self.statsd_port = <%= node['openstack']['object-storage']['statistics']['statsd_port'] %> + self.statsd_prefix = '<%= node['openstack']['object-storage']['statistics']['statsd_prefix'] %>' +<% if node['openstack']['object-storage']['statistics']['enable_dispersion_report'] -%> + self.enable_dispersion_report = True +<% else %> + self.enable_dispersion_report = False +<% end %> +<% if node['openstack']['object-storage']['statistics']['enable_recon_report'] -%> + self.enable_recon_report = True +<% else %> + self.enable_recon_report = False +<% end %> +<% if node['openstack']['object-storage']['statistics']['enable_disk_report'] -%> + self.enable_disk_report = True +<% else %> + self.enable_disk_report = False +<% end %> + self.recon_account_cache = '<%= node['openstack']['object-storage']['statistics']['recon_account_cache'] %>' + self.recon_container_cache = '<%= node['openstack']['object-storage']['statistics']['recon_container_cache'] %>' + self.recon_object_cache = '<%= node['openstack']['object-storage']['statistics']['recon_object_cache'] %>' + + def _dispersion_report(self): + """ + Swift Dispersion Report Collection + """ + p = Popen(['/usr/bin/swift-dispersion-report', '-j'], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + self.publish('%s.dispersion.errors' % self.statsd_prefix, len(stderr.split('\n')) - 1) + data = json.loads(stdout) + for t in ('object', 'container'): + for (k, v) in data[t].items(): + self.publish('%s.dispersion.%s.%s' % (self.statsd_prefix, t, k), v) + + def _recon_report(self): + """ + Swift Recon Collection + """ + recon_cache = {'account': self.recon_account_cache, + 'container': self.recon_container_cache, + 'object': self.recon_object_cache} + for recon_type in recon_cache: + if not os.access(recon_cache[recon_type], os.R_OK): + continue + try: + f = open(recon_cache[recon_type]) + try: + rmetrics = json.loads(f.readlines()[0].strip()) + metrics = self._process_cache(rmetrics) + for k, v in metrics: + metric_name = '%s.%s.%s' % (self.statsd_prefix, recon_type, ".".join(k)) + if isinstance(v, (int, float)): + self.publish(metric_name, v) + except (ValueError, IndexError): + continue + finally: + f.close() + + def _disk_report(self): + """ + Swift Disk Capacity Report + """ + p = Popen(['/usr/bin/swift-recon', '-d'], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + + used, total = 0, 0 + match = re.search(r'.* space used: ([0-9]*\.?[0-9]+) of ([0-9]*\.?[0-9]+)', stdout, re.M|re.I) + if match: + used, total = [int(i) for i in match.groups()] + + highest, avg = 0, 0 + match = re.search(r'.* lowest:.+highest: ([0-9]*\.?[0-9]+)%, avg: ([0-9]*\.?[0-9]+)%', stdout, re.M|re.I) + if match: + highest, avg = match.groups() + + self.publish('%s.capacity.bytes_used' % self.statsd_prefix, used) + self.publish('%s.capacity.bytes_free' % self.statsd_prefix, total-used) + self.publish('%s.capacity.bytes_utilization' % self.statsd_prefix, int((used/total)*100)) + self.publish('%s.capacity.single_disk_utilization_highest' % self.statsd_prefix, highest) + self.publish('%s.capacity.single_disk_utilization_average' % self.statsd_prefix, avg) + + def collect(self): + + if (self.enable_dispersion_report): + self._dispersion_report() + + if (self.enable_recon_report): + self._recon_report() + + if (self.enable_disk_report): + self._disk_report() + + def publish(self, metric_name, value): + """Publish a metric to statsd server""" + # TODO: IPv6 support + print '%s:%s|g' % (metric_name.encode('utf-8'), value), (self.statsd_host, self.statsd_port) + udp_sock = socket(AF_INET, SOCK_DGRAM) + udp_sock.sendto('%s:%s|g' % (metric_name.encode('utf-8'), value), (self.statsd_host, self.statsd_port)) + + def _process_cache(self, d, path=()): + """Recusively walk a nested recon cache dict to obtain path/values""" + metrics = [] + for k, v in d.iteritems(): + if not isinstance(v, dict): + metrics.append((path + (k,), v)) + else: + self._process_cache(v, path + (k,)) + return metrics + +if __name__ == '__main__': + collector = OpenStackSwiftStatisticsCollector() + collector.collect() diff --git a/chef/cookbooks/openstack-ops-database/.rubocop.yml b/chef/cookbooks/openstack-ops-database/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-ops-database/Berksfile b/chef/cookbooks/openstack-ops-database/Berksfile new file mode 100644 index 0000000..84e5b6d --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/Berksfile @@ -0,0 +1,4 @@ +metadata + +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" diff --git a/chef/cookbooks/openstack-ops-database/CHANGELOG.md b/chef/cookbooks/openstack-ops-database/CHANGELOG.md index d485638..cff8166 100644 --- a/chef/cookbooks/openstack-ops-database/CHANGELOG.md +++ b/chef/cookbooks/openstack-ops-database/CHANGELOG.md @@ -1,3 +1,20 @@ -## 2013.1.0 +openstack-ops-database Cookbook CHANGELG +=================================== +This file is used to list changes made in each version of the openstack-ops-database cookbook. -* initial release +## 9.0.1 +* Fix metadata for database + +## 9.0.0 +* Upgrade to Icehouse + +## 8.1.0 +* Rename openstack-metering to openstack-telemetry + +## 8.0.0 +* Havana support + +## 7.0.0 + +* Initial release intended for Grizzly-based OpenStack releases, + for use with Stackforge upstream repositories. diff --git a/chef/cookbooks/openstack-ops-database/Gemfile b/chef/cookbooks/openstack-ops-database/Gemfile new file mode 100644 index 0000000..ccfa456 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/Gemfile @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' diff --git a/chef/cookbooks/openstack-ops-database/Gemfile.lock b/chef/cookbooks/openstack-ops-database/Gemfile.lock new file mode 100644 index 0000000..2954927 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/Gemfile.lock @@ -0,0 +1,237 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.17) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.6) + akami (1.2.1) + gyoku (>= 0.4.0) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + chozo (>= 0.6.1) + faraday (~> 0.8.0) + faraday (~> 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.5.0) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) + buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.1) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + coderay (1.1.0) + diff-lcs (1.2.5) + erubis (2.7.0) + faraday (0.8.9) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) + net-ssh + ohai + ffi (1.9.3) + foodcritic (3.0.3) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rake + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.1.1) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) + httpi (0.9.7) + rack + i18n (0.6.9) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + logging (1.8.2) + little-plugger (>= 1.1.3) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) + minitar (0.5.4) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.4.0) + mixlib-config (2.1.0) + mixlib-log (1.6.0) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) + multipart-post (1.2.0) + net-http-persistent (2.9.4) + net-ssh (2.8.0) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) + nori (1.1.5) + ohai (6.20.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu (~> 2.5.2) + yajl-ruby + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) + rack (1.5.2) + rainbow (2.0.0) + rake (10.2.2) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.5) + ridley (1.5.3) + addressable + buff-config (~> 0.2) + buff-extensions (~> 0.3) + buff-ignore (~> 1.1) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + nio4r (>= 0.5.0) + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) + berkshelf (~> 2.0) + buff-platform (~> 0.1) + systemu (2.5.2) + thor (0.18.1) + timers (2.0.0) + hitimes + treetop (1.4.15) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.3.2) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.3) + gssapi (~> 1.0.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + foodcritic (~> 3.0.3) + json (<= 1.7.7) + rubocop (~> 0.18.1) + strainer diff --git a/chef/cookbooks/openstack-ops-database/README.md b/chef/cookbooks/openstack-ops-database/README.md index 920fce3..7121370 100644 --- a/chef/cookbooks/openstack-ops-database/README.md +++ b/chef/cookbooks/openstack-ops-database/README.md @@ -1,6 +1,6 @@ # Description # -This cookbook provides shared database configuration for the OpenStack **Grizzly** reference deployment provided by Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. It currently supports MySQL and will soon support PostgreSQL. +This cookbook provides a reference example of database configuration for the OpenStack **Icehouse** reference deployment provided by Chef for OpenStack. It currently supports MySQL and PostgreSQL. # Requirements # @@ -9,14 +9,25 @@ Chef 11 with Ruby 1.9.x required. # Platforms # * Ubuntu-12.04 +* SLES 11 SP3 +* RHEL/CentOS 6.5 # Cookbooks # The following cookbooks are dependencies: * database +* openstack-common * mysql -* openssl +* postgresql + +# Usage # + +The usage of this cookbook is optional, you may choose to set up your own databases without using this cookbook. If you choose to do so, you will need to do the following: + +* create the schema specified by the `openstack-db` recipe. +* create and upload encrypted data bags into your chef environment, as + specified by `#get_password` in the `openstack-db` recipe. # Resources/Providers # @@ -44,12 +55,46 @@ None - configures the mysql server for OpenStack +## postgresql-client ## + +- calls postgresql::ruby and postgresql::client and installs 'postgresql_python_packages' + +## postgresql-server ## + +- configures the PostgreSQL server for OpenStack + +## openstack-db ## + +- creates necessary tables, users, and grants for OpenStack + # Attributes # -* `openstack['role']['database]` - which role should other nodes search on to find the database service, defaults to 'os-ops-database' +* `openstack["db"]["platform"]["mysql_python_packages"]` - platform-specific mysql python packages to install -* `openstack['database']['service']` - which service to use, defaults to 'mysql' -* `openstack['database']['platform']['mysql_python_packages']` - platform-specific mysql python packages to install +The following attributes are defined in attributes/database.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack["endpoints"]["db"]["host"]` - The IP address to bind the database service to +* `openstack["endpoints"]["db"]["scheme"]` - Unused at this time +* `openstack["endpoints"]["db"]["port"]` - The port to bind the database service to +* `openstack["endpoints"]["db"]["path"]` - Unused at this time +* `openstack["endpoints"]["db"]["bind_interface"]` - The interface name to bind the database service to + +If the value of the "bind_interface" attribute is non-nil, then the database service will be bound to the first IP address on that interface. If the value of the "bind_interface" attribute is nil, then the database service will be bound to the IP address specified in the host attribute. + +Testing +===== + +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. + +Berkshelf +===== + +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -65,9 +110,14 @@ License and Author | **Author** | Evan Callicoat () | | **Author** | Matt Thompson () | | **Author** | Matt Ray () | +| **Author** | Sean Gallagher () | +| **Author** | John Dewey () | +| **Author** | Ionut Artarisi () | | | | | **Copyright** | Copyright (c) 2012-2013, Rackspace US, Inc. | | **Copyright** | Copyright (c) 2012-2013, Opscode, Inc. | +| **Copyright** | Copyright (c) 2013, AT&T Services, Inc. | +| **Copyright** | Copyright (c) 2013, SUSE Linux GmbH | Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/chef/cookbooks/openstack-metering/Strainerfile b/chef/cookbooks/openstack-ops-database/Strainerfile similarity index 80% rename from chef/cookbooks/openstack-metering/Strainerfile rename to chef/cookbooks/openstack-ops-database/Strainerfile index 7e292b4..44e3e14 100644 --- a/chef/cookbooks/openstack-metering/Strainerfile +++ b/chef/cookbooks/openstack-ops-database/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK knife test: bundle exec knife cookbook test $COOKBOOK foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-ops-database/TESTING.md b/chef/cookbooks/openstack-ops-database/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-ops-database/attributes/default.rb b/chef/cookbooks/openstack-ops-database/attributes/default.rb index 80efa58..381954f 100644 --- a/chef/cookbooks/openstack-ops-database/attributes/default.rb +++ b/chef/cookbooks/openstack-ops-database/attributes/default.rb @@ -1,11 +1,32 @@ -default['openstack']['role']['database'] = 'os-ops-database' +# encoding: UTF-8# +# +# Cookbook Name:: openstack-ops-database +# Recipe:: default +# +# Copyright 2013, AT&T Services, Inc. +# +# 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. +# -default['openstack']['database']['service'] = 'mysql' - -# platform defaults -case platform -when 'fedora', 'redhat', 'centos' # :pragma-foodcritic: ~FC024 - won't fix this - default['openstack']['database']['platform']['mysql_python_packages'] = [ 'MySQL-python' ] -when 'ubuntu' - default['openstack']['database']['platform']['mysql_python_packages'] = [ 'python-mysqldb' ] +# Platform defaults +case platform_family +when 'fedora', 'rhel' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['db']['platform']['mysql_python_packages'] = ['MySQL-python'] + default['openstack']['db']['platform']['postgresql_python_packages'] = ['python-psycopg2'] +when 'suse' + default['openstack']['db']['platform']['mysql_python_packages'] = ['python-mysql'] + default['openstack']['db']['platform']['postgresql_python_packages'] = ['python-psycopg2'] +when 'debian' + default['openstack']['db']['platform']['mysql_python_packages'] = ['python-mysqldb'] + default['openstack']['db']['platform']['postgresql_python_packages'] = ['python-psycopg2'] end diff --git a/chef/cookbooks/openstack-ops-database/metadata.rb b/chef/cookbooks/openstack-ops-database/metadata.rb index 2e54594..3d98d91 100644 --- a/chef/cookbooks/openstack-ops-database/metadata.rb +++ b/chef/cookbooks/openstack-ops-database/metadata.rb @@ -1,16 +1,23 @@ -name "openstack-ops-database" -maintainer "Opscode, Inc." -maintainer_email "matt@opscode.com" -license "Apache 2.0" -description "Provides the shared database configuration for Chef for OpenStack." -version "0.1.0" +name 'openstack-ops-database' +maintainer 'Opscode, Inc.' +maintainer_email 'matt@opscode.com' +license 'Apache 2.0' +description 'Provides the shared database configuration for Chef for OpenStack.' +version '9.0.1' -recipe "default", "Selects the database service." -recipe "mysql", "Configures MySQL." +recipe 'client', 'Installs client packages for the database used by the deployment.' +recipe 'server', 'Installs and configures server packages for the database used by the deployment.' +recipe 'mysql-client', 'Installs MySQL client packages.' +recipe 'mysql-server', 'Installs and configures MySQL server packages.' +recipe 'postgresql-client', 'Installs PostgreSQL client packages.' +recipe 'postgresql-server', 'Installs and configures PostgreSQL server packages.' +recipe 'openstack-db', 'Creates necessary tables, users, and grants for OpenStack.' -%w{ ubuntu }.each do |os| +%w{ fedora ubuntu redhat centos suse }.each do |os| supports os end -depends "database", ">= 1.3.12" -depends "mysql", ">= 3.0.0" +depends 'mysql', '~> 4.1' +depends 'postgresql', '~> 3.3' +depends 'database', '~> 2.0' +depends 'openstack-common', '~> 9.0' diff --git a/chef/cookbooks/openstack-ops-database/recipes/client.rb b/chef/cookbooks/openstack-ops-database/recipes/client.rb index 63e934f..985ac90 100644 --- a/chef/cookbooks/openstack-ops-database/recipes/client.rb +++ b/chef/cookbooks/openstack-ops-database/recipes/client.rb @@ -1,8 +1,10 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-database # Recipe:: client # # Copyright 2013, Opscode, Inc. +# Copyright 2013, AT&T Services, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +19,4 @@ # limitations under the License. # -include_recipe "openstack-ops-database::#{node['openstack']['database']['service']}-client" +include_recipe "openstack-ops-database::#{node['openstack']['db']['service_type']}-client" diff --git a/chef/cookbooks/openstack-ops-database/recipes/mysql-client.rb b/chef/cookbooks/openstack-ops-database/recipes/mysql-client.rb index b7def7f..2e5bf74 100644 --- a/chef/cookbooks/openstack-ops-database/recipes/mysql-client.rb +++ b/chef/cookbooks/openstack-ops-database/recipes/mysql-client.rb @@ -1,8 +1,10 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-database # Recipe:: mysql-client # # Copyright 2013, Opscode, Inc. +# Copyright 2013, AT&T Services, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,7 +19,9 @@ # limitations under the License. # -include_recipe "mysql::ruby" -include_recipe "mysql::client" +include_recipe 'mysql::ruby' +include_recipe 'mysql::client' -node.default['openstack']['packages']['ops-database::mysql-client'] = node['openstack']['database']['platform']['mysql_python_packages'] +node['openstack']['db']['platform']['mysql_python_packages'].each do |pkg| + package pkg +end diff --git a/chef/cookbooks/openstack-ops-database/recipes/mysql-server.rb b/chef/cookbooks/openstack-ops-database/recipes/mysql-server.rb index 3888977..840f8d0 100644 --- a/chef/cookbooks/openstack-ops-database/recipes/mysql-server.rb +++ b/chef/cookbooks/openstack-ops-database/recipes/mysql-server.rb @@ -1,9 +1,11 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-database # Recipe:: mysql-server # # Copyright 2013, Opscode, Inc. # Copyright 2012-2013, Rackspace US, Inc. +# Copyright 2013, AT&T Services, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,52 +20,72 @@ # limitations under the License. # -# override default attributes in the upstream mysql cookbook -node.set['mysql']['bind_address'] = node['openstack']['db']['bind_address'] -node.set['mysql']['server_debian_password'] = node['openstack']['db']['super']['password'] -node.set['mysql']['server_root_password'] = node['openstack']['db']['super']['password'] -node.set['mysql']['server_repl_password'] = node['openstack']['db']['super']['password'] - -node.set['mysql']['tunable']['innodb_thread_concurrency'] = "0" -node.set['mysql']['tunable']['innodb_commit_concurrency'] = "0" -node.set['mysql']['tunable']['innodb_read_io_threads'] = "4" -node.set['mysql']['tunable']['innodb_flush_log_at_trx_commit'] = "2" - -node.save - -include_recipe "openstack-ops-database::mysql-client" -include_recipe "mysql::server" - -mysql_connection_info = {:host => "localhost", - :username => 'root', - :password => node['mysql']['server_root_password']} - -# removing insecure default mysql users -mysql_database_user 'drop empty localhost user' do - username '' - host 'localhost' - connection mysql_connection_info - action :drop +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack end -# removing insecure default mysql users -mysql_database_user 'drop empty hostname user' do - username '' - host "#{node.hostname}" - connection mysql_connection_info - action :drop +db_endpoint = endpoint 'db' + +node.override['mysql']['bind_address'] = db_endpoint.host +node.override['mysql']['tunable']['innodb_thread_concurrency'] = '0' +node.override['mysql']['tunable']['innodb_commit_concurrency'] = '0' +node.override['mysql']['tunable']['innodb_read_io_threads'] = '4' +node.override['mysql']['tunable']['innodb_flush_log_at_trx_commit'] = '2' +node.override['mysql']['tunable']['skip-name-resolve'] = true + +include_recipe 'openstack-ops-database::mysql-client' +include_recipe 'mysql::server' + +# NOTE:(mancdaz) This is a temporary workaround for this upstream bug in the +# mysql cookbook. It can be removed once the upstream issue is resolved: +# +# https://tickets.opscode.com/browse/COOK-4161 +case node['platform_family'] +when 'debian' + mycnf_template = '/etc/mysql/my.cnf' +when 'rhel' + mycnf_template = 'final-my.cnf' +end + +r = resources("template[#{mycnf_template}]") +r.notifies_immediately(:restart, 'service[mysql]') + +if node['openstack']['db']['root_user_use_databag'] + super_password = get_password 'user', node['openstack']['db']['root_user_key'] +else + super_password = node['mysql']['server_root_password'] +end + +mysql_connection_info = { + host: 'localhost', + username: 'root', + password: super_password +} + +mysql_database 'FLUSH PRIVILEGES' do + connection mysql_connection_info + sql 'FLUSH PRIVILEGES' + action :query +end + +# Unfortunately, this is needed to get around a MySQL bug +# that repeatedly shows its face when running this in Vagabond +# containers: +# +# http://bugs.mysql.com/bug.php?id=69644 +mysql_database 'drop empty localhost user' do + sql "DELETE FROM mysql.user WHERE User = '' OR Password = ''" + connection mysql_connection_info + action :query end -# drop the test database mysql_database 'test' do connection mysql_connection_info action :drop end -# flush the privileges -mysql_database "FLUSH privileges" do +mysql_database 'FLUSH PRIVILEGES' do connection mysql_connection_info - sql "FLUSH privileges" - action :nothing - subscribes :query, "mysql_database[test]" + sql 'FLUSH PRIVILEGES' + action :query end diff --git a/chef/cookbooks/openstack-ops-database/recipes/openstack-db.rb b/chef/cookbooks/openstack-ops-database/recipes/openstack-db.rb index 8b8a072..b8639c4 100644 --- a/chef/cookbooks/openstack-ops-database/recipes/openstack-db.rb +++ b/chef/cookbooks/openstack-ops-database/recipes/openstack-db.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-database # Recipe:: openstack-db @@ -17,49 +18,54 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end db_create_with_user( - "compute", - node["openstack"]["db"]["compute"]["username"], - db_password(node['openstack']['db']['compute']['password']) + 'compute', + node['openstack']['db']['compute']['username'], + get_password('db', 'nova') ) db_create_with_user( - "dashboard", - node["openstack"]["db"]["dashboard"]["username"], - db_password(node['openstack']['db']['dashboard']['password']) + 'dashboard', + node['openstack']['db']['dashboard']['username'], + get_password('db', 'horizon') ) db_create_with_user( - "identity", - node["openstack"]["db"]["identity"]["username"], - db_password(node['openstack']['db']['identity']['password']) + 'identity', + node['openstack']['db']['identity']['username'], + get_password('db', 'keystone') ) db_create_with_user( - "image", - node["openstack"]["db"]["image"]["username"], - db_password(node['openstack']['db']['image']['password']) + 'image', + node['openstack']['db']['image']['username'], + get_password('db', 'glance') ) db_create_with_user( - "metering", - node["openstack"]["db"]["metering"]["username"], - db_password(node['openstack']['db']['metering']['password']) + 'telemetry', + node['openstack']['db']['telemetry']['username'], + get_password('db', 'ceilometer') ) db_create_with_user( - "network", - node["openstack"]["db"]["network"]["username"], - db_password(node['openstack']['db']['network']['password']) + 'network', + node['openstack']['db']['network']['username'], + get_password('db', 'neutron') ) db_create_with_user( - "volume", - node["openstack"]["db"]["volume"]["username"], - db_password(node['openstack']['db']['volume']['password']) + 'block-storage', + node['openstack']['db']['block-storage']['username'], + get_password('db', 'cinder') ) +db_create_with_user( + 'orchestration', + node['openstack']['db']['orchestration']['username'], + get_password('db', 'heat') +) diff --git a/chef/cookbooks/openstack-ops-database/recipes/postgresql-client.rb b/chef/cookbooks/openstack-ops-database/recipes/postgresql-client.rb index 5d259b4..29d017c 100644 --- a/chef/cookbooks/openstack-ops-database/recipes/postgresql-client.rb +++ b/chef/cookbooks/openstack-ops-database/recipes/postgresql-client.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-database # Recipe:: postgresql-client @@ -19,9 +20,9 @@ # limitations under the License. # -include_recipe "postgresql::ruby" -include_recipe "postgresql::client" +include_recipe 'postgresql::ruby' +include_recipe 'postgresql::client' -node["openstack"]["db"]["platform"]["postgresql_python_packages"].each do |pkg| +node['openstack']['db']['platform']['postgresql_python_packages'].each do |pkg| package pkg end diff --git a/chef/cookbooks/openstack-ops-database/recipes/postgresql-server.rb b/chef/cookbooks/openstack-ops-database/recipes/postgresql-server.rb index 56bf4e9..6a37bae 100644 --- a/chef/cookbooks/openstack-ops-database/recipes/postgresql-server.rb +++ b/chef/cookbooks/openstack-ops-database/recipes/postgresql-server.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-database # Recipe:: postgresql-server @@ -20,13 +21,13 @@ # limitations under the License. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -listen_address = address_for node["openstack"]["db"]["bind_interface"] +db_endpoint = endpoint 'db' -node.override["postgresql"]["config"]["listen_addresses"] = listen_address +node.override['postgresql']['config']['listen_addresses'] = db_endpoint.host -include_recipe "openstack-ops-database::postgresql-client" -include_recipe "postgresql::server" +include_recipe 'openstack-ops-database::postgresql-client' +include_recipe 'postgresql::server' diff --git a/chef/cookbooks/openstack-ops-database/recipes/server.rb b/chef/cookbooks/openstack-ops-database/recipes/server.rb index 09f4b40..70cb04f 100644 --- a/chef/cookbooks/openstack-ops-database/recipes/server.rb +++ b/chef/cookbooks/openstack-ops-database/recipes/server.rb @@ -1,8 +1,10 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-database # Recipe:: server # # Copyright 2013, Opscode, Inc. +# Copyright 2013, AT&T Services, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +19,4 @@ # limitations under the License. # -include_recipe "openstack-ops-database::#{node['openstack']['database']['service']}-server" +include_recipe "openstack-ops-database::#{node['openstack']['db']['service_type']}-server" diff --git a/chef/cookbooks/openstack-ops-database/spec/client_spec.rb b/chef/cookbooks/openstack-ops-database/spec/client_spec.rb new file mode 100644 index 0000000..a899725 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/client_spec.rb @@ -0,0 +1,23 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::client' do + describe 'ubuntu' do + include_context 'database-stubs' + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + it 'uses mysql database client recipe by default' do + expect(chef_run).to include_recipe 'openstack-ops-database::mysql-client' + end + + it 'uses postgresql database client recipe when configured' do + node.set['openstack']['db']['service_type'] = 'postgresql' + node.set['postgresql']['password']['postgres'] = 'secret' + + expect(chef_run).to include_recipe 'openstack-ops-database::postgresql-client' + end + end +end diff --git a/chef/cookbooks/openstack-ops-database/spec/mysql-client-suse_spec.rb b/chef/cookbooks/openstack-ops-database/spec/mysql-client-suse_spec.rb new file mode 100644 index 0000000..89f3983 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/mysql-client-suse_spec.rb @@ -0,0 +1,15 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::mysql-client' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + it 'installs mysql packages' do + expect(chef_run).to install_package('python-mysql') + end + end +end diff --git a/chef/cookbooks/openstack-ops-database/spec/mysql-client_spec.rb b/chef/cookbooks/openstack-ops-database/spec/mysql-client_spec.rb new file mode 100644 index 0000000..53795d0 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/mysql-client_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::mysql-client' do + include_context 'database-stubs' + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + it 'includes mysql recipes' do + expect(chef_run).to include_recipe 'mysql::ruby' + expect(chef_run).to include_recipe 'mysql::client' + end + + it 'installs mysql packages' do + expect(chef_run).to install_package 'python-mysqldb' + end + end +end diff --git a/chef/cookbooks/openstack-ops-database/spec/mysql-server-redhat_spec.rb b/chef/cookbooks/openstack-ops-database/spec/mysql-server-redhat_spec.rb new file mode 100644 index 0000000..51406cb --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/mysql-server-redhat_spec.rb @@ -0,0 +1,24 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::mysql-server' do + describe 'redhat' do + include_context 'database-stubs' + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set_unless['mysql'] = { + 'server_debian_password' => 'server-debian-password', + 'server_root_password' => 'server-root-password', + 'server_repl_password' => 'server-repl-password' + } + runner.converge(described_recipe) + end + + it 'modifies my.cnf template to notify mysql restart' do + file = chef_run.template('final-my.cnf') + expect(file).to notify('service[mysql]').to(:restart) + end + end +end diff --git a/chef/cookbooks/openstack-ops-database/spec/mysql-server_spec.rb b/chef/cookbooks/openstack-ops-database/spec/mysql-server_spec.rb new file mode 100644 index 0000000..e236f7f --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/mysql-server_spec.rb @@ -0,0 +1,84 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::mysql-server' do + describe 'ubuntu' do + include_context 'database-stubs' + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set_unless['mysql'] = { + 'server_debian_password' => 'server-debian-password', + 'server_root_password' => 'server-root-password', + 'server_repl_password' => 'server-repl-password' + } + runner.converge(described_recipe) + end + + it 'overrides default mysql attributes' do + expect(chef_run.node['mysql']['bind_address']).to eql '127.0.0.1' + expect(chef_run.node['mysql']['tunable']['innodb_thread_concurrency']).to eql '0' + expect(chef_run.node['mysql']['tunable']['innodb_commit_concurrency']).to eql '0' + expect(chef_run.node['mysql']['tunable']['innodb_read_io_threads']).to eql '4' + expect(chef_run.node['mysql']['tunable']['innodb_flush_log_at_trx_commit']).to eql '2' + expect(chef_run.node['mysql']['tunable']['skip-name-resolve']).to eql true + end + + it 'includes mysql recipes' do + expect(chef_run).to include_recipe 'openstack-ops-database::mysql-client' + expect(chef_run).to include_recipe 'mysql::server' + end + + it 'modifies my.cnf template to notify mysql restart' do + file = chef_run.template '/etc/mysql/my.cnf' + expect(file).to notify('service[mysql]').to(:restart) + end + + describe 'lwrps' do + connection = { + host: 'localhost', + username: 'root', + password: 'server-root-password' + } + + it 'removes insecure default localhost mysql users' do + resource = chef_run.find_resource( + 'mysql_database', + 'drop empty localhost user' + ).to_hash + + expect(resource).to include( + sql: "DELETE FROM mysql.user WHERE User = '' OR Password = ''", + connection: connection, + action: [:query] + ) + end + + it 'drops the test database' do + resource = chef_run.find_resource( + 'mysql_database', + 'test' + ).to_hash + + expect(resource).to include( + connection: connection, + action: [:drop] + ) + end + + it 'flushes privileges' do + resource = chef_run.find_resource( + 'mysql_database', + 'FLUSH PRIVILEGES' + ).to_hash + + expect(resource).to include( + connection: connection, + sql: 'FLUSH PRIVILEGES', + action: [:query] + ) + end + end + end +end diff --git a/chef/cookbooks/openstack-ops-database/spec/openstack-db_spec.rb b/chef/cookbooks/openstack-ops-database/spec/openstack-db_spec.rb new file mode 100644 index 0000000..548ea99 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/openstack-db_spec.rb @@ -0,0 +1,69 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::openstack-db' do + before do + ::Chef::Recipe.any_instance.stub(:db_create_with_user) + ::Chef::Recipe.any_instance.stub(:get_password) + .with('db', anything) + .and_return('test-pass') + @chef_run = ::ChefSpec::Runner.new ::UBUNTU_OPTS + end + + it 'creates nova database and user' do + ::Chef::Recipe.any_instance.should_receive(:db_create_with_user) + .with 'compute', 'nova', 'test-pass' + + @chef_run.converge 'openstack-ops-database::openstack-db' + end + + it 'creates dashboard database and user' do + ::Chef::Recipe.any_instance.should_receive(:db_create_with_user) + .with 'dashboard', 'dash', 'test-pass' + + @chef_run.converge 'openstack-ops-database::openstack-db' + end + + it 'creates identity database and user' do + ::Chef::Recipe.any_instance.should_receive(:db_create_with_user) + .with 'identity', 'keystone', 'test-pass' + + @chef_run.converge 'openstack-ops-database::openstack-db' + end + + it 'creates image database and user' do + ::Chef::Recipe.any_instance.should_receive(:db_create_with_user) + .with 'image', 'glance', 'test-pass' + + @chef_run.converge 'openstack-ops-database::openstack-db' + end + + it 'creates telemetry database and user' do + ::Chef::Recipe.any_instance.should_receive(:db_create_with_user) + .with 'telemetry', 'ceilometer', 'test-pass' + + @chef_run.converge 'openstack-ops-database::openstack-db' + end + + it 'creates network database and user' do + ::Chef::Recipe.any_instance.should_receive(:db_create_with_user) + .with 'network', 'neutron', 'test-pass' + + @chef_run.converge 'openstack-ops-database::openstack-db' + end + + it 'creates volume database and user' do + ::Chef::Recipe.any_instance.should_receive(:db_create_with_user) + .with 'block-storage', 'cinder', 'test-pass' + + @chef_run.converge 'openstack-ops-database::openstack-db' + end + + it 'creates orchestration database and user' do + ::Chef::Recipe.any_instance.should_receive(:db_create_with_user) + .with 'orchestration', 'heat', 'test-pass' + + @chef_run.converge 'openstack-ops-database::openstack-db' + end +end diff --git a/chef/cookbooks/openstack-ops-database/spec/postgresql-server_spec.rb b/chef/cookbooks/openstack-ops-database/spec/postgresql-server_spec.rb new file mode 100644 index 0000000..048dba2 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/postgresql-server_spec.rb @@ -0,0 +1,26 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::postgresql-server' do + describe 'ubuntu' do + include_context 'database-stubs' + + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + # The postgresql cookbook will raise an 'uninitialized constant + # Chef::Application' error without this attribute when running + # the tests + node.set_unless['postgresql']['password']['postgres'] = String.new + + runner.converge(described_recipe) + end + + it 'includes postgresql recipes' do + expect(chef_run).to include_recipe( + 'openstack-ops-database::postgresql-client') + expect(chef_run).to include_recipe('postgresql::server') + end + end +end diff --git a/chef/cookbooks/openstack-ops-database/spec/server_spec.rb b/chef/cookbooks/openstack-ops-database/spec/server_spec.rb new file mode 100644 index 0000000..33c84cb --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/server_spec.rb @@ -0,0 +1,34 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::server' do + describe 'ubuntu' do + include_context 'database-stubs' + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + node.set_unless['mysql'] = { + 'server_debian_password' => 'server-debian-password', + 'server_root_password' => 'server-root-password', + 'server_repl_password' => 'server-repl-password' + } + runner.converge(described_recipe) + end + + it 'uses mysql database server recipe by default' do + expect(chef_run).to include_recipe('openstack-ops-database::mysql-server') + end + + it 'uses postgresql database server recipe when configured' do + node.set['openstack']['db']['service_type'] = 'postgresql' + # The postgresql cookbook will raise an 'uninitialized constant + # Chef::Application' error without this attribute when running + # the tests + node.set_unless['postgresql']['password']['postgres'] = String.new + + expect(chef_run).to include_recipe( + 'openstack-ops-database::postgresql-server') + end + end +end diff --git a/chef/cookbooks/openstack-ops-database/spec/spec_helper.rb b/chef/cookbooks/openstack-ops-database/spec/spec_helper.rb new file mode 100644 index 0000000..4eceb7b --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/spec/spec_helper.rb @@ -0,0 +1,35 @@ +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' + +ChefSpec::Coverage.start! { add_filter 'openstack-ops-database' } + +::LOG_LEVEL = :fatal +::SUSE_OPTS = { + platform: 'suse', + version: '11.03', + log_level: ::LOG_LEVEL +} +::REDHAT_OPTS = { + platform: 'redhat', + version: '6.5', + log_level: ::LOG_LEVEL +} +::UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: ::LOG_LEVEL +} + +shared_context 'database-stubs' do + before do + # for redhat + stub_command("/usr/bin/mysql -u root -e 'show databases;'") + # for debian + stub_command("\"/usr/bin/mysql\" -u root -e 'show databases;'") + + ::Chef::Recipe.any_instance.stub(:address_for) + .with('lo') + .and_return('127.0.0.1') + end +end diff --git a/chef/cookbooks/openstack-ops-messaging/.rubocop.yml b/chef/cookbooks/openstack-ops-messaging/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-ops-messaging/.tailor b/chef/cookbooks/openstack-ops-messaging/.tailor deleted file mode 100644 index 99f0dcf..0000000 --- a/chef/cookbooks/openstack-ops-messaging/.tailor +++ /dev/null @@ -1,25 +0,0 @@ -Tailor.config do |config| - config.formatters "text" - config.file_set '**/*.rb' do |style| - style.max_line_length 80, level: :off - style.allow_camel_case_methods false, level: :error - style.allow_hard_tabs false, level: :error - style.allow_screaming_snake_case_classes false, level: :error - style.allow_trailing_line_spaces false, level: :error - style.allow_invalid_ruby false, level: :warn - style.indentation_spaces 2, level: :error - style.max_code_lines_in_class 300, level: :error - style.max_code_lines_in_method 30, level: :error - style.spaces_after_comma 1, level: :error - style.spaces_after_lbrace 1, level: :error - style.spaces_after_lbracket 0, level: :error - style.spaces_after_lparen 0, level: :error - style.spaces_before_comma 0, level: :error - style.spaces_before_lbrace 1, level: :error - style.spaces_before_rbrace 1, level: :error - style.spaces_before_rbracket 0, level: :error - style.spaces_before_rparen 0, level: :error - style.spaces_in_empty_braces 0, level: :error - style.trailing_newlines 1, level: :error - end -end diff --git a/chef/cookbooks/openstack-ops-messaging/Berksfile.lock b/chef/cookbooks/openstack-ops-messaging/Berksfile.lock deleted file mode 100644 index 517fc20..0000000 --- a/chef/cookbooks/openstack-ops-messaging/Berksfile.lock +++ /dev/null @@ -1,46 +0,0 @@ -{ - "sha": "45e1bf81e0dd550088ee5794e167786e616570ea", - "sources": { - "openstack-ops-messaging": { - "path": "." - }, - "openstack-common": { - "locked_version": "0.3.0", - "git": "git://github.com/stackforge/cookbook-openstack-common.git", - "ref": "25b183f2362fa501cfee4db331491b3d984a5c05" - }, - "rabbitmq": { - "locked_version": "2.1.2" - }, - "erlang": { - "locked_version": "1.3.0" - }, - "apt": { - "locked_version": "2.0.0" - }, - "yum": { - "locked_version": "2.3.0" - }, - "build-essential": { - "locked_version": "1.4.0" - }, - "database": { - "locked_version": "1.4.0" - }, - "mysql": { - "locked_version": "3.0.2" - }, - "openssl": { - "locked_version": "1.0.2" - }, - "postgresql": { - "locked_version": "3.0.2" - }, - "aws": { - "locked_version": "0.101.2" - }, - "xfs": { - "locked_version": "1.1.0" - } - } -} diff --git a/chef/cookbooks/openstack-ops-messaging/CHANGELOG.md b/chef/cookbooks/openstack-ops-messaging/CHANGELOG.md index fa14ddd..5151f9f 100644 --- a/chef/cookbooks/openstack-ops-messaging/CHANGELOG.md +++ b/chef/cookbooks/openstack-ops-messaging/CHANGELOG.md @@ -1,4 +1,32 @@ +# CHANGELOG for cookbook-openstack-ops-messaging + +This file is used to list changes made in each version of cookbook-openstack-ops-messaging. + +## 9.0.1 +### Bug +* Fix the depends cookbook version issue in metadata.rb + +## 9.0.0 +* Upgrade to Icehouse + +## 8.0.1: +* Add change_password to make rabbitmq work when develop_mode=false + +## 8.0.0 +* upgrade to Havana + +## 7.0.1: + +* default the node['openstack'][*]['rabbit'] attributes for all the services + using rabbitmq (block-storage, compute, image, metering, network) to whatever + node['openstack']['mq'] attributes are set. + ## 7.0.0 * Initial release intended for Grizzly-based OpenStack releases, for use with Stackforge upstream repositories. + +- - - +Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. + +The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. diff --git a/chef/cookbooks/openstack-ops-messaging/Gemfile b/chef/cookbooks/openstack-ops-messaging/Gemfile index 04ef97e..2b8e634 100644 --- a/chef/cookbooks/openstack-ops-messaging/Gemfile +++ b/chef/cookbooks/openstack-ops-messaging/Gemfile @@ -1,9 +1,10 @@ -source "https://rubygems.org" +# encoding: UTF-8 +source 'https://rubygems.org' -gem "chef", "~> 11.4.4" -gem "json", "<= 1.7.7" # chef 11 dependency -gem "berkshelf", "~> 2.0.3" -gem "chefspec", "~> 1.3.0" -gem "foodcritic" -gem "strainer" -gem "tailor" +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' diff --git a/chef/cookbooks/openstack-ops-messaging/Gemfile.lock b/chef/cookbooks/openstack-ops-messaging/Gemfile.lock index 2d00f7d..2954927 100644 --- a/chef/cookbooks/openstack-ops-messaging/Gemfile.lock +++ b/chef/cookbooks/openstack-ops-messaging/Gemfile.lock @@ -1,34 +1,36 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.0.0) + activesupport (3.2.17) i18n (~> 0.6, >= 0.6.4) - minitest (~> 4.2) - multi_json (~> 1.3) - thread_safe (~> 0.1) - tzinfo (~> 0.3.37) - addressable (2.3.5) - akami (1.2.0) + multi_json (~> 1.0) + addressable (2.3.6) + akami (1.2.1) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - atomic (1.1.10) - berkshelf (2.0.5) - activesupport (>= 3.2.0) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) + activesupport (~> 3.2.0) addressable (~> 2.3.4) buff-shell_out (~> 0.1) - celluloid (>= 0.14.0) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) minitar (~> 0.5.4) rbzip2 (~> 0.2.0) retryable (~> 1.3.3) - ridley (~> 1.2.1) + ridley (~> 1.5.0) solve (>= 0.5.0) thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) buff-ruby_engine (0.1.0) - buff-shell_out (0.1.0) + buff-shell_out (0.1.1) buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) @@ -36,113 +38,127 @@ GEM celluloid-io (0.14.1) celluloid (>= 0.14.1) nio4r (>= 0.4.5) - chef (11.4.4) - erubis - highline (>= 1.6.9) - json (>= 1.4.4, <= 1.7.7) - mixlib-authentication (>= 1.3.0) - mixlib-cli (~> 1.3.0) - mixlib-config (>= 1.1.2) - mixlib-log (>= 1.3.0) - mixlib-shellout + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) net-ssh (~> 2.6) - net-ssh-multi (~> 1.1.0) - ohai (>= 0.6.0) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) rest-client (>= 1.0.4, < 1.7.0) yajl-ruby (~> 1.1) - chefspec (1.3.1) - chef (>= 10.0) - erubis - fauxhai (>= 0.1.1, < 2.0) - minitest-chef-handler (>= 0.6.0) - rspec (~> 2.0) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) chozo (0.6.1) activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.8.4) - builder (>= 2.1.2) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.7) - multipart-post (~> 1.1) - fauxhai (1.1.1) - httparty + faraday (0.8.9) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) net-ssh ohai - ffi (1.9.0) - foodcritic (2.1.0) + ffi (1.9.3) + foodcritic (3.0.3) erubis gherkin (~> 2.11.7) nokogiri (~> 1.5.4) - rak (~> 1.4) + rake treetop (~> 1.4.10) yajl-ruby (~> 1.1.0) gherkin (2.11.8) multi_json (~> 1.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.0.0) + gyoku (1.1.1) builder (>= 2.1.2) hashie (2.0.5) - highline (1.6.19) - httparty (0.11.0) - multi_json (~> 1.0) - multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.4) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - mime-types (1.23) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) minitar (0.5.4) - minitest (4.7.5) - minitest-chef-handler (1.0.1) - chef - ci_reporter - minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log - mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-cli (1.4.0) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.1.0) - multi_json (1.7.7) - multi_xml (0.5.4) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) multipart-post (1.2.0) - net-http-persistent (2.8) - net-ssh (2.6.7) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - net-ssh-multi (1.1) - net-ssh (>= 2.1.4) - net-ssh-gateway (>= 0.99.0) - nio4r (0.4.6) - nokogiri (1.5.10) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) nori (1.1.5) - ohai (6.16.0) + ohai (6.20.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) rack (1.5.2) - rak (1.4) + rainbow (2.0.0) + rake (10.2.2) rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (1.2.3) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) buff-extensions (~> 0.3) + buff-ignore (~> 1.1) buff-shell_out (~> 0.1) celluloid (~> 0.14.0) celluloid-io (~> 0.14.0) @@ -153,18 +169,24 @@ GEM mixlib-authentication (>= 1.3.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) varia_model (~> 0.1) winrm (~> 1.1.0) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.1) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) rubyntlm (0.1.1) savon (0.9.5) akami (~> 1.0) @@ -174,37 +196,29 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.6.0) - strainer (3.0.3) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) berkshelf (~> 2.0) + buff-platform (~> 0.1) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - text-table (1.2.3) thor (0.18.1) - thread_safe (0.1.0) - atomic - timers (1.1.0) - tins (0.8.2) - treetop (1.4.14) + timers (2.0.0) + hitimes + treetop (1.4.15) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.37) uuidtools (2.1.4) - varia_model (0.1.0) - buff-extensions (~> 0.1) + varia_model (0.3.2) + buff-extensions (~> 0.2) hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) @@ -214,10 +228,10 @@ PLATFORMS ruby DEPENDENCIES - berkshelf (~> 2.0.3) - chef (~> 11.4.4) - chefspec (~> 1.3.0) - foodcritic + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + foodcritic (~> 3.0.3) json (<= 1.7.7) + rubocop (~> 0.18.1) strainer - tailor diff --git a/chef/cookbooks/openstack-ops-messaging/README.md b/chef/cookbooks/openstack-ops-messaging/README.md index 347470f..109e617 100644 --- a/chef/cookbooks/openstack-ops-messaging/README.md +++ b/chef/cookbooks/openstack-ops-messaging/README.md @@ -1,6 +1,6 @@ # Description # -This cookbook provides shared message queue configuration for the OpenStack **Grizzly** reference deployment provided by Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. It currently supports RabbitMQ and will soon other queues. +This cookbook provides shared message queue configuration for the OpenStack **Icehouse** reference deployment provided by Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. It currently supports RabbitMQ and will soon other queues. # Requirements # @@ -41,21 +41,32 @@ None # Attributes # -* `openstack["mq"]["bind_interface"]` - bind to interfaces IPv4 address * `openstack["mq"]["cluster"]` - whether or not to cluster rabbit, defaults to 'false' +The following attributes are defined in attributes/messaging.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack["endpoints"]["mq"]["host"]` - The IP address to bind the rabbit service to +* `openstack["endpoints"]["mq"]["scheme"]` - Unused at this time +* `openstack["endpoints"]["mq"]["port"]` - The port to bind the rabbit service to +* `openstack["endpoints"]["mq"]["path"]` - Unused at this time +* `openstack["endpoints"]["mq"]["bind_interface"]` - The interface name to bind the rabbit service to + +If the value of the "bind_interface" attribute is non-nil, then the rabbit service will be bound to the first IP address on that interface. If the value of the "bind_interface" attribute is nil, then the rabbit service will be bound to the IP address specified in the host attribute. + Testing ===== -This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. -Tests are defined in Strainerfile. +Berkshelf +===== -To run tests: - - $ bundle install # install gem dependencies - $ bundle exec berks install # install cookbook dependencies - $ bundle exec strainer test # run tests +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. License and Author ================== @@ -65,10 +76,15 @@ License and Author | **Author** | John Dewey () | | **Author** | Matt Ray () | | **Author** | Craig Tracey () | +| **Author** | Ionut Artarisi () | +| **Author** | JieHua Jin () | | | | | **Copyright** | Copyright (c) 2013, Opscode, Inc. | | **Copyright** | Copyright (c) 2013, Craig Tracey | | **Copyright** | Copyright (c) 2013, AT&T Services, Inc. | +| **Copyright** | Copyright (c) 2013, SUSE Linux GmbH. | +| **Copyright** | Copyright (c) 2013, IBM Corp. | + Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/chef/cookbooks/openstack-ops-messaging/Strainerfile b/chef/cookbooks/openstack-ops-messaging/Strainerfile index 7e292b4..3af3468 100644 --- a/chef/cookbooks/openstack-ops-messaging/Strainerfile +++ b/chef/cookbooks/openstack-ops-messaging/Strainerfile @@ -1,5 +1,5 @@ # Strainerfile -tailor: bundle exec tailor +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK/ --config $SANDBOX/$COOKBOOK/.rubocop.yml knife test: bundle exec knife cookbook test $COOKBOOK -foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 $SANDBOX/$COOKBOOK chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-ops-messaging/TESTING.md b/chef/cookbooks/openstack-ops-messaging/TESTING.md new file mode 100644 index 0000000..d9b1b74 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/TESTING.md @@ -0,0 +1,33 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-ops-messaging/attributes/default.rb b/chef/cookbooks/openstack-ops-messaging/attributes/default.rb index 95782cc..1d3271b 100644 --- a/chef/cookbooks/openstack-ops-messaging/attributes/default.rb +++ b/chef/cookbooks/openstack-ops-messaging/attributes/default.rb @@ -1,8 +1,10 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-messaging # Recipe:: default # # Copyright 2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,5 +19,8 @@ # limitations under the License. # -default["openstack"]["mq"]["bind_interface"] = "lo" -default["openstack"]["mq"]["cluster"] = false +default['openstack']['mq']['cluster'] = false + +if platform_family?('debian', 'suse') + override['rabbitmq']['use_distro_version'] = true +end diff --git a/chef/cookbooks/openstack-ops-messaging/metadata.rb b/chef/cookbooks/openstack-ops-messaging/metadata.rb index 093386f..c74dd84 100644 --- a/chef/cookbooks/openstack-ops-messaging/metadata.rb +++ b/chef/cookbooks/openstack-ops-messaging/metadata.rb @@ -1,16 +1,17 @@ -name "openstack-ops-messaging" -maintainer "Opscode, Inc." -maintainer_email "matt@opscode.com" -license "Apache 2.0" -description "Provides the shared messaging configuration for Chef for OpenStack." -version "7.0.0" +# encoding: UTF-8 +name 'openstack-ops-messaging' +maintainer 'Chef Software, Inc.' +maintainer_email 'matt@getchef.com' +license 'Apache 2.0' +description 'Provides the shared messaging configuration for Chef for OpenStack.' +version '9.0.1' -recipe "server", "Installs and configures server packages for messaging queue used by the deployment." -recipe "rabbitmq-server", "Installs and configures RabbitMQ and is called via the server recipe" +recipe 'server', 'Installs and configures server packages for messaging queue used by the deployment.' +recipe 'rabbitmq-server', 'Installs and configures RabbitMQ and is called via the server recipe' -%w{ fedora ubuntu redhat centos }.each do |os| +%w{ fedora ubuntu redhat centos suse }.each do |os| supports os end -depends "openstack-common", "~> 0.4.0" -depends "rabbitmq", ">= 2.1.0" +depends 'openstack-common', '~> 9.0' +depends 'rabbitmq', '>= 3.0.4' diff --git a/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq-server.rb b/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq-server.rb index 3dab40a..54b6607 100644 --- a/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq-server.rb +++ b/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq-server.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-messaging # Recipe:: rabbitmq-server @@ -6,75 +7,80 @@ # Copyright 2013, AT&T Services, Inc. # Copyright 2013, Craig Tracey # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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. # -class ::Chef::Recipe +class ::Chef::Recipe # rubocop:disable Documentation include ::Openstack end -rabbit_server_role = node["openstack"]["mq"]["server_role"] -user = node["openstack"]["mq"]["user"] -password = node["openstack"]["mq"]["password"] -pass = user_password password -vhost = node["openstack"]["mq"]["vhost"] -bind_interface = node["openstack"]["mq"]["bind_interface"] -#listen_address = address_for node["openstack"]["mq"]["bind_interface"] unless defined?(node['openstack']['mq']['bind_address']) -listen_address = node['openstack']['mq']['bind_address'] +user = node['openstack']['mq']['user'] +password = node['openstack']['mq']['password'] +pass = get_password 'user', user, password + +vhost = node['openstack']['mq']['vhost'] +rabbit_endpoint = endpoint 'mq' +listen_address = rabbit_endpoint.host # Used by OpenStack#rabbit_servers/#rabbit_server -node.set["openstack"]["mq"]["listen"] = listen_address +node.set['openstack']['mq']['listen'] = listen_address -node.override["rabbitmq"]["port"] = node["openstack"]["mq"]["port"] -node.override["rabbitmq"]["address"] = listen_address -node.override["rabbitmq"]["default_user"] = user -node.override["rabbitmq"]["default_pass"] = pass -node.override["rabbitmq"]["use_distro_version"] = true +node.override['rabbitmq']['port'] = rabbit_endpoint.port +node.override['rabbitmq']['address'] = listen_address +node.override['rabbitmq']['default_user'] = user +node.override['rabbitmq']['default_pass'] = pass # Clustering -if node["openstack"]["mq"]["cluster"] - node.override["rabbitmq"]["cluster"] = node["openstack"]["mq"]["cluster"] - node.override["rabbitmq"]["erlang_cookie"] = service_password "rabbit_cookie" - qs = "roles:#{rabbit_server_role} AND chef_environment:#{node.chef_environment}" - node.override["rabbitmq"]["cluster_disk_nodes"] = search(:node, qs).map do |n| +if node['openstack']['mq']['cluster'] + node.override['rabbitmq']['cluster'] = node['openstack']['mq']['cluster'] + node.override['rabbitmq']['erlang_cookie'] = get_password 'service', 'rabbit_cookie' + qs = "roles:#{node['openstack']['mq']['server_role']} AND chef_environment:#{node.chef_environment}" + node.override['rabbitmq']['cluster_disk_nodes'] = search(:node, qs).map do |n| "#{user}@#{n['hostname']}" end.sort end -include_recipe "rabbitmq" -include_recipe "rabbitmq::mgmt_console" +include_recipe 'rabbitmq' +include_recipe 'rabbitmq::mgmt_console' -rabbitmq_user "remove rabbit guest user" do - user "guest" +rabbitmq_user 'remove rabbit guest user' do + user 'guest' action :delete - not_if { user == "guest" } + not_if { user == 'guest' } end -rabbitmq_user "add openstack rabbit user" do +rabbitmq_user 'add openstack rabbit user' do user user password pass action :add end -rabbitmq_vhost "add openstack rabbit vhost" do +rabbitmq_user 'change openstack rabbit user password' do + user user + password pass + + action :change_password +end + +rabbitmq_vhost 'add openstack rabbit vhost' do vhost vhost action :add end -rabbitmq_user "set openstack user permissions" do +rabbitmq_user 'set openstack user permissions' do user user vhost vhost permissions '.* .* .*' @@ -82,9 +88,9 @@ rabbitmq_user "set openstack user permissions" do end # Necessary for graphing. -rabbitmq_user "set rabbit administrator tag" do +rabbitmq_user 'set rabbit administrator tag' do user user - tag "administrator" + tag 'administrator' action :set_tags end diff --git a/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq.rb b/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq.rb deleted file mode 100644 index e867748..0000000 --- a/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq.rb +++ /dev/null @@ -1,58 +0,0 @@ -# -# Cookbook Name:: openstack-ops-messaging -# Recipe:: rabbitmq -# -# Copyright 2013, Opscode, Inc. -# Copyright 2012, John Dewey -# -# 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::Recipe.send(:include, Opscode::OpenSSL::Password) - -node.set_unless['openstack']['messaging']['password'] = secure_password -node.set_unless['rabbitmq']['address'] = '0.0.0.0' -node.set_unless['rabbitmq']['port'] = 5672 - -include_recipe "rabbitmq" -include_recipe "rabbitmq::mgmt_console" - -user = node['openstack']['messaging']['user'] -vhost = node['openstack']['messaging']['vhost'] - -# remove the guest user -rabbitmq_user 'guest' do - action :delete - not_if { user.eql?('guest') } -end - -rabbitmq_user user do - password node['openstack']['messaging']['password'] - action :add -end - -rabbitmq_vhost vhost do - action :add -end - -rabbitmq_user user do - vhost vhost - permissions ".* .* .*" - action :set_permissions -end - -# Necessary for graphing. -rabbitmq_user user do - tag "administrator" - action :set_tags -end diff --git a/chef/cookbooks/openstack-ops-messaging/recipes/server.rb b/chef/cookbooks/openstack-ops-messaging/recipes/server.rb index 35ccd73..2da1747 100644 --- a/chef/cookbooks/openstack-ops-messaging/recipes/server.rb +++ b/chef/cookbooks/openstack-ops-messaging/recipes/server.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # # Cookbook Name:: openstack-ops-messaging # Recipe:: server @@ -6,17 +7,17 @@ # Copyright 2013, Craig Tracey # Copyright 2013, AT&T Services, Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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. # -include_recipe "openstack-ops-messaging::#{node["openstack"]["mq"]["service_type"]}-server" +include_recipe "openstack-ops-messaging::#{node['openstack']['mq']['service_type']}-server" diff --git a/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server-redhat_spec.rb b/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server-redhat_spec.rb new file mode 100644 index 0000000..f3e5d36 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server-redhat_spec.rb @@ -0,0 +1,17 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-ops-messaging::rabbitmq-server' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'ops_messaging_stubs' + + it 'does not set use_distro_version to true' do + expect(chef_run.node['rabbitmq']['use_distro_version']).to be_false + end + + end +end diff --git a/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server_spec.rb b/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server_spec.rb index 817387d..dc48864 100644 --- a/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server_spec.rb +++ b/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server_spec.rb @@ -1,129 +1,115 @@ -require_relative "spec_helper" +# encoding: UTF-8 +require_relative 'spec_helper' -describe "openstack-ops-messaging::rabbitmq-server" do - before { ops_messaging_stubs } - describe "ubuntu" do - before do - @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - @chef_run.converge "openstack-ops-messaging::rabbitmq-server" +describe 'openstack-ops-messaging::rabbitmq-server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'ops_messaging_stubs' + + it 'overrides default rabbit attributes' do + expect(chef_run.node['openstack']['endpoints']['mq']['port']).to eq('5672') + expect(chef_run.node['openstack']['mq']['listen']).to eq('127.0.0.1') + expect(chef_run.node['rabbitmq']['address']).to eq('127.0.0.1') + expect(chef_run.node['rabbitmq']['default_user']).to eq('guest') + expect(chef_run.node['rabbitmq']['default_pass']).to eq('rabbit-pass') + expect(chef_run.node['rabbitmq']['use_distro_version']).to be_true end - it "overrides default rabbit attributes" do - expect(@chef_run.node["openstack"]["mq"]["port"]).to eql "5672" - expect(@chef_run.node["openstack"]["mq"]["listen"]).to eql "127.0.0.1" - expect(@chef_run.node["rabbitmq"]["address"]).to eql "127.0.0.1" - expect(@chef_run.node["rabbitmq"]["default_user"]).to eql "guest" - expect(@chef_run.node['rabbitmq']['default_pass']).to eql "rabbit-pass" + it 'overrides rabbit and openstack image attributes' do + node.set['openstack']['endpoints']['mq']['bind_interface'] = 'eth0' + node.set['openstack']['endpoints']['mq']['port'] = '4242' + node.set['openstack']['mq']['user'] = 'foo' + node.set['openstack']['mq']['vhost'] = '/bar' + + expect(chef_run.node['openstack']['mq']['listen']).to eq('33.44.55.66') + expect(chef_run.node['openstack']['endpoints']['mq']['port']).to eq('4242') + expect(chef_run.node['openstack']['mq']['user']).to eq('foo') + expect(chef_run.node['openstack']['mq']['vhost']).to eq('/bar') + expect(chef_run.node['openstack']['mq']['image']['rabbit']['port']).to eq('4242') + expect(chef_run.node['openstack']['mq']['image']['rabbit']['userid']).to eq('foo') + expect(chef_run.node['openstack']['mq']['image']['rabbit']['vhost']).to eq('/bar') end - describe "cluster" do + describe 'cluster' do before do - @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| - n.set["openstack"]["mq"] = { - "cluster" => true - } + node.set['openstack']['mq'] = { + 'cluster' => true + } + end + + it 'overrides cluster' do + expect(chef_run.node['rabbitmq']['cluster']).to be_true + end + + it 'overrides erlang_cookie' do + expect(chef_run.node['rabbitmq']['erlang_cookie']).to eq( + 'erlang-cookie' + ) + end + + it 'overrides and sorts cluster_disk_nodes' do + expect(chef_run.node['rabbitmq']['cluster_disk_nodes']).to eq( + ['guest@host1', 'guest@host2'] + ) + end + end + + it 'includes rabbit recipes' do + expect(chef_run).to include_recipe 'rabbitmq' + expect(chef_run).to include_recipe 'rabbitmq::mgmt_console' + end + + describe 'lwrps' do + context 'default mq attributes' do + it 'does not delete the guest user' do + expect(chef_run).not_to delete_rabbitmq_user('remove rabbit guest user') end - @chef_run.converge "openstack-ops-messaging::rabbitmq-server" end - it "overrides cluster" do - expect(@chef_run.node['rabbitmq']['cluster']).to be_true - end + context 'custom mq attributes' do + before do + node.set['openstack']['mq']['user'] = 'not-a-guest' + node.set['openstack']['mq']['vhost'] = '/foo' + end - it "overrides erlang_cookie" do - expect(@chef_run.node['rabbitmq']['erlang_cookie']).to eql( - "erlang-cookie" - ) - end + it 'deletes the guest user' do + expect(chef_run).to delete_rabbitmq_user( + 'remove rabbit guest user' + ).with(user: 'guest') + end - it "overrides and sorts cluster_disk_nodes" do - expect(@chef_run.node['rabbitmq']['cluster_disk_nodes']).to eql( - ["guest@host1", "guest@host2"] - ) - end - end + it 'adds openstack rabbit user' do + expect(chef_run).to add_rabbitmq_user( + 'add openstack rabbit user' + ).with(user: 'not-a-guest', password: 'rabbit-pass') + end - it "includes rabbit recipes" do - expect(@chef_run).to include_recipe "rabbitmq" - expect(@chef_run).to include_recipe "rabbitmq::mgmt_console" - end + it 'changes openstack rabbit user password' do + expect(chef_run).to change_password_rabbitmq_user( + 'change openstack rabbit user password' + ).with(user: 'not-a-guest', password: 'rabbit-pass') + end - describe "lwrps" do - it "deletes guest user" do - resource = @chef_run.find_resource( - "rabbitmq_user", - "remove rabbit guest user" - ).to_hash + it 'adds openstack rabbit vhost' do + expect(chef_run).to add_rabbitmq_vhost( + 'add openstack rabbit vhost' + ).with(vhost: '/foo') + end - expect(resource).to include( - :user => "guest", - :action => [:delete] - ) - end + it 'sets openstack user permissions' do + expect(chef_run).to set_permissions_rabbitmq_user( + 'set openstack user permissions' + ).with(user: 'not-a-guest', vhost: '/foo', permissions: '.* .* .*') + end - it "doesn't delete guest user" do - opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) - chef_run = ::ChefSpec::ChefRunner.new opts - chef_run.converge "openstack-ops-messaging::rabbitmq-server" - - resource = chef_run.find_resource( - "rabbitmq_user", - "remove rabbit guest user" - ) - - expect(resource).to be_nil - end - - it "adds user" do - resource = @chef_run.find_resource( - "rabbitmq_user", - "add openstack rabbit user" - ).to_hash - - expect(resource).to include( - :user => "guest", - :password => "rabbit-pass", - :action => [:add] - ) - end - - it "adds vhost" do - resource = @chef_run.find_resource( - "rabbitmq_vhost", - "add openstack rabbit vhost" - ).to_hash - - expect(resource).to include( - :vhost => "/", - :action => [:add] - ) - end - - it "sets user permissions" do - resource = @chef_run.find_resource( - "rabbitmq_user", - "set openstack user permissions" - ).to_hash - - expect(resource).to include( - :user => "guest", - :vhost => "/", - :permissions => '.* .* .*', - :action => [:set_permissions] - ) - end - - it "sets administrator tag" do - resource = @chef_run.find_resource( - "rabbitmq_user", - "set rabbit administrator tag" - ).to_hash - - expect(resource).to include( - :user => "guest", - :tag => "administrator", - :action => [:set_tags] - ) + it 'sets administrator tag' do + expect(chef_run).to set_tags_rabbitmq_user( + 'set rabbit administrator tag' + ).with(user: 'not-a-guest', tag: 'administrator') + end end end end diff --git a/chef/cookbooks/openstack-ops-messaging/spec/server_spec.rb b/chef/cookbooks/openstack-ops-messaging/spec/server_spec.rb index 139a7f4..268e3c0 100644 --- a/chef/cookbooks/openstack-ops-messaging/spec/server_spec.rb +++ b/chef/cookbooks/openstack-ops-messaging/spec/server_spec.rb @@ -1,14 +1,16 @@ +# encoding: UTF-8 require_relative 'spec_helper' -describe "openstack-ops-messaging::server" do - before { ops_messaging_stubs } - describe "ubuntu" do +describe 'openstack-ops-messaging::server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } - it "uses proper messaging server recipe" do - chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS - chef_run.converge "openstack-ops-messaging::server" + include_context 'ops_messaging_stubs' - expect(chef_run).to include_recipe "openstack-ops-messaging::rabbitmq-server" + it 'uses proper messaging server recipe' do + expect(chef_run).to include_recipe 'openstack-ops-messaging::rabbitmq-server' end end end diff --git a/chef/cookbooks/openstack-ops-messaging/spec/spec_helper.rb b/chef/cookbooks/openstack-ops-messaging/spec/spec_helper.rb index 5a94c19..fdd0389 100644 --- a/chef/cookbooks/openstack-ops-messaging/spec/spec_helper.rb +++ b/chef/cookbooks/openstack-ops-messaging/spec/spec_helper.rb @@ -1,30 +1,40 @@ -require "chefspec" +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' -::LOG_LEVEL = :fatal -::REDHAT_OPTS = { - :platform => "redhat", - :version => "6.3", - :log_level => ::LOG_LEVEL +ChefSpec::Coverage.start! { add_filter 'openstack-ops-messaging' } + +LOG_LEVEL = :fatal +REDHAT_OPTS = { + platform: 'redhat', + version: '6.5', + log_level: LOG_LEVEL } -::UBUNTU_OPTS = { - :platform => "ubuntu", - :version => "12.04", - :log_level => ::LOG_LEVEL +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: LOG_LEVEL } -def ops_messaging_stubs - ::Chef::Recipe.any_instance.stub(:address_for). - with("lo"). - and_return "127.0.0.1" - ::Chef::Recipe.any_instance.stub(:search). - with(:node, "roles:os-ops-messaging AND chef_environment:_default"). - and_return [ +shared_context 'ops_messaging_stubs' do + before do + Chef::Recipe.any_instance.stub(:address_for) + .with('lo') + .and_return '127.0.0.1' + Chef::Recipe.any_instance.stub(:address_for) + .with('eth0') + .and_return '33.44.55.66' + Chef::Recipe.any_instance.stub(:search) + .with(:node, 'roles:os-ops-messaging AND chef_environment:_default') + .and_return [ { 'hostname' => 'host2' }, { 'hostname' => 'host1' } ] - ::Chef::Recipe.any_instance.stub(:user_password). - and_return "rabbit-pass" - ::Chef::Recipe.any_instance.stub(:service_password). - with("rabbit_cookie"). - and_return "erlang-cookie" + Chef::Recipe.any_instance.stub(:get_password) + .with('user', anything) + .and_return 'rabbit-pass' + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'rabbit_cookie') + .and_return 'erlang-cookie' + end end diff --git a/chef/cookbooks/openstack-orchestration/.rubocop.yml b/chef/cookbooks/openstack-orchestration/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-metering/Berksfile b/chef/cookbooks/openstack-orchestration/Berksfile similarity index 98% rename from chef/cookbooks/openstack-metering/Berksfile rename to chef/cookbooks/openstack-orchestration/Berksfile index 6ee299f..e82f525 100644 --- a/chef/cookbooks/openstack-metering/Berksfile +++ b/chef/cookbooks/openstack-orchestration/Berksfile @@ -1,6 +1,6 @@ metadata -cookbook "openstack-common", - git: "git://github.com/stackforge/cookbook-openstack-common.git" cookbook "openstack-identity", git: "git://github.com/stackforge/cookbook-openstack-identity.git" +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" \ No newline at end of file diff --git a/chef/cookbooks/openstack-orchestration/CHANGELOG.md b/chef/cookbooks/openstack-orchestration/CHANGELOG.md new file mode 100644 index 0000000..5245833 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/CHANGELOG.md @@ -0,0 +1,36 @@ +# CHANGELOG for cookbook-openstack-orchestration + +This file is used to list changes made in each version of cookbook-openstack-orchestration +## 9.1.5 +* Fix to properly set signing_dir + +## 9.1.4 +* Fix ability to configure separate endpoint and bind addresses + +## 9.1.3 +* Fix package reference, need keystone client not keystone + +## 9.1.2 +* Fix endpoint ref in heat conf + +## 9.1.1 +* Revert bug 1279577 Create api-cfn identity registrations bug 1309123 + +## 9.1.0 +* Add notification attributes + +## 9.0.1 +* Remove policy file + +## 9.0.0 +* Upgrade to Icehouse + +## 8.1.1 +### Bug +* Fix the DB2 ODBC driver issue + +## 8.1.0 +* Add client recipe + +## 8.0.0: +* Initial release of cookbook-openstack-orchestration. diff --git a/chef/cookbooks/openstack-orchestration/Gemfile b/chef/cookbooks/openstack-orchestration/Gemfile new file mode 100644 index 0000000..2b8e634 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/Gemfile @@ -0,0 +1,10 @@ +# encoding: UTF-8 +source 'https://rubygems.org' + +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' diff --git a/chef/cookbooks/openstack-orchestration/Gemfile.lock b/chef/cookbooks/openstack-orchestration/Gemfile.lock new file mode 100644 index 0000000..2954927 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/Gemfile.lock @@ -0,0 +1,237 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.17) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.6) + akami (1.2.1) + gyoku (>= 0.4.0) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + chozo (>= 0.6.1) + faraday (~> 0.8.0) + faraday (~> 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.5.0) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) + buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.1) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + coderay (1.1.0) + diff-lcs (1.2.5) + erubis (2.7.0) + faraday (0.8.9) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) + net-ssh + ohai + ffi (1.9.3) + foodcritic (3.0.3) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rake + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.1.1) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) + httpi (0.9.7) + rack + i18n (0.6.9) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + logging (1.8.2) + little-plugger (>= 1.1.3) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) + minitar (0.5.4) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.4.0) + mixlib-config (2.1.0) + mixlib-log (1.6.0) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) + multipart-post (1.2.0) + net-http-persistent (2.9.4) + net-ssh (2.8.0) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) + nori (1.1.5) + ohai (6.20.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu (~> 2.5.2) + yajl-ruby + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) + rack (1.5.2) + rainbow (2.0.0) + rake (10.2.2) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.5) + ridley (1.5.3) + addressable + buff-config (~> 0.2) + buff-extensions (~> 0.3) + buff-ignore (~> 1.1) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + nio4r (>= 0.5.0) + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) + berkshelf (~> 2.0) + buff-platform (~> 0.1) + systemu (2.5.2) + thor (0.18.1) + timers (2.0.0) + hitimes + treetop (1.4.15) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.3.2) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.3) + gssapi (~> 1.0.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + foodcritic (~> 3.0.3) + json (<= 1.7.7) + rubocop (~> 0.18.1) + strainer diff --git a/chef/cookbooks/openstack-orchestration/README.md b/chef/cookbooks/openstack-orchestration/README.md new file mode 100644 index 0000000..f1ac400 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/README.md @@ -0,0 +1,154 @@ +Description +=========== + +This cookbook installs the OpenStack Heat service **Heat** as part of an OpenStack reference deployment Chef for OpenStack. + +http://heat.openstack.org/ + +Requirements +============ + +Chef 11 or higher required (for Chef environment use). + +Cookbooks +--------- + +The following cookbooks are dependencies: + +* openstack-common +* openstack-identity + +Usage +===== + +api +------ +- Configure and start heat-api service + +api-cfn +------ +- Configure and start heat-api-cfn service + +api-cloudwatch +------ +- Configure and start heat-api-cloudwatch service + +client +---- +- Install the heat client packages + +common +------ +- Installs the heat packages and setup configuration for Heat. + +engine +------ +- Setup the heat database and start heat-engine service + +identity_registration +--------------------- +- Registers the Heat API endpoint, heat service and user + +Attributes +========== + +Attributes for the Heat service are in the ['openstack']['orchestration'] namespace. + +* `openstack['orchestration']['verbose']` - Enables/disables verbose output for heat services. +* `openstack['orchestration']['debug']` - Enables/disables debug output for heat services. +* `openstack['orchestration']['identity_service_chef_role']` - The name of the Chef role that installs the Keystone Service API +* `openstack['orchestration']['rabbit_server_chef_role']` - The name of the Chef role that knows about the message queue server +* `openstack['orchestration']['user']` - User heat runs as +* `openstack['orchestration']['group']` - Group heat runs as +* `openstack['orchestration']['db']['username']` - Username for heat database access +* `openstack['orchestration']['api']['adminURL']` - Used when registering heat endpoint with keystone +* `openstack['orchestration']['api']['internalURL']` - Used when registering heat endpoint with keystone +* `openstack['orchestration']['api']['publicURL']` - Used when registering heat endpoint with keystone +* `openstack['orchestration']['service_tenant_name']` - Tenant name used by heat when interacting with keystone - used in the API and registry paste.ini files +* `openstack['orchestration']['service_user']` - User name used by heat when interacting with keystone - used in the API and registry paste.ini files +* `openstack['orchestration']['service_role']` - User role used by heat when interacting with keystone - used in the API and registry paste.ini files +* `openstack['orchestration']['api']['auth']['cache_dir']` - Defaults to `/var/cache/heat`. Directory where `auth_token` middleware writes certificates for heat +* `openstack['orchestration']['syslog']['use']` - Should heat log to syslog? +* `openstack['orchestration']['syslog']['facility']` - Which facility heat should use when logging in python style (for example, `LOG_LOCAL1`) +* `openstack['orchestration']['syslog']['config_facility']` - Which facility heat should use when logging in rsyslog style (for example, local1) +* `openstack['orchestration']['rpc_thread_pool_size']` - size of RPC thread pool +* `openstack['orchestration']['rpc_conn_pool_size']` - size of RPC connection pool +* `openstack['orchestration']['rpc_response_timeout']` - seconds to wait for a response from call or multicall +* `openstack['orchestration']['platform']` - hash of platform specific package/service names and options +* `openstack['orchestration']['api']['auth']['version']` - Select v2.0 or v3.0. Default v2.0. The auth API version used to interact with identity service. + +Notification definitions +------------------------ +* `openstack['orchestration']['notification_driver']` - driver +* `openstack['orchestration']['default_notification_level']` - level +* `openstack['orchestration']['default_publisher_id']` - publisher id +* `openstack['orchestration']['list_notifier_drivers']` - list of drivers +* `openstack['orchestration']['notification_topics']` - notifications topics + +MQ attributes +------------- +* `openstack["orchestration"]["mq"]["service_type"]` - Select qpid or rabbitmq. default rabbitmq +TODO: move rabbit parameters under openstack["orchestration"]["mq"] +* `openstack["orchestration"]["rabbit"]["username"]` - Username for nova rabbit access +* `openstack["orchestration"]["rabbit"]["vhost"]` - The rabbit vhost to use +* `openstack["orchestration"]["rabbit"]["port"]` - The rabbit port to use +* `openstack["orchestration"]["rabbit"]["host"]` - The rabbit host to use (must set when `openstack["orchestration"]["rabbit"]["ha"]` false). +* `openstack["orchestration"]["rabbit"]["ha"]` - Whether or not to use rabbit ha + +* `openstack["orchestration"]["mq"]["qpid"]["host"]` - The qpid host to use +* `openstack["orchestration"]["mq"]["qpid"]["port"]` - The qpid port to use +* `openstack["orchestration"]["mq"]["qpid"]["qpid_hosts"]` - Qpid hosts. TODO. use only when ha is specified. +* `openstack["orchestration"]["mq"]["qpid"]["username"]` - Username for qpid connection +* `openstack["orchestration"]["mq"]["qpid"]["password"]` - Password for qpid connection +* `openstack["orchestration"]["mq"]["qpid"]["sasl_mechanisms"]` - Space separated list of SASL mechanisms to use for auth +* `openstack["orchestration"]["mq"]["qpid"]["reconnect_timeout"]` - The number of seconds to wait before deciding that a reconnect attempt has failed. +* `openstack["orchestration"]["mq"]["qpid"]["reconnect_limit"]` - The limit for the number of times to reconnect before considering the connection to be failed. +* `openstack["orchestration"]["mq"]["qpid"]["reconnect_interval_min"]` - Minimum number of seconds between connection attempts. +* `openstack["orchestration"]["mq"]["qpid"]["reconnect_interval_max"]` - Maximum number of seconds between connection attempts. +* `openstack["orchestration"]["mq"]["qpid"]["reconnect_interval"]` - Equivalent to setting qpid_reconnect_interval_min and qpid_reconnect_interval_max to the same value. +* `openstack["orchestration"]["mq"]["qpid"]["heartbeat"]` - Seconds between heartbeat messages sent to ensure that the connection is still alive. +* `openstack["orchestration"]["mq"]["qpid"]["protocol"]` - Protocol to use. Default tcp. +* `openstack["orchestration"]["mq"]["qpid"]["tcp_nodelay"]` - Disable the Nagle algorithm. default disabled. + +The following attributes are defined in attributes/default.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack['endpoints']['orchestration-api-bind']['host']` - The IP address to bind the service to +* `openstack['endpoints']['orchestration-api-bind']['port']` - The port to bind the service to +* `openstack['endpoints']['orchestration-api-bind']['bind_interface']` - The interface name to bind the service to + +* `openstack['endpoints']['orchestration-api-cfn-bind']['host']` - The IP address to bind the service to +* `openstack['endpoints']['orchestration-api-cfn-bind']['port']` - The port to bind the service to +* `openstack['endpoints']['orchestration-api-cfn-bind']['bind_interface']` - The interface name to bind the-cfn service to + +* `openstack['endpoints']['orchestration-api-cloudwatch-bind']['host']` - The IP address to bind the service to +* `openstack['endpoints']['orchestration-api-cloudwatch-bind']['port']` - The port to bind the service to +* `openstack['endpoints']['orchestration-api-cloudwatch-bind']['bind_interface']` - The interface name to bind the-cloudwatch service to + +If the value of the 'bind_interface' attribute is non-nil, then the service will be bound to the first IP address on that interface. If the value of the 'bind_interface' attribute is nil, then the service will be bound to the IP address specifie> + +Testing +===== + +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | Zhao Fang Han () | +| **Author** | Chen Zhiwei () | +| | | +| **Copyright** | Copyright (c) 2013-2014, IBM Corp. | + +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. diff --git a/chef/cookbooks/openstack-orchestration/Strainerfile b/chef/cookbooks/openstack-orchestration/Strainerfile new file mode 100644 index 0000000..44e3e14 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-orchestration/TESTING.md b/chef/cookbooks/openstack-orchestration/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-orchestration/attributes/default.rb b/chef/cookbooks/openstack-orchestration/attributes/default.rb new file mode 100644 index 0000000..8c7fd47 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/attributes/default.rb @@ -0,0 +1,118 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-orchestration +# Attributes:: default +# +# Copyright 2013, IBM Corp. +# 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. +# + +# Set to some text value if you want templated config files +# to contain a custom banner at the top of the written file +default['openstack']['orchestration']['custom_template_banner'] = ' +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +' + +default['openstack']['orchestration']['verbose'] = 'False' +default['openstack']['orchestration']['debug'] = 'False' +# This is the name of the Chef role that will install the Keystone Service API +default['openstack']['orchestration']['identity_service_chef_role'] = 'os-identity' + +# Gets set in the Heat Endpoint when registering with Keystone +default['openstack']['orchestration']['region'] = node['openstack']['region'] + +# The name of the Chef role that knows about the message queue server +# that Heat uses +default['openstack']['orchestration']['rabbit_server_chef_role'] = 'os-ops-messaging' + +default['openstack']['orchestration']['service_tenant_name'] = 'service' +default['openstack']['orchestration']['service_user'] = 'heat' +default['openstack']['orchestration']['service_role'] = 'admin' + +default['openstack']['orchestration']['api']['auth']['version'] = node['openstack']['api']['auth']['version'] + +# If set, heat API service will bind to the address on this interface, +# otherwise it will bind to the API endpoint's host. +default['openstack']['orchestration']['api']['bind_interface'] = nil + +# If set, heat api-cfn service will bind to the address on this interface, +# otherwise it will bind to the API endpoint's host. +default['openstack']['orchestration']['api-cfn']['bind_interface'] = nil + +# If set, heat api-cloudwatch service will bind to the address on this interface, +# otherwise it will bind to the API endpoint's host. +default['openstack']['orchestration']['api-cloudwatch']['bind_interface'] = nil + +# Keystone PKI signing directory. Only written to the filter:authtoken section +# of the api-paste.ini when node['openstack']['auth']['strategy'] == 'pki' +default['openstack']['orchestration']['api']['auth']['cache_dir'] = '/var/cache/heat' + +# logging attribute +default['openstack']['orchestration']['syslog']['use'] = false +default['openstack']['orchestration']['syslog']['facility'] = 'LOG_LOCAL2' +default['openstack']['orchestration']['syslog']['config_facility'] = 'local2' + +# Common rpc definitions +default['openstack']['orchestration']['rpc_thread_pool_size'] = 240 +default['openstack']['orchestration']['rpc_conn_pool_size'] = 100 +default['openstack']['orchestration']['rpc_response_timeout'] = 60 + +# Notification definitions +default['openstack']['orchestration']['notification_driver'] = 'heat.openstack.common.notifier.rpc_notifier' +default['openstack']['orchestration']['default_notification_level'] = 'INFO' +default['openstack']['orchestration']['default_publisher_id'] = '' +default['openstack']['orchestration']['list_notifier_drivers'] = 'heat.openstack.common.notifier.no_op_notifier' +default['openstack']['orchestration']['notification_topics'] = 'notifications' + +# platform-specific settings +case platform_family +when 'fedora', 'rhel' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['orchestration']['user'] = 'heat' + default['openstack']['orchestration']['group'] = 'heat' + default['openstack']['orchestration']['platform'] = { + 'mysql_python_packages' => ['MySQL-python'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'db2_python_packages' => ['python-ibm-db', 'python-ibm-db-sa'], + 'heat_common_packages' => ['openstack-heat'], + 'heat_client_packages' => ['python-heatclient'], + 'heat_api_packages' => ['python-heatclient'], + 'heat_api_service' => 'openstack-heat-api', + 'heat_api_cfn_packages' => ['python-heatclient'], + 'heat_api_cfn_service' => 'openstack-heat-api-cfn', + 'heat_api_cloudwatch_packages' => ['python-heatclient'], + 'heat_api_cloudwatch_service' => 'openstack-heat-api-cloudwatch', + 'heat_engine_packages' => [], + 'heat_engine_service' => 'openstack-heat-engine', + 'heat_api_process_name' => 'heat-api', + 'package_overrides' => '' + } +when 'debian' + default['openstack']['orchestration']['user'] = 'heat' + default['openstack']['orchestration']['group'] = 'heat' + default['openstack']['orchestration']['platform'] = { + 'mysql_python_packages' => ['python-mysqldb'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'heat_common_packages' => ['heat-common'], + 'heat_client_packages' => ['python-heatclient'], + 'heat_api_packages' => ['heat-api', 'python-heatclient'], + 'heat_api_service' => 'heat-api', + 'heat_api_cfn_packages' => ['heat-api-cfn', 'python-heatclient'], + 'heat_api_cfn_service' => 'heat-api-cfn', + 'heat_api_cloudwatch_packages' => ['heat-api-cloudwatch', 'python-heatclient'], + 'heat_api_cloudwatch_service' => 'heat-api-cloudwatch', + 'heat_engine_packages' => ['heat-engine'], + 'heat_engine_service' => 'heat-engine', + 'package_overrides' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } +end diff --git a/chef/cookbooks/openstack-orchestration/metadata.rb b/chef/cookbooks/openstack-orchestration/metadata.rb new file mode 100644 index 0000000..2f943b0 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/metadata.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 +name 'openstack-orchestration' +maintainer 'IBM, Inc.' +license 'Apache 2.0' +description 'Installs and configures the Heat Service' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '9.1.5' +recipe 'openstack-orchestration::api', 'Start and configure the Heat API service' +recipe 'openstack-orchestration::api-cfn', 'Start and configure the Heat API CloudFormation service' +recipe 'openstack-orchestration::api-cloudwatch', 'Start and configure the Heat API CloudWatch service' +recipe 'openstack-orchestration::client', 'Installs packages for heat client' +recipe 'openstack-orchestration::common', 'Installs packages and configures a Heat Server' +recipe 'openstack-orchestration::engine', 'Sets up Heat database and starts Heat Engine service' +recipe 'openstack-orchestration::identity_registration', 'Registers Heat service, user and endpoints with Keystone' + +%w{ ubuntu fedora redhat centos }.each do |os| + supports os +end + +depends 'openstack-common', '~> 9.0' +depends 'openstack-identity', '~> 9.0' diff --git a/chef/cookbooks/openstack-orchestration/recipes/api-cfn.rb b/chef/cookbooks/openstack-orchestration/recipes/api-cfn.rb new file mode 100644 index 0000000..bfa27e9 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/recipes/api-cfn.rb @@ -0,0 +1,47 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-orchestration +# Recipe:: api-cfn +# +# Copyright 2013, IBM Corp. +# +# 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. +# + +include_recipe 'openstack-orchestration::common' + +platform_options = node['openstack']['orchestration']['platform'] + +platform_options['heat_api_cfn_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end + +service 'heat-api-cfn' do + service_name platform_options['heat_api_cfn_service'] + supports status: true, restart: true + + action :enable + subscribes :restart, 'template[/etc/heat/heat.conf]' +end + +template '/etc/heat/api-paste.ini' do + source 'api-paste.ini.erb' + group node['openstack']['orchestration']['group'] + owner node['openstack']['orchestration']['user'] + mode 00644 + notifies :restart, 'service[heat-api-cfn]', :immediately +end diff --git a/chef/cookbooks/openstack-orchestration/recipes/api-cloudwatch.rb b/chef/cookbooks/openstack-orchestration/recipes/api-cloudwatch.rb new file mode 100644 index 0000000..1b74bab --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/recipes/api-cloudwatch.rb @@ -0,0 +1,51 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-orchestration +# Recipe:: api-cloudwatch +# +# +# Cookbook Name:: openstack-orchestration +# Recipe:: api-cloudwatch +# +# Copyright 2013, IBM Corp. +# +# 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. +# + +include_recipe 'openstack-orchestration::common' + +platform_options = node['openstack']['orchestration']['platform'] + +platform_options['heat_api_cloudwatch_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end + +service 'heat-api-cloudwatch' do + service_name platform_options['heat_api_cloudwatch_service'] + supports status: true, restart: true + + action :enable + subscribes :restart, 'template[/etc/heat/heat.conf]' +end + +template '/etc/heat/api-paste.ini' do + source 'api-paste.ini.erb' + group node['openstack']['orchestration']['group'] + owner node['openstack']['orchestration']['user'] + mode 00644 + notifies :restart, 'service[heat-api-cloudwatch]', :immediately +end diff --git a/chef/cookbooks/openstack-orchestration/recipes/api.rb b/chef/cookbooks/openstack-orchestration/recipes/api.rb new file mode 100644 index 0000000..28fbadc --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/recipes/api.rb @@ -0,0 +1,47 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-orchestration +# Recipe:: api +# +# Copyright 2013, IBM Corp. +# +# 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. +# + +include_recipe 'openstack-orchestration::common' + +platform_options = node['openstack']['orchestration']['platform'] + +platform_options['heat_api_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end + +service 'heat-api' do + service_name platform_options['heat_api_service'] + supports status: true, restart: true + + action :enable + subscribes :restart, 'template[/etc/heat/heat.conf]' +end + +template '/etc/heat/api-paste.ini' do + source 'api-paste.ini.erb' + group node['openstack']['orchestration']['group'] + owner node['openstack']['orchestration']['user'] + mode 00644 + notifies :restart, 'service[heat-api]', :immediately +end diff --git a/chef/cookbooks/openstack-orchestration/recipes/client.rb b/chef/cookbooks/openstack-orchestration/recipes/client.rb new file mode 100644 index 0000000..a6c86dd --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/recipes/client.rb @@ -0,0 +1,32 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-orchestration +# Recipe:: client +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +platform_options = node['openstack']['orchestration']['platform'] +platform_options['heat_client_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end diff --git a/chef/cookbooks/openstack-orchestration/recipes/common.rb b/chef/cookbooks/openstack-orchestration/recipes/common.rb new file mode 100644 index 0000000..c1a9be9 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/recipes/common.rb @@ -0,0 +1,133 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-orchestration +# Recipe:: engine +# +# Copyright 2013, IBM Corp. +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +if node['openstack']['orchestration']['syslog']['use'] + include_recipe 'openstack-common::logging' +end + +platform_options = node['openstack']['orchestration']['platform'] + +package 'python-keystoneclient' do + options platform_options['package_overrides'] + action :upgrade +end + +platform_options['heat_common_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end + +db_type = node['openstack']['db']['orchestration']['service_type'] +platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + action :upgrade + end +end + +db_user = node['openstack']['db']['orchestration']['username'] +db_pass = get_password 'db', 'heat' +sql_connection = db_uri('orchestration', db_user, db_pass) + +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' +heat_api_bind = endpoint 'orchestration-api-bind' +heat_api_endpoint = endpoint 'orchestration-api' +heat_api_cfn_bind = endpoint 'orchestration-api-cfn-bind' +heat_api_cfn_endpoint = endpoint 'orchestration-api-cfn' +heat_api_cloudwatch_bind = endpoint 'orchestration-api-cloudwatch-bind' +heat_api_cloudwatch_endpoint = endpoint 'orchestration-api-cloudwatch' + +service_pass = get_password 'service', 'openstack-orchestration' + +auth_uri = auth_uri_transform identity_endpoint.to_s, node['openstack']['orchestration']['api']['auth']['version'] + +mq_service_type = node['openstack']['mq']['orchestration']['service_type'] + +if mq_service_type == 'rabbitmq' + if node['openstack']['mq']['orchestration']['rabbit']['ha'] + rabbit_hosts = rabbit_servers + end + mq_password = get_password 'user', node['openstack']['mq']['orchestration']['rabbit']['userid'] +elsif mq_service_type == 'qpid' + mq_password = get_password 'user', node['openstack']['mq']['orchestration']['qpid']['username'] +end + +directory '/etc/heat' do + group node['openstack']['orchestration']['group'] + owner node['openstack']['orchestration']['user'] + mode 00700 + action :create +end + +directory '/etc/heat/environment.d' do + group node['openstack']['orchestration']['group'] + owner node['openstack']['orchestration']['user'] + mode 00700 + action :create +end + +directory node['openstack']['orchestration']['api']['auth']['cache_dir'] do + owner node['openstack']['orchestration']['user'] + group node['openstack']['orchestration']['group'] + mode 00700 +end + +template '/etc/heat/heat.conf' do + source 'heat.conf.erb' + group node['openstack']['orchestration']['group'] + owner node['openstack']['orchestration']['user'] + mode 00644 + variables( + mq_service_type: mq_service_type, + mq_password: mq_password, + rabbit_hosts: rabbit_hosts, + auth_uri: auth_uri, + identity_admin_endpoint: identity_admin_endpoint, + service_pass: service_pass, + sql_connection: sql_connection, + heat_api_bind: heat_api_bind, + heat_api_endpoint: heat_api_endpoint, + heat_api_cfn_bind: heat_api_cfn_bind, + heat_api_cfn_endpoint: heat_api_cfn_endpoint, + heat_api_cloudwatch_bind: heat_api_cloudwatch_bind, + heat_api_cloudwatch_endpoint: heat_api_cloudwatch_endpoint + ) +end + +template '/etc/heat/environment.d/default.yaml' do + source 'default.yaml.erb' + group node['openstack']['orchestration']['group'] + owner node['openstack']['orchestration']['user'] + mode 00644 +end + +execute 'heat-manage db_sync' do + user node['openstack']['orchestration']['user'] + group node['openstack']['orchestration']['group'] + command 'heat-manage db_sync' + action :run +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/default.rb b/chef/cookbooks/openstack-orchestration/recipes/default.rb similarity index 86% rename from chef/cookbooks/apt/test/cookbooks/apt_test/recipes/default.rb rename to chef/cookbooks/openstack-orchestration/recipes/default.rb index ce4d3f9..d60e991 100644 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/default.rb +++ b/chef/cookbooks/openstack-orchestration/recipes/default.rb @@ -1,8 +1,9 @@ +# encoding: UTF-8 # -# Cookbook Name:: apt_test +# Cookbook Name:: openstack-orchestration # Recipe:: default # -# Copyright 2012, Opscode, Inc. +# Copyright 2013, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,5 +17,3 @@ # See the License for the specific language governing permissions and # limitations under the License. # - -include_recipe "apt::default" diff --git a/chef/cookbooks/openstack-orchestration/recipes/engine.rb b/chef/cookbooks/openstack-orchestration/recipes/engine.rb new file mode 100644 index 0000000..bd9946c --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/recipes/engine.rb @@ -0,0 +1,38 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-orchestration +# Recipe:: engine +# +# Copyright 2013, IBM Corp. +# +# 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. +# + +include_recipe 'openstack-orchestration::common' + +platform_options = node['openstack']['orchestration']['platform'] + +platform_options['heat_engine_packages'].each do |pkg| + package pkg do + options platform_options['package_overrides'] + + action :upgrade + end +end + +service 'heat_engine' do + service_name platform_options['heat_engine_service'] + supports status: true, restart: true + action :enable + subscribes :restart, 'template[/etc/heat/heat.conf]' +end diff --git a/chef/cookbooks/openstack-orchestration/recipes/identity_registration.rb b/chef/cookbooks/openstack-orchestration/recipes/identity_registration.rb new file mode 100644 index 0000000..cfb703b --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/recipes/identity_registration.rb @@ -0,0 +1,130 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-orchestration +# Recipe:: identity_registration +# +# Copyright 2013, IBM Corp. +# +# 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. +# + +require 'uri' + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +identity_admin_endpoint = endpoint 'identity-admin' + +token = get_secret 'openstack_identity_bootstrap_token' +auth_url = ::URI.decode identity_admin_endpoint.to_s + +heat_endpoint = endpoint 'orchestration-api' +heat_cfn_endpoint = endpoint 'orchestration-api-cfn' + +service_pass = get_password 'service', 'openstack-orchestration' +service_tenant_name = node['openstack']['orchestration']['service_tenant_name'] +service_user = node['openstack']['orchestration']['service_user'] +service_role = node['openstack']['orchestration']['service_role'] +region = node['openstack']['orchestration']['region'] + +# Do not configure a service/endpoint in keystone for heat-api-cloudwatch(Bug #1167927), +# See discussions on https://bugs.launchpad.net/heat/+bug/1167927 + +# Register Heat API Service +openstack_identity_register 'Register Heat Orchestration Service' do + auth_uri auth_url + bootstrap_token token + service_name 'heat' + service_type 'orchestration' + service_description 'Heat Orchestration Service' + + action :create_service +end + +# Register Heat API Endpoint +openstack_identity_register 'Register Heat Orchestration Endpoint' do + auth_uri auth_url + bootstrap_token token + service_type 'orchestration' + endpoint_region region + endpoint_adminurl heat_endpoint.to_s + endpoint_internalurl heat_endpoint.to_s + endpoint_publicurl heat_endpoint.to_s + + action :create_endpoint +end + +# TODO: (MRV) Revert this change until a better solution can be found +# Bug: #1309123 reverts 1279577 +# if node.run_list.include?('openstack-orchestration::api-cfn') + +# Register Heat API Cloudformation Service +openstack_identity_register 'Register Heat Cloudformation Service' do + auth_uri auth_url + bootstrap_token token + service_name 'heat-cfn' + service_type 'cloudformation' + service_description 'Heat Cloudformation Service' + + action :create_service +end + +# Register Heat API CloudFormation Endpoint +openstack_identity_register 'Register Heat Cloudformation Endpoint' do + auth_uri auth_url + bootstrap_token token + service_type 'cloudformation' + endpoint_region region + endpoint_adminurl heat_cfn_endpoint.to_s + endpoint_internalurl heat_cfn_endpoint.to_s + endpoint_publicurl heat_cfn_endpoint.to_s + + action :create_endpoint +end +# end + +# Register Service Tenant +openstack_identity_register 'Register Service Tenant' do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + tenant_description 'Service Tenant' + tenant_enabled true # Not required as this is the default + + action :create_tenant +end + +# Register Service User +openstack_identity_register 'Register Heat Service User' do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + user_name service_user + user_pass service_pass + # String until https://review.openstack.org/#/c/29498/ merged + user_enabled true + + action :create_user +end + +## Grant Admin role to Service User for Service Tenant ## +openstack_identity_register "Grant '#{service_role}' Role to #{service_user} User for #{service_tenant_name} Tenant" do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + user_name service_user + role_name service_role + + action :grant_role +end diff --git a/chef/cookbooks/openstack-orchestration/spec/api-cfn-redhat_spec.rb b/chef/cookbooks/openstack-orchestration/spec/api-cfn-redhat_spec.rb new file mode 100644 index 0000000..dc97ccf --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/api-cfn-redhat_spec.rb @@ -0,0 +1,28 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::api-cfn' do + before { orchestration_stubs } + describe 'redhat' do + before do + @chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS do |n| + n.set['openstack']['orchestration']['syslog']['use'] = true + end + @chef_run.converge 'openstack-orchestration::api-cfn' + end + + expect_runs_openstack_orchestration_common_recipe + expect_runs_openstack_common_logging_recipe + expect_installs_python_keystoneclient + + it 'installs heat client packages' do + expect(@chef_run).to upgrade_package 'python-heatclient' + end + + expect_creates_api_paste 'service[heat-api-cfn]' + + it 'starts heat api-cfn on boot' do + expect(@chef_run).to enable_service('openstack-heat-api-cfn') + end + end +end diff --git a/chef/cookbooks/openstack-orchestration/spec/api-cloudwatch-redhat_spec.rb b/chef/cookbooks/openstack-orchestration/spec/api-cloudwatch-redhat_spec.rb new file mode 100644 index 0000000..75e51ff --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/api-cloudwatch-redhat_spec.rb @@ -0,0 +1,28 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::api-cloudwatch' do + before { orchestration_stubs } + describe 'redhat' do + before do + @chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS do |n| + n.set['openstack']['orchestration']['syslog']['use'] = true + end + @chef_run.converge 'openstack-orchestration::api-cloudwatch' + end + + expect_runs_openstack_orchestration_common_recipe + expect_runs_openstack_common_logging_recipe + expect_installs_python_keystoneclient + + it 'installs heat client packages' do + expect(@chef_run).to upgrade_package 'python-heatclient' + end + + expect_creates_api_paste 'service[heat-api-cloudwatch]' + + it 'starts heat api-cloudwatch on boot' do + expect(@chef_run).to enable_service('openstack-heat-api-cloudwatch') + end + end +end diff --git a/chef/cookbooks/openstack-orchestration/spec/api-redhat_spec.rb b/chef/cookbooks/openstack-orchestration/spec/api-redhat_spec.rb new file mode 100644 index 0000000..fc7d452 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/api-redhat_spec.rb @@ -0,0 +1,29 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::api' do + before { orchestration_stubs } + describe 'redhat' do + before do + @chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS + @chef_run.converge 'openstack-orchestration::api' + end + + expect_runs_openstack_orchestration_common_recipe + expect_installs_python_keystoneclient + + it 'does not run logging recipe' do + expect(@chef_run).not_to include_recipe 'openstack-common::logging' + end + + it 'installs heat client packages' do + expect(@chef_run).to upgrade_package 'python-heatclient' + end + + expect_creates_api_paste 'service[heat-api]' + + it 'starts heat api on boot' do + expect(@chef_run).to enable_service('openstack-heat-api') + end + end +end diff --git a/chef/cookbooks/openstack-orchestration/spec/client-redhat_spec.rb b/chef/cookbooks/openstack-orchestration/spec/client-redhat_spec.rb new file mode 100644 index 0000000..93fe8f6 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/client-redhat_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::client' do + + describe 'redhat' do + + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'installs packages' do + expect(chef_run).to upgrade_package('python-heatclient') + end + end +end diff --git a/chef/cookbooks/openstack-orchestration/spec/client_spec.rb b/chef/cookbooks/openstack-orchestration/spec/client_spec.rb new file mode 100644 index 0000000..a49190e --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/client_spec.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::client' do + + describe 'ubuntu' do + + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + it 'installs packages' do + expect(chef_run).to upgrade_package('python-heatclient') + end + end +end diff --git a/chef/cookbooks/openstack-orchestration/spec/common-redhat_spec.rb b/chef/cookbooks/openstack-orchestration/spec/common-redhat_spec.rb new file mode 100644 index 0000000..c1aad3e --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/common-redhat_spec.rb @@ -0,0 +1,126 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::common' do + before { orchestration_stubs } + before do + @chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS + @chef_run.converge 'openstack-orchestration::common' + end + + expect_installs_python_keystoneclient + + it 'installs the openstack-heat package' do + expect(@chef_run).to upgrade_package 'openstack-heat' + end + + it 'installs mysql python packages by default' do + expect(@chef_run).to upgrade_package 'MySQL-python' + end + + it 'installs postgresql python packages if explicitly told' do + chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS + node = chef_run.node + node.set['openstack']['db']['orchestration']['service_type'] = 'postgresql' + chef_run.converge 'openstack-orchestration::common' + + expect(chef_run).to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'MySQL-python' + expect(chef_run).not_to upgrade_package 'python-ibm-db' + expect(chef_run).not_to upgrade_package 'python-ibm-db-sa' + end + + it 'installs db2 python packages if explicitly told' do + chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS + node = chef_run.node + node.set['openstack']['db']['orchestration']['service_type'] = 'db2' + chef_run.converge 'openstack-orchestration::common' + expect(chef_run).to upgrade_package 'python-ibm-db' + expect(chef_run).to upgrade_package 'python-ibm-db-sa' + expect(chef_run).not_to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'MySQL-python' + end + + describe '/etc/heat' do + before do + @dir = @chef_run.directory '/etc/heat' + end + + it 'has proper owner' do + expect(@dir.owner).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @dir.mode)).to eq '700' + end + + end + + describe '/etc/heat/environment.d' do + before do + @dir = @chef_run.directory '/etc/heat/environment.d' + end + + it 'has proper owner' do + expect(@dir.owner).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @dir.mode)).to eq '700' + end + + end + + describe '/var/cache/heat' do + before do + @dir = @chef_run.directory '/var/cache/heat' + end + + it 'has proper owner' do + expect(@dir.owner).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @dir.mode)).to eq '700' + end + end + + describe 'heat.conf' do + before do + @template = @chef_run.template '/etc/heat/heat.conf' + end + it 'has proper owner' do + expect(@template.owner).to eq('heat') + expect(@template.group).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @template.mode)).to eq '644' + end + + # Pending on https://review.openstack.org/#/c/59088/ + it 'template contents' do + pending 'TODO: implement' + end + end + + describe 'default.yaml' do + before do + @template = @chef_run.template '/etc/heat/environment.d/default.yaml' + end + + it 'has proper owner' do + expect(@template.owner).to eq('heat') + expect(@template.group).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @template.mode)).to eq '644' + end + end + + it 'runs db migrations' do + cmd = 'heat-manage db_sync' + expect(@chef_run).to run_execute(cmd).with(user: 'heat', group: 'heat') + end +end diff --git a/chef/cookbooks/openstack-orchestration/spec/common_spec.rb b/chef/cookbooks/openstack-orchestration/spec/common_spec.rb new file mode 100755 index 0000000..106110b --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/common_spec.rb @@ -0,0 +1,122 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::common' do + before { orchestration_stubs } + before do + @chef_run = ::ChefSpec::Runner.new ::UBUNTU_OPTS + @chef_run.converge 'openstack-orchestration::common' + end + + expect_installs_python_keystoneclient + + it 'installs the openstack-heat package' do + expect(@chef_run).to upgrade_package 'heat-common' + end + + it 'installs mysql python packages by default' do + expect(@chef_run).to upgrade_package 'python-mysqldb' + end + + it 'installs postgresql python packages if explicitly told' do + chef_run = ::ChefSpec::Runner.new ::UBUNTU_OPTS + node = chef_run.node + node.set['openstack']['db']['orchestration']['service_type'] = 'postgresql' + chef_run.converge 'openstack-orchestration::common' + + expect(chef_run).to upgrade_package 'python-psycopg2' + expect(chef_run).not_to upgrade_package 'MySQL-python' + expect(chef_run).not_to upgrade_package 'python-ibm-db' + expect(chef_run).not_to upgrade_package 'python-ibm-db-sa' + end + + describe '/etc/heat' do + before do + @dir = @chef_run.directory '/etc/heat' + end + + it 'has proper owner' do + expect(@dir.owner).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @dir.mode)).to eq '700' + end + + end + + describe '/etc/heat/environment.d' do + before do + @dir = @chef_run.directory '/etc/heat/environment.d' + end + + it 'has proper owner' do + expect(@dir.owner).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @dir.mode)).to eq '700' + end + + end + + describe '/var/cache/heat' do + before do + @dir = @chef_run.directory '/var/cache/heat' + end + + it 'has proper owner' do + expect(@dir.owner).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @dir.mode)).to eq '700' + end + end + + describe 'heat.conf' do + before do + @template = @chef_run.template '/etc/heat/heat.conf' + end + it 'has proper owner' do + expect(@template.owner).to eq('heat') + expect(@template.group).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @template.mode)).to eq '644' + end + + # TODO: (MRV) Add rest of conf items + [ + %r{^heat_metadata_server_url=http://127.0.0.1:8000$}, + %r{^heat_waitcondition_server_url=http://127.0.0.1:8000/v1/waitcondition$}, + %r{^heat_watch_server_url=http://127.0.0.1:8003$}, + %r{^signing_dir=/var/cache/heat$} + ].each do |content| + it "has a #{content.source[1...-1]} line" do + expect(@chef_run).to render_file(@template.name).with_content(content) + end + end + end + + describe 'default.yaml' do + before do + @template = @chef_run.template '/etc/heat/environment.d/default.yaml' + end + + it 'has proper owner' do + expect(@template.owner).to eq('heat') + expect(@template.group).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @template.mode)).to eq '644' + end + end + + it 'runs db migrations' do + cmd = 'heat-manage db_sync' + expect(@chef_run).to run_execute(cmd).with(user: 'heat', group: 'heat') + end +end diff --git a/chef/cookbooks/openstack-orchestration/spec/engine-redhat_spec.rb b/chef/cookbooks/openstack-orchestration/spec/engine-redhat_spec.rb new file mode 100644 index 0000000..3d66ac6 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/engine-redhat_spec.rb @@ -0,0 +1,23 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::engine' do + before { orchestration_stubs } + describe 'redhat' do + before do + @chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS + @chef_run.converge 'openstack-orchestration::engine' + end + + expect_runs_openstack_orchestration_common_recipe + expect_installs_python_keystoneclient + + it 'does not run logging recipe' do + expect(@chef_run).not_to include_recipe 'openstack-common::logging' + end + + it 'starts heat engine on boot' do + expect(@chef_run).to enable_service('openstack-heat-engine') + end + end +end diff --git a/chef/cookbooks/openstack-orchestration/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-orchestration/spec/identity_registration_spec.rb new file mode 100644 index 0000000..48c0114 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/identity_registration_spec.rb @@ -0,0 +1,124 @@ +# encoding: UTF-8 +require_relative 'spec_helper' + +describe 'openstack-orchestration::identity_registration' do + before do + orchestration_stubs + @chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS + @chef_run.converge 'openstack-orchestration::identity_registration' + end + + it 'Register Heat Orchestration Service' do + resource = @chef_run.find_resource( + 'openstack-identity_register', + 'Register Heat Orchestration Service' + ).to_hash + + expect(resource).to include( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_name: 'heat', + service_type: 'orchestration', + service_description: 'Heat Orchestration Service', + action: [:create_service] + ) + end + + it 'Register Heat Orchestration Endpoint' do + resource = @chef_run.find_resource( + 'openstack-identity_register', + 'Register Heat Orchestration Endpoint' + ).to_hash + + expect(resource).to include( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'orchestration', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:8004/v1/%(tenant_id)s', + endpoint_internalurl: 'http://127.0.0.1:8004/v1/%(tenant_id)s', + endpoint_publicurl: 'http://127.0.0.1:8004/v1/%(tenant_id)s', + action: [:create_endpoint] + ) + end + + describe 'openstack-orchestration::identity_registration-cfn' do + before do + orchestration_stubs + @chef_run = ::ChefSpec::Runner.new ::REDHAT_OPTS + @chef_run.converge 'openstack-orchestration::identity_registration' +# TODO: (MRV) Revert this change until a better solution can be found +# Bug: #1309123 reverts 1279577 +# 'openstack-orchestration::api-cfn' + end + + it 'Register Heat Cloudformation Service' do + resource = @chef_run.find_resource( + 'openstack-identity_register', + 'Register Heat Cloudformation Service' + ).to_hash + + expect(resource).to include( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_name: 'heat-cfn', + service_type: 'cloudformation', + service_description: 'Heat Cloudformation Service', + action: [:create_service] + ) + end + + # Pending on https://review.openstack.org/#/c/59088/ + it 'Register Heat Cloudformation Endpoint' do + resource = @chef_run.find_resource( + 'openstack-identity_register', + 'Register Heat Cloudformation Endpoint' + ).to_hash + + expect(resource).to include( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'cloudformation', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:8000/v1', + endpoint_internalurl: 'http://127.0.0.1:8000/v1', + endpoint_publicurl: 'http://127.0.0.1:8000/v1', + action: [:create_endpoint] + ) + end + end + + it 'registers service user' do + resource = @chef_run.find_resource( + 'openstack-identity_register', + 'Register Heat Service User' + ).to_hash + + expect(resource).to include( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'heat', + user_pass: 'heat-pass', + user_enabled: true, + action: [:create_user] + ) + end + + it 'grants admin role to service user for service tenant' do + resource = @chef_run.find_resource( + 'openstack-identity_register', + "Grant 'admin' Role to heat User for service Tenant" + ).to_hash + + expect(resource).to include( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'heat', + role_name: 'admin', + action: [:grant_role] + ) + end + +end diff --git a/chef/cookbooks/openstack-orchestration/spec/spec_helper.rb b/chef/cookbooks/openstack-orchestration/spec/spec_helper.rb new file mode 100644 index 0000000..af85515 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/spec/spec_helper.rb @@ -0,0 +1,88 @@ +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' + +ChefSpec::Coverage.start! { add_filter 'openstack-orchestration' } + +require 'chef/application' + +::LOG_LEVEL = :fatal +::REDHAT_OPTS = { + platform: 'redhat', + version: '6.3', + log_level: ::LOG_LEVEL +} +::UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: ::LOG_LEVEL +} + +# TDODO(chrislaco) factor these into shared_context/examples +def orchestration_stubs # rubocop:disable MethodLength + ::Chef::Recipe.any_instance.stub(:rabbit_servers) + .and_return '1.1.1.1:5672,2.2.2.2:5672' + ::Chef::Recipe.any_instance.stub(:address_for) + .with('lo') + .and_return '127.0.1.1' + ::Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_identity_bootstrap_token') + .and_return 'bootstrap-token' + + ::Chef::Recipe.any_instance.stub(:get_password) + .with('db', 'heat') + .and_return 'heat' + ::Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'guest') + .and_return 'mq-pass' + ::Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'admin-user') + .and_return 'admin-pass' + ::Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack-orchestration') + .and_return 'heat-pass' + ::Chef::Application.stub(:fatal!) +end + +def expect_runs_openstack_orchestration_common_recipe + it 'runs orchestration common recipe' do + expect(@chef_run).to include_recipe 'openstack-orchestration::common' + end +end + +def expect_installs_python_keystoneclient + it 'installs python-keystoneclient' do + expect(@chef_run).to upgrade_package 'python-keystoneclient' + end +end + +def expect_runs_openstack_common_logging_recipe + it 'runs logging recipe if node attributes say to' do + expect(@chef_run).to include_recipe 'openstack-common::logging' + end +end + +def expect_creates_api_paste(service, action = :restart) # rubocop:disable MethodLength + describe 'api-paste.ini' do + before do + @template = @chef_run.template '/etc/heat/api-paste.ini' + end + + it 'has proper owner' do + expect(@template.owner).to eq('heat') + expect(@template.group).to eq('heat') + end + + it 'has proper modes' do + expect(sprintf('%o', @template.mode)).to eq '644' + end + + it 'template contents' do + pending 'TODO: implement' + end + + it 'notifies heat-api restart' do + expect(@template).to notify(service).to(action) + end + end +end diff --git a/chef/cookbooks/openstack-orchestration/templates/default/api-paste.ini.erb b/chef/cookbooks/openstack-orchestration/templates/default/api-paste.ini.erb new file mode 100644 index 0000000..c7dcd8e --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/templates/default/api-paste.ini.erb @@ -0,0 +1,96 @@ +<%= node["openstack"]["orchestration"]["custom_template_banner"] %> + +# heat-api pipeline +[pipeline:heat-api] +pipeline = faultwrap ssl versionnegotiation authurl authtoken context apiv1app + +# heat-api pipeline for standalone heat +# ie. uses alternative auth backend that authenticates users against keystone +# using username and password instead of validating token (which requires +# an admin/service token). +# To enable, in heat.conf: +# [paste_deploy] +# flavor = standalone +# +[pipeline:heat-api-standalone] +pipeline = faultwrap ssl versionnegotiation authurl authpassword context apiv1app + +# heat-api pipeline for custom cloud backends +# i.e. in heat.conf: +# [paste_deploy] +# flavor = custombackend +# +[pipeline:heat-api-custombackend] +pipeline = faultwrap versionnegotiation context custombackendauth apiv1app + +# heat-api-cfn pipeline +[pipeline:heat-api-cfn] +pipeline = cfnversionnegotiation ec2authtoken authtoken context apicfnv1app + +# heat-api-cfn pipeline for standalone heat +# relies exclusively on authenticating with ec2 signed requests +[pipeline:heat-api-cfn-standalone] +pipeline = cfnversionnegotiation ec2authtoken context apicfnv1app + +# heat-api-cloudwatch pipeline +[pipeline:heat-api-cloudwatch] +pipeline = versionnegotiation ec2authtoken authtoken context apicwapp + +# heat-api-cloudwatch pipeline for standalone heat +# relies exclusively on authenticating with ec2 signed requests +[pipeline:heat-api-cloudwatch-standalone] +pipeline = versionnegotiation ec2authtoken context apicwapp + +[app:apiv1app] +paste.app_factory = heat.common.wsgi:app_factory +heat.app_factory = heat.api.openstack.v1:API + +[app:apicfnv1app] +paste.app_factory = heat.common.wsgi:app_factory +heat.app_factory = heat.api.cfn.v1:API + +[app:apicwapp] +paste.app_factory = heat.common.wsgi:app_factory +heat.app_factory = heat.api.cloudwatch:API + +[filter:versionnegotiation] +paste.filter_factory = heat.common.wsgi:filter_factory +heat.filter_factory = heat.api.openstack:version_negotiation_filter + +[filter:faultwrap] +paste.filter_factory = heat.common.wsgi:filter_factory +heat.filter_factory = heat.api.openstack:faultwrap_filter + +[filter:cfnversionnegotiation] +paste.filter_factory = heat.common.wsgi:filter_factory +heat.filter_factory = heat.api.cfn:version_negotiation_filter + +[filter:cwversionnegotiation] +paste.filter_factory = heat.common.wsgi:filter_factory +heat.filter_factory = heat.api.cloudwatch:version_negotiation_filter + +[filter:context] +paste.filter_factory = heat.common.context:ContextMiddleware_filter_factory + +[filter:ec2authtoken] +paste.filter_factory = heat.api.aws.ec2token:EC2Token_filter_factory + +[filter:ssl] +paste.filter_factory = heat.common.wsgi:filter_factory +heat.filter_factory = heat.api.openstack:sslmiddleware_filter + +# Middleware to set auth_url header appropriately +[filter:authurl] +paste.filter_factory = heat.common.auth_url:filter_factory + +# Auth middleware that validates token against keystone +[filter:authtoken] +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory + +# Auth middleware that validates username/password against keystone +[filter:authpassword] +paste.filter_factory = heat.common.auth_password:filter_factory + +# Auth middleware that validates against custom backend +[filter:custombackendauth] +paste.filter_factory = heat.common.custom_backend_auth:filter_factory diff --git a/chef/cookbooks/openstack-orchestration/templates/default/default.yaml.erb b/chef/cookbooks/openstack-orchestration/templates/default/default.yaml.erb new file mode 100644 index 0000000..56fb61f --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/templates/default/default.yaml.erb @@ -0,0 +1,10 @@ +<%= node["openstack"]["orchestration"]["custom_template_banner"] %> + +resource_registry: + # allow older templates with Quantum in them. + "OS::Quantum*": "OS::Neutron*" + # Choose your implementation of AWS::CloudWatch::Alarm + #"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml" + "AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm" + "OS::Metering::Alarm": "OS::Ceilometer::Alarm" + "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml" diff --git a/chef/cookbooks/openstack-orchestration/templates/default/heat.conf.erb b/chef/cookbooks/openstack-orchestration/templates/default/heat.conf.erb new file mode 100755 index 0000000..7957fa3 --- /dev/null +++ b/chef/cookbooks/openstack-orchestration/templates/default/heat.conf.erb @@ -0,0 +1,1229 @@ +<%= node["openstack"]["orchestration"]["custom_template_banner"] %> + +[DEFAULT] + +# +# Options defined in heat.api.middleware.ssl +# + +# The HTTP Header that will be used to determine which the +# original request protocol scheme was, even if it was removed +# by an SSL terminator proxy. (string value) +#secure_proxy_ssl_header=X-Forwarded-Proto + +# +# Options defined in heat.common.config +# + +sql_connection=<%= @sql_connection %> + +# The default user for new instances. This option is +# deprecated and will be removed in the Juno release. If it's +# empty, Heat will use the default user set up with your cloud +# image (for OS::Nova::Server) or 'ec2-user' (for +# AWS::EC2::Instance). (string value) +#instance_user=ec2-user + +# Driver to use for controlling instances. (string value) +#instance_driver=heat.engine.nova + +# Engine identifier for multi-engine distributed lock. If +# this is set to "generate_uuid", a UUID will be generated. +# (string value) +#engine_id=generate_uuid + +# List of directories to search for plug-ins. (list value) +#plugin_dirs=/usr/lib64/heat,/usr/lib/heat + +# The directory to search for environment files. (string +# value) +#environment_dir=/etc/heat/environment.d + +# Select deferred auth method, stored password or trusts. +# (string value) +#deferred_auth_method=password + +# Subset of trustor roles to be delegated to heat. (list +# value) +#trusts_delegated_roles=heat_stack_owner + +# Maximum resources allowed per top-level stack. (integer +# value) +#max_resources_per_stack=1000 + +# Maximum number of stacks any one tenant may have active at +# one time. (integer value) +#max_stacks_per_tenant=100 + +# Controls how many events will be pruned whenever a stack's +# events exceed max_events_per_stack. Set this lower to keep +# more events at the expense of more frequent purges. (integer +# value) +#event_purge_batch_size=10 + +# Maximum events that will be available per stack. Older +# events will be deleted when this is reached. Set to 0 for +# unlimited events per stack. (integer value) +#max_events_per_stack=1000 + +# RPC timeout for the engine liveness check that is used for +# stack locking. (integer value) +#engine_life_check_timeout=2 + +# onready allows you to send a notification when the heat +# processes are ready to serve. This is either a module with +# the notify() method or a shell command. To enable +# notifications with systemd, one may use the 'systemd-notify +# --ready' shell command or the 'heat.common.systemd' +# notification module. (string value) +#onready= + +# Name of the engine node. This can be an opaque identifier. +# It is not necessarily a hostname, FQDN, or IP address. +# (string value) +#host=heat + +# Seconds between running periodic tasks. (integer value) +#periodic_interval=60 + +# URL of the Heat metadata server. (string value) +heat_metadata_server_url=http://<%= @heat_api_cfn_endpoint.host %>:<%= @heat_api_cfn_endpoint.port %> + +# URL of the Heat waitcondition server. (string value) +heat_waitcondition_server_url=http://<%= @heat_api_cfn_endpoint.host %>:<%= @heat_api_cfn_endpoint.port %><%= @heat_api_cfn_endpoint.path %>/waitcondition + +# URL of the Heat CloudWatch server. (string value) +heat_watch_server_url=http://<%= @heat_api_cloudwatch_endpoint.host %>:<%= @heat_api_cloudwatch_endpoint.port %> + +# Instance connection to CFN/CW API via https. (string value) +#instance_connection_is_secure=0 + +# Instance connection to CFN/CW API validate certs if SSL is +# used. (string value) +#instance_connection_https_validate_certificates=1 + +# Default region name used to get services endpoints. (string +# value) +#region_name_for_services= + +# Keystone role for heat template-defined users. (string +# value) +#heat_stack_user_role=heat_stack_user + +# Keystone domain ID which contains heat template-defined +# users. (string value) +#stack_user_domain= + +# Keystone username, a user with roles sufficient to manage +# users and projects in the stack_user_domain. (string value) +#stack_domain_admin= + +# Keystone password for stack_domain_admin user. (string +# value) +#stack_domain_admin_password= + +# Maximum raw byte size of any template. (integer value) +#max_template_size=524288 + +# Maximum depth allowed when using nested stacks. (integer +# value) +#max_nested_stack_depth=3 + + +# +# Options defined in heat.common.crypt +# + +# Encryption key used for authentication info in database. +# (string value) +#auth_encryption_key=notgood but just long enough i think + + +# +# Options defined in heat.common.heat_keystoneclient +# + +# Fully qualified class name to use as a keystone backend. +# (string value) +#keystone_backend=heat.common.heat_keystoneclient.KeystoneClientV3 + + +# +# Options defined in heat.common.wsgi +# + +# Maximum raw byte size of JSON request body. Should be larger +# than max_template_size. (integer value) +#max_json_body_size=1048576 + + +# +# Options defined in heat.db.api +# + +# The backend to use for db. (string value) +#db_backend=sqlalchemy + + +# +# Options defined in heat.engine.clients +# + +# Fully qualified class name to use as a client backend. +# (string value) +#cloud_backend=heat.engine.clients.OpenStackClients + + +# +# Options defined in heat.engine.resources.loadbalancer +# + +# Custom template for the built-in loadbalancer nested stack. +# (string value) +#loadbalancer_template= + + +# +# Options defined in heat.openstack.common.db.sqlalchemy.session +# + +# the filename to use with sqlite (string value) +#sqlite_db=heat.sqlite + +# If true, use synchronous mode for sqlite (boolean value) +#sqlite_synchronous=true + + +# +# Options defined in heat.openstack.common.eventlet_backdoor +# + +# Enable eventlet backdoor. Acceptable values are 0, , +# and :, where 0 results in listening on a random +# tcp port number; results in listening on the +# specified port number (and not enabling backdoor if that +# port is in use); and : results in listening on +# the smallest unused port number within the specified range +# of port numbers. The chosen port is displayed in the +# service's log file. (string value) +#backdoor_port= + + +# +# Options defined in heat.openstack.common.lockutils +# + +# Whether to disable inter-process locks (boolean value) +#disable_process_locking=false + +# Directory to use for lock files. (string value) +#lock_path= + + +# +# Options defined in heat.openstack.common.log +# + +# Print debugging output (set logging level to DEBUG instead +# of default WARNING level). (boolean value) +debug=<%= node["openstack"]["orchestration"]["debug"] %> + +# Print more verbose output (set logging level to INFO instead +# of default WARNING level). (boolean value) +verbose=<%= node["openstack"]["orchestration"]["verbose"] %> + +# Log output to standard error (boolean value) +#use_stderr=true + +# format string to use for log messages with context (string +# value) +#logging_context_format_string=%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user)s %(tenant)s] %(instance)s%(message)s + +# format string to use for log messages without context +# (string value) +#logging_default_format_string=%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s + +# data to append to log format when level is DEBUG (string +# value) +#logging_debug_format_suffix=%(funcName)s %(pathname)s:%(lineno)d + +# prefix each line of exception output with this format +# (string value) +#logging_exception_prefix=%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s + +# list of logger=LEVEL pairs (list value) +#default_log_levels=amqp=WARN,amqplib=WARN,boto=WARN,qpid=WARN,sqlalchemy=WARN,suds=INFO,iso8601=WARN + +# publish error events (boolean value) +#publish_errors=false + +# make deprecations fatal (boolean value) +#fatal_deprecations=false + +# If an instance is passed with the log message, format it +# like this (string value) +#instance_format="[instance: %(uuid)s] " + +# If an instance UUID is passed with the log message, format +# it like this (string value) +#instance_uuid_format="[instance: %(uuid)s] " + +# The name of logging configuration file. It does not disable +# existing loggers, but just appends specified logging +# configuration to any other existing logging options. Please +# see the Python logging module documentation for details on +# logging configuration files. (string value) +# Deprecated group/name - [DEFAULT]/log_config +<% if node["openstack"]["orchestration"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% end %> + +# DEPRECATED. A logging.Formatter log message format string +# which may use any of the available logging.LogRecord +# attributes. This option is deprecated. Please use +# logging_context_format_string and +# logging_default_format_string instead. (string value) +#log_format= + +# Format string for %%(asctime)s in log records. Default: +# %(default)s (string value) +#log_date_format=%Y-%m-%d %H:%M:%S + +# (Optional) Name of log file to output to. If no default is +# set, logging will go to stdout. (string value) +# Deprecated group/name - [DEFAULT]/logfile +#log_file= + +# (Optional) The base directory used for relative --log-file +# paths (string value) +# Deprecated group/name - [DEFAULT]/logdir +#log_dir= + +# Use syslog for logging. (boolean value) +#use_syslog=false + +# syslog facility to receive log lines (string value) +#syslog_log_facility=LOG_USER + + +# +# Options defined in heat.openstack.common.notifier.api +# + +# Driver or drivers to handle sending notifications (multi +# valued) +notification_driver = <%= node['openstack']['orchestration']['notification_driver'] %> + +# Default notification level for outgoing notifications +# (string value) +default_notification_level = <%= node['openstack']['orchestration']['default_notification_level'] %> + +# Default publisher_id for outgoing notifications (string +# value) +default_publisher_id = <%= node['openstack']['orchestration']['default_publisher_id'] %> + + +# +# Options defined in heat.openstack.common.notifier.list_notifier +# + +# List of drivers to send notifications (multi valued) +list_notifier_drivers = <%= node['openstack']['orchestration']['list_notifier_drivers'] %> + +# +# Options defined in heat.openstack.common.notifier.rpc_notifier +# + +# AMQP topic used for OpenStack notifications (list value) +notification_topics = <%= node['openstack']['orchestration']['notification_topics'] %> + + +# +# Options defined in heat.openstack.common.policy +# + +# JSON file containing policy (string value) +#policy_file=policy.json + +# Rule enforced when requested rule is not found (string +# value) +#policy_default_rule=default + + +# +# Options defined in heat.openstack.common.rpc +# + +# The messaging module to use, defaults to kombu. (string +# value) +#rpc_backend=heat.openstack.common.rpc.impl_kombu + +# Size of RPC thread pool (integer value) +rpc_thread_pool_size=<%= node["openstack"]["orchestration"]["rpc_thread_pool_size"] %> + +# Size of RPC connection pool (integer value) +rpc_conn_pool_size=<%= node["openstack"]["orchestration"]["rpc_conn_pool_size"] %> + +# Seconds to wait for a response from call or multicall +# (integer value) +rpc_response_timeout=<%= node["openstack"]["orchestration"]["rpc_response_timeout"] %> + +# Seconds to wait before a cast expires (TTL). Only supported +# by impl_zmq. (integer value) +#rpc_cast_timeout=30 + +# Modules of exceptions that are permitted to be recreated +# upon receiving exception data from an rpc call. (list value) +#allowed_rpc_exception_modules=nova.exception,cinder.exception,exceptions + +# If passed, use a fake RabbitMQ provider (boolean value) +#fake_rabbit=false + +# AMQP exchange to connect to if using RabbitMQ or Qpid +# (string value) +#control_exchange=heat + + +# +# Options defined in heat.openstack.common.rpc.amqp +# + +# Use durable queues in amqp. (boolean value) +# Deprecated group/name - [DEFAULT]/rabbit_durable_queues +#amqp_durable_queues=false + +# Auto-delete queues in amqp. (boolean value) +#amqp_auto_delete=false + + +# +# Options defined in heat.openstack.common.rpc.impl_kombu +# + +# SSL version to use (valid only if SSL enabled). valid values +# are TLSv1, SSLv23 and SSLv3. SSLv2 may be available on some +# distributions (string value) +#kombu_ssl_version= + +# SSL key file (valid only if SSL enabled) (string value) +#kombu_ssl_keyfile= + +# SSL cert file (valid only if SSL enabled) (string value) +#kombu_ssl_certfile= + +# SSL certification authority file (valid only if SSL enabled) +# (string value) +#kombu_ssl_ca_certs= + +<% if @mq_service_type == "rabbitmq" %> +# RabbitMQ HA cluster host:port pairs (list value) +<% if node["openstack"]["mq"]["orchestration"]["rabbit"]["ha"] -%> +rabbit_hosts=<%= @rabbit_hosts %> +<% else -%> +# The RabbitMQ broker address where a single node is used +# (string value) +rabbit_host=<%= node["openstack"]["mq"]["orchestration"]["rabbit"]["host"] %> + +# The RabbitMQ broker port where a single node is used +# (integer value) +rabbit_port=<%= node["openstack"]["mq"]["orchestration"]["rabbit"]["port"] %> +<% end -%> + + +# connect over SSL for RabbitMQ (boolean value) +rabbit_use_ssl=<%= node["openstack"]["mq"]["orchestration"]["rabbit"]["use_ssl"] %> + +# the RabbitMQ userid (string value) +rabbit_userid=<%= node["openstack"]["mq"]["orchestration"]["rabbit"]["userid"] %> + +# the RabbitMQ password (string value) +rabbit_password=<%= @mq_password %> + +# the RabbitMQ virtual host (string value) +rabbit_virtual_host=<%= node["openstack"]["mq"]["orchestration"]["rabbit"]["vhost"] %> + +# how frequently to retry connecting with RabbitMQ (integer +# value) +#rabbit_retry_interval=1 + +# how long to backoff for between retries when connecting to +# RabbitMQ (integer value) +#rabbit_retry_backoff=2 + +# maximum retries with trying to connect to RabbitMQ (the +# default of 0 implies an infinite retry count) (integer +# value) +#rabbit_max_retries=0 + +# use H/A queues in RabbitMQ (x-ha-policy: all).You need to +# wipe RabbitMQ database when changing this option. (boolean +# value) +#rabbit_ha_queues=false +<% end -%> + +# +# Options defined in heat.openstack.common.rpc.impl_qpid +# +<% if @mq_service_type == "qpid" %> + +rpc_backend=heat.openstack.common.rpc.impl_qpid + +# Qpid broker hostname (string value) +qpid_hostname=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["host"] %> + +# Qpid broker port (integer value) +qpid_port=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["port"] %> + +# Qpid HA cluster host:port pairs (list value) +#qpid_hosts=$qpid_hostname:$qpid_port + +# Username for qpid connection (string value) +qpid_username=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["username"] %> + +# Password for qpid connection (string value) +qpid_password=<%= @mq_password %> + +# Space separated list of SASL mechanisms to use for auth +# (string value) +qpid_sasl_mechanisms=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["sasl_mechanisms"] %> + +# Seconds between connection keepalive heartbeats (integer +# value) +qpid_heartbeat=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["heartbeat"] %> + +# Transport to use, either 'tcp' or 'ssl' (string value) +qpid_protocol=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["protocol"] %> + +# Disable Nagle algorithm (boolean value) +qpid_tcp_nodelay=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["tcp_nodelay"] %> + +# The qpid topology version to use. Version 1 is what was +# originally used by impl_qpid. Version 2 includes some +# backwards-incompatible changes that allow broker federation +# to work. Users should update to version 2 when they are +# able to take everything down, as it requires a clean break. +# (integer value) +#qpid_topology_version=1 + +qpid_reconnect_timeout=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["reconnect_timeout"] %> +qpid_reconnect_limit=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["reconnect_limit"] %> +qpid_reconnect_interval_min=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["reconnect_interval_min"] %> +qpid_reconnect_interval_max=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["reconnect_interval_max"] %> +qpid_reconnect_interval=<%= node["openstack"]["mq"]["orchestration"]["qpid"]["reconnect_interval"] %> +<% end -%> + +# +# Options defined in heat.openstack.common.rpc.impl_zmq +# + +# ZeroMQ bind address. Should be a wildcard (*), an ethernet +# interface, or IP. The "host" option should point or resolve +# to this address. (string value) +#rpc_zmq_bind_address=* + +# MatchMaker driver (string value) +#rpc_zmq_matchmaker=heat.openstack.common.rpc.matchmaker.MatchMakerLocalhost + +# ZeroMQ receiver listening port (integer value) +#rpc_zmq_port=9501 + +# Number of ZeroMQ contexts, defaults to 1 (integer value) +#rpc_zmq_contexts=1 + +# Maximum number of ingress messages to locally buffer per +# topic. Default is unlimited. (integer value) +#rpc_zmq_topic_backlog= + +# Directory for holding IPC sockets (string value) +#rpc_zmq_ipc_dir=/var/run/openstack + +# Name of this node. Must be a valid hostname, FQDN, or IP +# address. Must match "host" option, if running Nova. (string +# value) +#rpc_zmq_host=heat + + +# +# Options defined in heat.openstack.common.rpc.matchmaker +# + +# Heartbeat frequency (integer value) +#matchmaker_heartbeat_freq=300 + +# Heartbeat time-to-live. (integer value) +#matchmaker_heartbeat_ttl=600 + + +[auth_password] + +# +# Options defined in heat.common.config +# + +# Allow orchestration of multiple clouds. (boolean value) +#multi_cloud=false + +# Allowed keystone endpoints for auth_uri when multi_cloud is +# enabled. At least one endpoint needs to be specified. (list +# value) +#allowed_auth_uris= + + +[clients] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + + +[clients_ceilometer] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + + +[clients_cinder] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + + +[clients_heat] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + +# Optional heat url in format like +# http://0.0.0.0:8004/v1/%(tenant_id)s. (string value) +#url= + + +[clients_keystone] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + + +[clients_neutron] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + + +[clients_nova] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + + +[clients_swift] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + + +[clients_trove] + +# +# Options defined in heat.common.config +# + +# Type of endpoint in Identity service catalog to use for +# communication with the OpenStack service. (string value) +#endpoint_type=publicURL + +# Optional CA cert file to use in SSL connections. (string +# value) +#ca_file= + +# Optional PEM-formatted certificate chain file. (string +# value) +#cert_file= + +# Optional PEM-formatted file that contains the private key. +# (string value) +#key_file= + +# If set, then the server's certificate will not be verified. +# (boolean value) +#insecure=false + + +[database] + +# +# Options defined in heat.openstack.common.db.api +# + +# The backend to use for db (string value) +# Deprecated group/name - [DEFAULT]/db_backend +#backend=sqlalchemy + + +# +# Options defined in heat.openstack.common.db.sqlalchemy.session +# + +# The SQLAlchemy connection string used to connect to the +# database (string value) +# Deprecated group/name - [DEFAULT]/sql_connection +# Deprecated group/name - [DATABASE]/sql_connection +# Deprecated group/name - [sql]/connection +#connection=sqlite:////heat/openstack/common/db/$sqlite_db + +# The SQLAlchemy connection string used to connect to the +# slave database (string value) +#slave_connection= + +# timeout before idle sql connections are reaped (integer +# value) +# Deprecated group/name - [DEFAULT]/sql_idle_timeout +# Deprecated group/name - [DATABASE]/sql_idle_timeout +# Deprecated group/name - [sql]/idle_timeout +#idle_timeout=3600 + +# Minimum number of SQL connections to keep open in a pool +# (integer value) +# Deprecated group/name - [DEFAULT]/sql_min_pool_size +# Deprecated group/name - [DATABASE]/sql_min_pool_size +#min_pool_size=1 + +# Maximum number of SQL connections to keep open in a pool +# (integer value) +# Deprecated group/name - [DEFAULT]/sql_max_pool_size +# Deprecated group/name - [DATABASE]/sql_max_pool_size +#max_pool_size= + +# maximum db connection retries during startup. (setting -1 +# implies an infinite retry count) (integer value) +# Deprecated group/name - [DEFAULT]/sql_max_retries +# Deprecated group/name - [DATABASE]/sql_max_retries +#max_retries=10 + +# interval between retries of opening a sql connection +# (integer value) +# Deprecated group/name - [DEFAULT]/sql_retry_interval +# Deprecated group/name - [DATABASE]/reconnect_interval +#retry_interval=10 + +# If set, use this value for max_overflow with sqlalchemy +# (integer value) +# Deprecated group/name - [DEFAULT]/sql_max_overflow +# Deprecated group/name - [DATABASE]/sqlalchemy_max_overflow +#max_overflow= + +# Verbosity of SQL debugging information. 0=None, +# 100=Everything (integer value) +# Deprecated group/name - [DEFAULT]/sql_connection_debug +#connection_debug=0 + +# Add python stack traces to SQL as comment strings (boolean +# value) +# Deprecated group/name - [DEFAULT]/sql_connection_trace +#connection_trace=false + +# If set, use this value for pool_timeout with sqlalchemy +# (integer value) +# Deprecated group/name - [DATABASE]/sqlalchemy_pool_timeout +#pool_timeout= + + +[ec2authtoken] + +# +# Options defined in heat.api.aws.ec2token +# + +# Authentication Endpoint URI. (string value) +#auth_uri= + +# Allow orchestration of multiple clouds. (boolean value) +#multi_cloud=false + +# Allowed keystone endpoints for auth_uri when multi_cloud is +# enabled. At least one endpoint needs to be specified. (list +# value) +#allowed_auth_uris= + + +[heat_api] + +# +# Options defined in heat.common.wsgi +# + +# Address to bind the server. Useful when selecting a +# particular network interface. (string value) +bind_host=<%= @heat_api_bind.host %> + +# The port on which the server will listen. (integer value) +bind_port=<%= @heat_api_bind.port %> + +# Number of backlog requests to configure the socket with. +# (integer value) +#backlog=4096 + +# Location of the SSL certificate file to use for SSL mode. +# (string value) +#cert_file= + +# Location of the SSL key file to use for enabling SSL mode. +# (string value) +#key_file= + +# Number of workers for Heat service. (integer value) +#workers=0 + +# Maximum line size of message headers to be accepted. +# max_header_line may need to be increased when using large +# tokens (typically those generated by the Keystone v3 API +# with big service catalogs). (integer value) +#max_header_line=16384 + + +[heat_api_cfn] + +# +# Options defined in heat.common.wsgi +# + +# Address to bind the server. Useful when selecting a +# particular network interface. (string value) +bind_host=<%= @heat_api_cfn_bind.host %> + +# The port on which the server will listen. (integer value) +bind_port=<%= @heat_api_cfn_bind.port %> + +# Number of backlog requests to configure the socket with. +# (integer value) +#backlog=4096 + +# Location of the SSL certificate file to use for SSL mode. +# (string value) +#cert_file= + +# Location of the SSL key file to use for enabling SSL mode. +# (string value) +#key_file= + +# Number of workers for Heat service. (integer value) +#workers=0 + +# Maximum line size of message headers to be accepted. +# max_header_line may need to be increased when using large +# tokens (typically those generated by the Keystone v3 API +# with big service catalogs). (integer value) +#max_header_line=16384 + + +[heat_api_cloudwatch] + +# +# Options defined in heat.common.wsgi +# + +# Address to bind the server. Useful when selecting a +# particular network interface. (string value) +bind_host=<%= @heat_api_cloudwatch_bind.host %> + +# The port on which the server will listen. (integer value) +bind_port=<%= @heat_api_cloudwatch_bind.port %> + +# Number of backlog requests to configure the socket with. +# (integer value) +#backlog=4096 + +# Location of the SSL certificate file to use for SSL mode. +# (string value) +#cert_file= + +# Location of the SSL key file to use for enabling SSL mode. +# (string value) +#key_file= + +# Number of workers for Heat service. (integer value) +#workers=0 + +# Maximum line size of message headers to be accepted. +# max_header_line may need to be increased when using large +# tokens (typically those generated by the Keystone v3 API +# with big service catalogs.) (integer value) +#max_header_line=16384 + + +[keystone_authtoken] + +# +# Options defined in keystoneclient.middleware.auth_token +# + +# Prefix to prepend at the beginning of the path (string +# value) +#auth_admin_prefix= + +# Host providing the admin Identity API endpoint (string +# value) +auth_host=<%= @identity_admin_endpoint.host %> + +# Port of the admin Identity API endpoint (integer value) +auth_port=<%= @identity_admin_endpoint.port %> + +# Protocol of the admin Identity API endpoint(http or https) +# (string value) +auth_protocol=<%= @identity_admin_endpoint.scheme %> + +# Complete public Identity API endpoint (string value) +auth_uri=<%= @auth_uri %> + +# API version of the admin Identity API endpoint (string +# value) +auth_version=<%= node["openstack"]["orchestration"]["api"]["auth"]["version"] %> + +# Do not handle authorization requests within the middleware, +# but delegate the authorization decision to downstream WSGI +# components (boolean value) +#delay_auth_decision=false + +# Request timeout value for communicating with Identity API +# server. (boolean value) +#http_connect_timeout= + +# How many times are we trying to reconnect when communicating +# with Identity API Server. (integer value) +#http_request_max_retries=3 + +# Single shared secret with the Keystone configuration used +# for bootstrapping a Keystone installation, or otherwise +# bypassing the normal authentication process. (string value) +#admin_token= + +# Keystone account username (string value) +admin_user=<%= node["openstack"]["orchestration"]["service_user"] %> + +# Keystone account password (string value) +admin_password=<%= @service_pass %> + +# Keystone service account tenant name to validate user tokens +# (string value) +admin_tenant_name=<%= node["openstack"]["orchestration"]["service_tenant_name"] %> + +# Env key for the swift cache (string value) +#cache= + +# Required if Keystone server requires client certificate +# (string value) +#certfile= + +# Required if Keystone server requires client certificate +# (string value) +#keyfile= + +# A PEM encoded Certificate Authority to use when verifying +# HTTPs connections. Defaults to system CAs. (string value) +#cafile= + +# Verify HTTPS connections. (boolean value) +#insecure=false + +# Directory used to cache files related to PKI tokens (string +# value) +signing_dir=<%= node['openstack']['orchestration']['api']['auth']['cache_dir'] %> + +# Optionally specify a list of memcached server(s) to use for +# caching. If left undefined, tokens will instead be cached +# in-process. (list value) +# Deprecated group/name - [DEFAULT]/memcache_servers +#memcached_servers= + +# In order to prevent excessive effort spent validating +# tokens, the middleware caches previously-seen tokens for a +# configurable duration (in seconds). Set to -1 to disable +# caching completely. (integer value) +#token_cache_time=300 + +# Determines the frequency at which the list of revoked tokens +# is retrieved from the Identity service (in seconds). A high +# number of revocation events combined with a low cache +# duration may significantly reduce performance. (integer +# value) +#revocation_cache_time=300 + +# (optional) if defined, indicate whether token data should be +# authenticated or authenticated and encrypted. Acceptable +# values are MAC or ENCRYPT. If MAC, token data is +# authenticated (with HMAC) in the cache. If ENCRYPT, token +# data is encrypted and authenticated in the cache. If the +# value is not one of these options or empty, auth_token will +# raise an exception on initialization. (string value) +#memcache_security_strategy= + +# (optional, mandatory if memcache_security_strategy is +# defined) this string is used for key derivation. (string +# value) +#memcache_secret_key= + +# (optional) indicate whether to set the X-Service-Catalog +# header. If False, middleware will not ask for service +# catalog on token validation and will not set the X-Service- +# Catalog header. (boolean value) +#include_service_catalog=true + +# Used to control the use and type of token binding. Can be +# set to: "disabled" to not check token binding. "permissive" +# (default) to validate binding information if the bind type +# is of a form known to the server and ignore it if not. +# "strict" like "permissive" but if the bind type is unknown +# the token will be rejected. "required" any form of token +# binding is needed to be allowed. Finally the name of a +# binding method that must be present in tokens. (string +# value) +#enforce_token_bind=permissive + + +[matchmaker_redis] + +# +# Options defined in heat.openstack.common.rpc.matchmaker_redis +# + +# Host to locate redis (string value) +#host=127.0.0.1 + +# Use this port to connect to redis host. (integer value) +#port=6379 + +# Password for Redis server. (optional) (string value) +#password= + + +[matchmaker_ring] + +# +# Options defined in heat.openstack.common.rpc.matchmaker_ring +# + +# Matchmaker ring file (JSON) (string value) +# Deprecated group/name - [DEFAULT]/matchmaker_ringfile +#ringfile=/etc/oslo/matchmaker_ring.json + + +[paste_deploy] + +# +# Options defined in heat.common.config +# + +# The flavor to use. (string value) +#flavor= + +# The API paste config file to use. (string value) +#api_paste_config=api-paste.ini + + +[revision] + +# +# Options defined in heat.common.config +# + +# Heat build revision. If you would prefer to manage your +# build revision separately, you can move this section to a +# different file and add it as another config option. (string +# value) +#heat_revision=unknown + + +[rpc_notifier2] + +# +# Options defined in heat.openstack.common.notifier.rpc_notifier2 +# + +# AMQP topic(s) used for OpenStack notifications (list value) +#topics=notifications + + +[ssl] + +# +# Options defined in heat.openstack.common.sslutils +# + +# CA certificate file to use to verify connecting clients +# (string value) +#ca_file= + +# Certificate file to use when starting the server securely +# (string value) +#cert_file= + +# Private key file to use when starting the server securely +# (string value) +#key_file= diff --git a/chef/cookbooks/openstack-telemetry/.rubocop.yml b/chef/cookbooks/openstack-telemetry/.rubocop.yml new file mode 100644 index 0000000..5500e6d --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/.rubocop.yml @@ -0,0 +1,24 @@ +AllCops: + Includes: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + +Encoding: + Exclude: + - metadata.rb + - Gemfile + +NumericLiterals: + Enabled: false + +LineLength: + Enabled: false + +WordArray: + MinSize: 3 diff --git a/chef/cookbooks/openstack-telemetry/Berksfile b/chef/cookbooks/openstack-telemetry/Berksfile new file mode 100644 index 0000000..76191f5 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/Berksfile @@ -0,0 +1,12 @@ +metadata + +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" +cookbook "openstack-identity", + git: "git://github.com/stackforge/cookbook-openstack-identity.git" +cookbook "openstack-compute", + git: "git://github.com/stackforge/cookbook-openstack-compute.git" +cookbook "openstack-image", + git: "git://github.com/stackforge/cookbook-openstack-image.git" +cookbook "openstack-network", + git: "git://github.com/stackforge/cookbook-openstack-network.git" diff --git a/chef/cookbooks/openstack-telemetry/CHANGELOG.md b/chef/cookbooks/openstack-telemetry/CHANGELOG.md new file mode 100644 index 0000000..5e52738 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/CHANGELOG.md @@ -0,0 +1,59 @@ +openstack-telemetry Cookbook CHANGELG +============================== +This file is used to list changes made in each version of the openstack-metering cookbook. +## 9.1.1 +* Remove policy.json file, it's just a dup of what's in the package + +## 9.1.0 +### Blue print +# Add recipes for the ceilometer alarm services (alarm-services) +# Add recipes for the ceilometer agent notification service (alarm-services) + +## 9.0.0 +* Upgrade to Icehouse + +## 8.4.0 +### Blue print +# Use the library method auth_uri_transform + +## 8.3.0 +* Rename openstack-metering to openstack-telemetry + +## 8.2.0 +### Blueprint +* Add NoSQL support for metering. + +## 8.1.0 +* Add client recipe + +## 8.0.0 +### New version +* Upgrade to upstream Havana release + +## 7.1.1 +### Bug +* Relax the dependency on openstack-identity to the 7.x series + +## 7.1.0 +### Blueprint +* Added qpid support to ceilometer. default is rabbitmq + +## 7.0.5 +### Bug +* Corrected inconsistent keystone middleware auth_token for ceilometer.conf.erb. + +## 7.0.4 +### Bug +* Ubuntu package dependency for python-mysqldb missing for ceilometer-collector + +## 7.0.3 +### Bug +* Ubuntu cloud archive dpkg failing to install init script properly for agent-compute + +## 7.0.2 +### Improvement +* Add optional host to the ceilometer.conf + +## 7.0.1 +### Bug +* Fix naming inconsistency for db password databag. This makes the metering cookbook consistent with all the others. diff --git a/chef/cookbooks/openstack-telemetry/Gemfile b/chef/cookbooks/openstack-telemetry/Gemfile new file mode 100644 index 0000000..58934c6 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/Gemfile @@ -0,0 +1,10 @@ +source 'https://rubygems.org' + +gem 'chef', '~> 11.8' +gem 'json', '<= 1.7.7' # chef 11 dependency +gem 'berkshelf', '~> 2.0.10' +gem 'chefspec', '~> 3.4.0' +gem 'foodcritic', '~> 3.0.3' +gem 'strainer' +gem 'rubocop', '~> 0.18.1' +gem 'fauxhai', '>= 2.1.0' diff --git a/chef/cookbooks/openstack-telemetry/Gemfile.lock b/chef/cookbooks/openstack-telemetry/Gemfile.lock new file mode 100644 index 0000000..bbe41ab --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/Gemfile.lock @@ -0,0 +1,238 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.17) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.6) + akami (1.2.1) + gyoku (>= 0.4.0) + nokogiri + ast (1.1.0) + berkshelf (2.0.14) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + chozo (>= 0.6.1) + faraday (~> 0.8.0) + faraday (~> 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.5.0) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) + buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-platform (0.1.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.1) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.10.4) + chef-zero (~> 1.7, >= 1.7.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.8.1) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.4) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.3) + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chef-zero (1.7.3) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.4.0) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + coderay (1.1.0) + diff-lcs (1.2.5) + erubis (2.7.0) + faraday (0.8.9) + multipart-post (~> 1.2.0) + fauxhai (2.1.0) + net-ssh + ohai + ffi (1.9.3) + foodcritic (3.0.3) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rake + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.1.1) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.21) + hitimes (1.2.1) + httpclient (2.3.4.1) + httpi (0.9.7) + rack + i18n (0.6.9) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + logging (1.8.2) + little-plugger (>= 1.1.3) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (1.25.1) + minitar (0.5.4) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.4.0) + mixlib-config (2.1.0) + mixlib-log (1.6.0) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.9.2) + multipart-post (1.2.0) + net-http-persistent (2.9.4) + net-ssh (2.8.0) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-gateway (>= 1.2.0) + nio4r (1.0.0) + nokogiri (1.5.11) + nori (1.1.5) + ohai (6.20.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu (~> 2.5.2) + yajl-ruby + parser (2.1.7) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.4) + powerpack (0.0.9) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) + rack (1.5.2) + rainbow (2.0.0) + rake (10.2.2) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.5) + ridley (1.5.3) + addressable + buff-config (~> 0.2) + buff-extensions (~> 0.3) + buff-ignore (~> 1.1) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + nio4r (>= 0.5.0) + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.6) + rubocop (0.18.1) + json (>= 1.7.7, < 2) + parser (~> 2.1.3) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + slop (3.5.0) + solve (0.8.2) + strainer (3.3.0) + berkshelf (~> 2.0) + buff-platform (~> 0.1) + systemu (2.5.2) + thor (0.18.1) + timers (2.0.0) + hitimes + treetop (1.4.15) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.3.2) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.3) + gssapi (~> 1.0.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.10) + chef (~> 11.8) + chefspec (~> 3.4.0) + fauxhai (>= 2.1.0) + foodcritic (~> 3.0.3) + json (<= 1.7.7) + rubocop (~> 0.18.1) + strainer diff --git a/chef/cookbooks/openstack-telemetry/README.md b/chef/cookbooks/openstack-telemetry/README.md new file mode 100644 index 0000000..5b4d796 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/README.md @@ -0,0 +1,119 @@ +Description +=========== + +Installs the OpenStack Metering service **Ceilometer** as part of the OpenStack +reference deployment Chef for OpenStack. Ceilometer is currently installed +from packages. + +https://wiki.openstack.org/wiki/Ceilometer + +Requirements +============ + +Cookbooks +--------- + +Usage +===== + +agent-central +---- +- Installs agent central service. + +agent-compute +---- +- Installs agent compute service. + +agent-notification +---- +- Installs agent notification service. + +alarm-evaluator +---- +- Installs alarm evaluator service. + +alarm-notifier +---- +- Installs alarm notifier service. + +api +---- +- Installs API service. + +client +---- +- Install the client packages + +collector +---- +- Installs collector package and service. If the NoSQL database is used for metering service, ceilometer-dbsync will not be executed. + +common +---- +- Common metering configuration. + +identity_registration +---- +- Registers the endpoints, tenant and user for metering service with Keystone. + +Attributes +========== + +* `openstack['telemetry']['api']['auth']['version']` - Select v2.0 or v3.0. Default v2.0. The auth API version used to interact with identity service. +TODO: Add DB2 support on other platforms +* `openstack['telemetry']['platform']['db2_python_packages']` - Array of DB2 python packages, only available on redhat platform + +The following attributes are defined in attributes/default.rb of the common cookbook, but are documented here due to their relevance: + +* `openstack['endpoints']['telemetry-api-bind']['host']` - The IP address to bind the api service to +* `openstack['endpoints']['telemetry-api-bind']['port']` - The port to bind the api service to +* `openstack['endpoints']['telemetry-api-bind']['bind_interface']` - The interface name to bind the api service to + +If the value of the 'bind_interface' attribute is non-nil, then the telemetry service will be bound to the first IP address on that interface. If the value of the 'bind_interface' attribute is nil, then the telemetry service will be bound to the IP address specifie + +Testing +===== + +Please refer to the [TESTING.md](TESTING.md) for instructions for testing the cookbook. + +Berkshelf +===== + +Berks will resolve version requirements and dependencies on first run and +store these in Berksfile.lock. If new cookbooks become available you can run +`berks update` to update the references in Berksfile.lock. Berksfile.lock will +be included in stable branches to provide a known good set of dependencies. +Berksfile.lock will not be included in development branches to encourage +development against the latest cookbooks. + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | Matt Ray () | +| **Author** | John Dewey () | +| **Author** | Justin Shepherd () | +| **Author** | Salman Baset () | +| **Author** | Ionut Artarisi () | +| **Author** | Eric Zhou () | +| **Author** | Chen Zhiwei () | +| | | +| **Copyright** | Copyright (c) 2013, Opscode, Inc. | +| **Copyright** | Copyright (c) 2013, AT&T Services, Inc. | +| **Copyright** | Copyright (c) 2013, Rackspace US, Inc. | +| **Copyright** | Copyright (c) 2013-2014, IBM, Corp. | +| **Copyright** | Copyright (c) 2013, SUSE Linux GmbH | + + +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. diff --git a/chef/cookbooks/openstack-telemetry/Strainerfile b/chef/cookbooks/openstack-telemetry/Strainerfile new file mode 100644 index 0000000..44e3e14 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +rubocop: bundle exec rubocop $SANDBOX/$COOKBOOK +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-telemetry/TESTING.md b/chef/cookbooks/openstack-telemetry/TESTING.md new file mode 100644 index 0000000..2b52f68 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/TESTING.md @@ -0,0 +1,42 @@ +# Testing the Cookbook # + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in [Strainerfile](Strainerfile), which in turn calls rubocop, knife, foodcritic and chefspec. + +To run all of the tests with Strainer: + + $ bundle exec strainer test -s Strainerfile + +Or you may run the tests individually: + + $ bundle install --path=.bundle # install gem dependencies + $ bundle exec berks install --path=.cookbooks # install cookbook dependencies + $ bundle exec strainer test -s Strainerfile # run tests + +## Rubocop ## + +[Rubocop](https://github.com/bbatsov/rubocop) is a static Ruby code analyzer, based on the community [Ruby style guide](https://github.com/bbatsov/ruby-style-guide). We are attempting to adhere to this where applicable, slowly cleaning up the cookbooks until we can turn on Rubocop for gating the commits. + +### Attribute Rules ### + +Since there are slight style differences between the coding of attributes, recipes and metadata files there are specific `.rubocop.yml` files for each of: + + [Gemfile and metadata.rb](.rubocop.yml) + [attributes/*.rb](attributes/.rubocop.yml) + [recipes/.rubocop.yml](recipes/.rubocop.yml) + [spec/.rubocop.yml](spec/.rubocop.yml) + +## Knife ## + +[knife cookbook test](http://docs.opscode.com/chef/knife.html#test) is used to check the cookbook's Ruby and ERB files for basic syntax errors. + +## Foodcritic ## + +[Foodcritic](http://acrmp.github.io/foodcritic/) is a lint tool for Chef cookbooks. We ignore the following rules: + +[FC003](http://acrmp.github.io/foodcritic/#FC003) these cookbooks are not intended for Chef Solo. + +## Chefspec + +[ChefSpec](http://code.sethvargo.com/chefspec/) is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers. diff --git a/chef/cookbooks/openstack-telemetry/attributes/default.rb b/chef/cookbooks/openstack-telemetry/attributes/default.rb new file mode 100644 index 0000000..3ad37ed --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/attributes/default.rb @@ -0,0 +1,122 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-telemetry +# Recipe:: default +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# +# 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. +# + +# The name of the Chef role that knows about the message queue server +# that Nova uses +default['openstack']['telemetry']['rabbit_server_chef_role'] = 'os-ops-messaging' + +default['openstack']['telemetry']['conf_dir'] = '/etc/ceilometer' +default['openstack']['telemetry']['conf'] = ::File.join(node['openstack']['telemetry']['conf_dir'], 'ceilometer.conf') +default['openstack']['telemetry']['periodic_interval'] = 600 +default['openstack']['telemetry']['syslog']['use'] = false +default['openstack']['telemetry']['verbose'] = 'true' +default['openstack']['telemetry']['debug'] = 'false' + +default['openstack']['telemetry']['api']['auth']['cache_dir'] = '/var/cache/ceilometer/api' + +default['openstack']['telemetry']['api']['auth']['version'] = node['openstack']['api']['auth']['version'] + +default['openstack']['telemetry']['user'] = 'ceilometer' +default['openstack']['telemetry']['group'] = 'ceilometer' + +default['openstack']['telemetry']['region'] = node['openstack']['region'] +default['openstack']['telemetry']['service_user'] = 'ceilometer' +default['openstack']['telemetry']['service_tenant_name'] = 'service' +default['openstack']['telemetry']['service_role'] = 'admin' + +case node['openstack']['compute']['driver'] +when 'libvirt.LibvirtDriver' + default['openstack']['telemetry']['hypervisor_inspector'] = 'libvirt' +else + default['openstack']['telemetry']['hypervisor_inspector'] = nil +end + +case platform_family +when 'suse' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['telemetry']['platform'] = { + 'mysql_python_packages' => ['python-mysql'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'common_packages' => ['openstack-ceilometer'], + 'agent_central_packages' => ['openstack-ceilometer-agent-central'], + 'agent_central_service' => 'openstack-ceilometer-agent-central', + 'agent_compute_packages' => ['openstack-ceilometer-agent-compute'], + 'agent_compute_service' => 'openstack-ceilometer-agent-compute', + 'agent_notification_packages' => ['openstack-ceilometer-agent-notification'], + 'agent_notification_service' => 'openstack-ceilometer-agent-notification', + 'alarm_evaluator_packages' => ['openstack-ceilometer-alarm-evaluator'], + 'alarm_evaluator_service' => 'openstack-ceilometer-alarm-evaluator', + 'alarm_notifier_packages' => ['openstack-ceilometer-alarm-notifier'], + 'alarm_notifier_service' => 'openstack-ceilometer-alarm-notifier', + 'api_packages' => ['openstack-ceilometer-api'], + 'api_service' => 'openstack-ceilometer-api', + 'client_packages' => ['python-ceilometerclient'], + 'collector_packages' => ['openstack-ceilometer-collector'], + 'collector_service' => 'openstack-ceilometer-collector', + 'package_overrides' => '' + } + +when 'fedora', 'rhel' + default['openstack']['telemetry']['platform'] = { + 'mysql_python_packages' => ['MySQL-python'], + 'db2_python_packages' => ['python-ibm-db', 'python-ibm-db-sa'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'common_packages' => ['openstack-ceilometer-common'], + 'agent_central_packages' => ['openstack-ceilometer-central'], + 'agent_central_service' => 'openstack-ceilometer-central', + 'agent_compute_packages' => ['openstack-ceilometer-compute'], + 'agent_compute_service' => 'openstack-ceilometer-compute', + 'agent_notification_packages' => ['openstack-ceilometer-collector'], + 'agent_notification_service' => 'openstack-ceilometer-notification', + 'alarm_evaluator_packages' => ['openstack-ceilometer-alarm'], + 'alarm_evaluator_service' => 'openstack-ceilometer-alarm-evaluator', + 'alarm_notifier_packages' => ['openstack-ceilometer-alarm'], + 'alarm_notifier_service' => 'openstack-ceilometer-alarm-notifier', + 'api_packages' => ['openstack-ceilometer-api'], + 'api_service' => 'openstack-ceilometer-api', + 'client_packages' => ['python-ceilometerclient'], + 'collector_packages' => ['openstack-ceilometer-collector'], + 'collector_service' => 'openstack-ceilometer-collector', + 'package_overrides' => '' + } + +when 'debian' + default['openstack']['telemetry']['platform'] = { + 'mysql_python_packages' => ['python-mysqldb'], + 'postgresql_python_packages' => ['python-psycopg2'], + 'common_packages' => ['ceilometer-common'], + 'agent_central_packages' => ['ceilometer-agent-central'], + 'agent_central_service' => 'ceilometer-agent-central', + 'agent_compute_packages' => ['ceilometer-agent-compute'], + 'agent_compute_service' => 'ceilometer-agent-compute', + 'agent_notification_packages' => ['ceilometer-agent-notification'], + 'agent_notification_service' => 'ceilometer-agent-notification', + 'alarm_evaluator_packages' => ['ceilometer-alarm-evaluator'], + 'alarm_evaluator_service' => 'ceilometer-alarm-evaluator', + 'alarm_notifier_packages' => ['ceilometer-alarm-notifier'], + 'alarm_notifier_service' => 'ceilometer-alarm-notifier', + 'api_packages' => ['ceilometer-api'], + 'api_service' => 'ceilometer-api', + 'client_packages' => ['python-ceilometerclient'], + 'collector_packages' => ['ceilometer-collector', 'python-mysqldb'], + 'collector_service' => 'ceilometer-collector', + 'package_overrides' => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } +end diff --git a/chef/cookbooks/openstack-telemetry/metadata.rb b/chef/cookbooks/openstack-telemetry/metadata.rb new file mode 100644 index 0000000..5f13e6a --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/metadata.rb @@ -0,0 +1,26 @@ +name 'openstack-telemetry' +maintainer 'AT&T Services, Inc.' +maintainer_email 'cookbooks@lists.tfoundry.com' +license 'Apache 2.0' +description 'The OpenStack Metering service Ceilometer.' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '9.1.1' + +recipe 'openstack-telemetry::agent-central', 'Installs agent central service.' +recipe 'openstack-telemetry::agent-compute', 'Installs agent compute service.' +recipe 'openstack-telemetry::agent-notification', 'Installs the agent notification service.' +recipe 'openstack-telemetry::api', 'Installs API service.' +recipe 'openstack-telemetry::client', 'Installs client.' +recipe 'openstack-telemetry::collector', 'Installs collector service. If the NoSQL database is used for metering service, ceilometer-dbsync will not be executed.' +recipe 'openstack-telemetry::alarm-evaluator', 'Installs the alarm evaluator service.' +recipe 'openstack-telemetry::alarm-notifier', 'Installs the alarm notifier service.' +recipe 'openstack-telemetry::common', 'Common metering configuration.' +recipe 'openstack-telemetry::identity_registration', 'Registers the endpoints, tenant and user for metering service with Keystone' + +%w{ ubuntu suse }.each do |os| + supports os +end + +depends 'openstack-common', '~> 9.0' +depends 'openstack-identity', '~> 9.0' +depends 'openstack-compute', '~> 9.0' diff --git a/chef/cookbooks/openstack-metering/recipes/agent-central.rb b/chef/cookbooks/openstack-telemetry/recipes/agent-central.rb similarity index 56% rename from chef/cookbooks/openstack-metering/recipes/agent-central.rb rename to chef/cookbooks/openstack-telemetry/recipes/agent-central.rb index 5ba85b8..0c0d467 100644 --- a/chef/cookbooks/openstack-metering/recipes/agent-central.rb +++ b/chef/cookbooks/openstack-telemetry/recipes/agent-central.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: openstack-metering +# Cookbook Name:: openstack-telemetry # Recipe:: agent-central # # Copyright 2013, AT&T Services, Inc. @@ -18,13 +19,19 @@ # limitations under the License. # -include_recipe "openstack-metering::common" +include_recipe 'openstack-telemetry::common' -platform = node["openstack"]["metering"]["platform"] -platform["agent_central_packages"].each do |pkg| - package pkg +platform = node['openstack']['telemetry']['platform'] +platform['agent_central_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] + end end -service platform["agent_central_service"] do - action :start +service 'ceilometer-agent-central' do + service_name platform['agent_central_service'] + supports status: true, restart: true + subscribes :restart, "template[#{node['openstack']['telemetry']['conf']}]" + + action [:enable, :start] end diff --git a/chef/cookbooks/openstack-metering/recipes/agent-compute.rb b/chef/cookbooks/openstack-telemetry/recipes/agent-compute.rb similarity index 59% rename from chef/cookbooks/openstack-metering/recipes/agent-compute.rb rename to chef/cookbooks/openstack-telemetry/recipes/agent-compute.rb index f9dbf09..de43ef3 100644 --- a/chef/cookbooks/openstack-metering/recipes/agent-compute.rb +++ b/chef/cookbooks/openstack-telemetry/recipes/agent-compute.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: openstack-metering +# Cookbook Name:: openstack-telemetry # Recipe:: agent-compute # # Copyright 2013, AT&T Services, Inc. @@ -18,23 +19,29 @@ # limitations under the License. # -include_recipe "openstack-metering::common" +include_recipe 'openstack-telemetry::common' -platform = node["openstack"]["metering"]["platform"] -platform["agent_compute_packages"].each do |pkg| - package pkg +platform = node['openstack']['telemetry']['platform'] +platform['agent_compute_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] + end end # temp fix for compute-agent init not installing properly ubuntu # See https://bugs.launchpad.net/cloud-archive/+bug/1221945 -if node["platform"] == "ubuntu" - init_script = "/etc/init/ceilometer-agent-compute.conf" - execute "fix init script" do +if node['platform'] == 'ubuntu' + init_script = '/etc/init/ceilometer-agent-compute.conf' + execute 'fix init script' do command "cp #{init_script}.dpkg-new #{init_script}" not_if { ::File.exists?(init_script) } end end -service platform["agent_compute_service"] do - action :start +service 'ceilometer-agent-compute' do + service_name platform['agent_compute_service'] + supports status: true, restart: true + subscribes :restart, "template[#{node['openstack']['telemetry']['conf']}]" + + action [:enable, :start] end diff --git a/chef/cookbooks/openstack-telemetry/recipes/agent-notification.rb b/chef/cookbooks/openstack-telemetry/recipes/agent-notification.rb new file mode 100644 index 0000000..0bda5fd --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/recipes/agent-notification.rb @@ -0,0 +1,36 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-telemetry +# Recipe:: agent-notification +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +include_recipe 'openstack-telemetry::common' + +platform = node['openstack']['telemetry']['platform'] +platform['agent_notification_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] + end +end + +service 'ceilometer-agent-notification' do + service_name platform['agent_notification_service'] + supports status: true, restart: true + subscribes :restart, "template[#{node['openstack']['telemetry']['conf']}]" + + action [:enable, :start] +end diff --git a/chef/cookbooks/openstack-telemetry/recipes/alarm-evaluator.rb b/chef/cookbooks/openstack-telemetry/recipes/alarm-evaluator.rb new file mode 100644 index 0000000..a57741e --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/recipes/alarm-evaluator.rb @@ -0,0 +1,36 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-telemetry +# Recipe:: alarm-evaluator +# +# Copyright 2014, IBM Corp. +# +# 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. +# + +include_recipe 'openstack-telemetry::common' + +platform = node['openstack']['telemetry']['platform'] +platform['alarm_evaluator_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] + end +end + +service 'ceilometer-agent-evaluator' do + service_name platform['alarm_evaluator_service'] + supports status: true, restart: true + subscribes :restart, "template[#{node['openstack']['telemetry']['conf']}]" + + action [:enable, :start] +end diff --git a/chef/cookbooks/python/test/cookbooks/python_test/recipes/cook-3084.rb b/chef/cookbooks/openstack-telemetry/recipes/alarm-notifier.rb similarity index 50% rename from chef/cookbooks/python/test/cookbooks/python_test/recipes/cook-3084.rb rename to chef/cookbooks/openstack-telemetry/recipes/alarm-notifier.rb index 3f01581..e839b45 100644 --- a/chef/cookbooks/python/test/cookbooks/python_test/recipes/cook-3084.rb +++ b/chef/cookbooks/openstack-telemetry/recipes/alarm-notifier.rb @@ -1,9 +1,9 @@ +# encoding: UTF-8 # -# Author:: Alex Kiernan () -# Cookbook Name:: python -# Recipe:: cook-3084 +# Cookbook Name:: openstack-telemetry +# Recipe:: alarm-notifier # -# Copyright 2013, Alex Kiernan +# Copyright 2014, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,18 +18,19 @@ # limitations under the License. # -include_recipe "python" +include_recipe 'openstack-telemetry::common' -python_virtualenv "cook-3084" do -end - -python_virtualenv "cook-3084-interpreter" do - # on EL5 the default python we install is called python26 - if !node['python']['install_method'].eql?("source") && - platform_family?('rhel') && - node['platform_version'].split('.').first.to_i < 6 - interpreter '/usr/bin/python26' - else - interpreter 'python' +platform = node['openstack']['telemetry']['platform'] +platform['alarm_notifier_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] end end + +service 'ceilometer-alarm-notifier' do + service_name platform['alarm_notifier_service'] + supports status: true, restart: true + subscribes :restart, "template[#{node['openstack']['telemetry']['conf']}]" + + action [:enable, :start] +end diff --git a/chef/cookbooks/openstack-metering/recipes/api.rb b/chef/cookbooks/openstack-telemetry/recipes/api.rb similarity index 52% rename from chef/cookbooks/openstack-metering/recipes/api.rb rename to chef/cookbooks/openstack-telemetry/recipes/api.rb index ce611f2..d8fcc45 100644 --- a/chef/cookbooks/openstack-metering/recipes/api.rb +++ b/chef/cookbooks/openstack-telemetry/recipes/api.rb @@ -1,5 +1,6 @@ +# encoding: UTF-8 # -# Cookbook Name:: openstack-metering +# Cookbook Name:: openstack-telemetry # Recipe:: api # # Copyright 2013, AT&T Services, Inc. @@ -19,19 +20,25 @@ # limitations under the License. # -include_recipe "openstack-metering::common" +include_recipe 'openstack-telemetry::common' -directory ::File.dirname(node["openstack"]["metering"]["api"]["auth"]["cache_dir"]) do - owner node["openstack"]["metering"]["user"] - group node["openstack"]["metering"]["group"] +directory ::File.dirname(node['openstack']['telemetry']['api']['auth']['cache_dir']) do + owner node['openstack']['telemetry']['user'] + group node['openstack']['telemetry']['group'] mode 00700 end -platform = node["openstack"]["metering"]["platform"] -platform["api_packages"].each do |pkg| - package pkg +platform = node['openstack']['telemetry']['platform'] +platform['api_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] + end end -service platform["api_service"] do - action :start +service 'ceilometer-api' do + service_name platform['api_service'] + supports status: true, restart: true + subscribes :restart, "template[#{node['openstack']['telemetry']['conf']}]" + + action [:enable, :start] end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/default_test.rb b/chef/cookbooks/openstack-telemetry/recipes/client.rb similarity index 61% rename from chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/default_test.rb rename to chef/cookbooks/openstack-telemetry/recipes/client.rb index ab97676..9c6dce7 100644 --- a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/default_test.rb +++ b/chef/cookbooks/openstack-telemetry/recipes/client.rb @@ -1,8 +1,9 @@ +# encoding: UTF-8 # -# Cookbook Name:: apt_test -# Recipe:: default +# Cookbook Name:: openstack-telemetry +# Recipe:: client # -# Copyright 2012, Opscode, Inc. +# Copyright 2014, IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,12 +18,15 @@ # limitations under the License. # -require File.expand_path('../support/helpers', __FILE__) +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end -describe "apt_test::default" do - include Helpers::AptTest +platform = node['openstack']['telemetry']['platform'] +platform['client_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] - it 'creates the preseeding directory' do - directory('/var/cache/local/preseeding').must_exist + action :upgrade end end diff --git a/chef/cookbooks/openstack-telemetry/recipes/collector.rb b/chef/cookbooks/openstack-telemetry/recipes/collector.rb new file mode 100644 index 0000000..e5adcb4 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/recipes/collector.rb @@ -0,0 +1,56 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-telemetry +# Recipe:: collector +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, Craig Tracey +# Copyright 2013, SUSE Linux GmbH +# +# 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. +# + +include_recipe 'openstack-telemetry::common' + +conf_switch = "--config-file #{node["openstack"]["telemetry"]["conf"]}" + +unless node['openstack']['db']['telemetry']['nosql']['used'] + execute 'database migration' do + command "ceilometer-dbsync #{conf_switch}" + end +end + +platform = node['openstack']['telemetry']['platform'] +platform['collector_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] + end +end + +# temp fix for collector init not installing properly ubuntu +# See https://bugs.launchpad.net/cloud-archive/+bug/1221945 +if node['platform'] == 'ubuntu' + init_script = '/etc/init/ceilometer-collector.conf' + execute 'fix init script' do + command "cp #{init_script}.dpkg-new #{init_script}" + not_if { ::File.exists?(init_script) } + end +end + +service 'ceilometer-collector' do + service_name platform['collector_service'] + supports status: true, restart: true + subscribes :restart, "template[#{node['openstack']['telemetry']['conf']}]" + + action [:enable, :start] +end diff --git a/chef/cookbooks/openstack-telemetry/recipes/common.rb b/chef/cookbooks/openstack-telemetry/recipes/common.rb new file mode 100644 index 0000000..0b7ee3c --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/recipes/common.rb @@ -0,0 +1,104 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-telemetry +# Recipe:: common +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, Craig Tracey +# Copyright 2013, SUSE Linux GmbH +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +if node['openstack']['telemetry']['syslog']['use'] + include_recipe 'openstack-common::logging' +end + +platform = node['openstack']['telemetry']['platform'] + +db_type = node['openstack']['db']['telemetry']['service_type'] +platform["#{db_type}_python_packages"].each do |pkg| + package pkg do + action :install + end +end + +platform['common_packages'].each do |pkg| + package pkg do + options platform['package_overrides'] + end +end + +mq_service_type = node['openstack']['mq']['telemetry']['service_type'] + +if mq_service_type == 'rabbitmq' + mq_password = get_password 'user', node['openstack']['mq']['telemetry']['rabbit']['userid'] +elsif mq_service_type == 'qpid' + mq_password = get_password 'user', node['openstack']['mq']['telemetry']['qpid']['username'] +end + +db_user = node['openstack']['db']['telemetry']['username'] +db_pass = get_password 'db', 'ceilometer' +db_uri = db_uri('telemetry', db_user, db_pass).to_s + +service_user = node['openstack']['telemetry']['service_user'] +service_pass = get_password 'service', 'openstack-ceilometer' +service_tenant = node['openstack']['telemetry']['service_tenant_name'] + +identity_endpoint = endpoint 'identity-api' +identity_admin_endpoint = endpoint 'identity-admin' +image_endpoint = endpoint 'image-api' +telemetry_api_bind = endpoint 'telemetry-api-bind' + +auth_uri = auth_uri_transform identity_endpoint.to_s, node['openstack']['telemetry']['api']['auth']['version'] + +Chef::Log.debug("openstack-telemetry::common:service_user|#{service_user}") +Chef::Log.debug("openstack-telemetry::common:service_tenant|#{service_tenant}") +Chef::Log.debug("openstack-telemetry::common:identity_endpoint|#{identity_endpoint.to_s}") + +metering_secret = get_secret 'openstack_metering_secret' + +directory node['openstack']['telemetry']['conf_dir'] do + owner node['openstack']['telemetry']['user'] + group node['openstack']['telemetry']['group'] + mode 00750 + + action :create +end + +template node['openstack']['telemetry']['conf'] do + source 'ceilometer.conf.erb' + owner node['openstack']['telemetry']['user'] + group node['openstack']['telemetry']['group'] + mode 00640 + + variables( + auth_uri: auth_uri, + database_connection: db_uri, + image_endpoint: image_endpoint, + identity_endpoint: identity_endpoint, + identity_admin_endpoint: identity_admin_endpoint, + mq_service_type: mq_service_type, + mq_password: mq_password, + service_pass: service_pass, + service_tenant_name: service_tenant, + service_user: service_user, + metering_secret: metering_secret, + api_bind_host: telemetry_api_bind.host, + api_bind_port: telemetry_api_bind.port + ) +end diff --git a/chef/cookbooks/openstack-telemetry/recipes/identity_registration.rb b/chef/cookbooks/openstack-telemetry/recipes/identity_registration.rb new file mode 100644 index 0000000..a6796cd --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/recipes/identity_registration.rb @@ -0,0 +1,88 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-telemetry +# Recipe:: identity_registration +# +# Copyright 2013, AT&T Services, Inc. +# +# 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. +# + +require 'uri' + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +api_endpoint = endpoint 'telemetry-api' +identity_admin_endpoint = endpoint 'identity-admin' +bootstrap_token = get_secret 'openstack_identity_bootstrap_token' +auth_uri = ::URI.decode identity_admin_endpoint.to_s +service_pass = get_password 'service', 'openstack-ceilometer' +service_user = node['openstack']['telemetry']['service_user'] +service_role = node['openstack']['telemetry']['service_role'] +service_tenant_name = node['openstack']['telemetry']['service_tenant_name'] + +# Register Service Tenant +openstack_identity_register 'Register Service Tenant' do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + tenant_description 'Service Tenant' + + action :create_tenant +end + +# Register Service User +openstack_identity_register 'Register Service User' do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + user_name service_user + user_pass service_pass + + action :create_user +end + +# Grant Admin role to Service User for Service Tenant +openstack_identity_register "Grant 'admin' Role to Service User for Service Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + user_name service_user + role_name service_role + + action :grant_role +end + +openstack_identity_register 'Register Metering Service' do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name 'ceilometer' + service_type 'metering' + service_description 'Ceilometer Service' + + action :create_service +end + +openstack_identity_register 'Register Metering Endpoint' do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type 'metering' + endpoint_region node['openstack']['telemetry']['region'] + endpoint_adminurl ::URI.decode api_endpoint.to_s + endpoint_internalurl ::URI.decode api_endpoint.to_s + endpoint_publicurl ::URI.decode api_endpoint.to_s + + action :create_endpoint +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-central-rhel_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-central-rhel_spec.rb new file mode 100644 index 0000000..6ad7c7a --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-central-rhel_spec.rb @@ -0,0 +1,22 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-central' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the agent-central package' do + expect(chef_run).to install_package 'openstack-ceilometer-central' + end + + it 'starts the agent-central service' do + expect(chef_run).to start_service 'openstack-ceilometer-central' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-central-suse_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-central-suse_spec.rb new file mode 100644 index 0000000..619bff1 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-central-suse_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-central' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs the agent-central package' do + expect(chef_run).to install_package 'openstack-ceilometer-agent-central' + end + + it 'starts the agent-central service' do + expect(chef_run).to start_service 'openstack-ceilometer-agent-central' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-central_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-central_spec.rb new file mode 100644 index 0000000..e028e1b --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-central_spec.rb @@ -0,0 +1,29 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-central' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the agent-central package' do + expect(chef_run).to install_package 'ceilometer-agent-central' + end + + it 'starts and enables the agent-central service' do + expect(chef_run).to enable_service('ceilometer-agent-central') + expect(chef_run).to start_service('ceilometer-agent-central') + end + + describe 'ceilometer-agent-central' do + it 'subscribes to its config file' do + expect(chef_run.service('ceilometer-agent-central')).to subscribe_to('template[/etc/ceilometer/ceilometer.conf]').delayed + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-compute-rhel_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-compute-rhel_spec.rb new file mode 100644 index 0000000..319bae5 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-compute-rhel_spec.rb @@ -0,0 +1,22 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-compute' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the agent-compute package' do + expect(chef_run).to install_package 'openstack-ceilometer-compute' + end + + it 'starts ceilometer-agent-compute service' do + expect(chef_run).to start_service('openstack-ceilometer-compute') + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-compute-suse_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-compute-suse_spec.rb new file mode 100644 index 0000000..2b35635 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-compute-suse_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-compute' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs the agent-compute package' do + expect(chef_run).to install_package 'openstack-ceilometer-agent-compute' + end + + it 'starts the agent-compute service' do + expect(chef_run).to start_service 'openstack-ceilometer-agent-compute' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-compute_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-compute_spec.rb new file mode 100644 index 0000000..5d3593e --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-compute_spec.rb @@ -0,0 +1,29 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-compute' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the agent-compute package' do + expect(chef_run).to install_package 'ceilometer-agent-compute' + end + + it 'enables and starts the ceilometer-agent-compute service' do + expect(chef_run).to enable_service('ceilometer-agent-compute') + expect(chef_run).to start_service('ceilometer-agent-compute') + end + + describe 'ceilometer-agent-compute' do + it 'subscribes to its config file' do + expect(chef_run.service('ceilometer-agent-compute')).to subscribe_to('template[/etc/ceilometer/ceilometer.conf]').delayed + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-notification-rhel_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-notification-rhel_spec.rb new file mode 100644 index 0000000..ee78b9a --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-notification-rhel_spec.rb @@ -0,0 +1,22 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-notification' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the agent-notification package' do + expect(chef_run).to install_package 'openstack-ceilometer-collector' + end + + it 'starts the agent-notification service' do + expect(chef_run).to start_service 'openstack-ceilometer-notification' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-notification-suse_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-notification-suse_spec.rb new file mode 100644 index 0000000..deeb877 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-notification-suse_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-notification' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs the agent-notification package' do + expect(chef_run).to install_package 'openstack-ceilometer-agent-notification' + end + + it 'starts the agent-notification service' do + expect(chef_run).to start_service 'openstack-ceilometer-agent-notification' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/agent-notification_spec.rb b/chef/cookbooks/openstack-telemetry/spec/agent-notification_spec.rb new file mode 100644 index 0000000..8e8d7b2 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/agent-notification_spec.rb @@ -0,0 +1,29 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::agent-notification' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the agent-notification package' do + expect(chef_run).to install_package 'ceilometer-agent-notification' + end + + it 'enables and starts ceilometer-agent-notification service' do + expect(chef_run).to enable_service('ceilometer-agent-notification') + expect(chef_run).to start_service('ceilometer-agent-notification') + end + + describe 'ceilometer-agent-notification' do + it 'subscribes to its config file' do + expect(chef_run.service('ceilometer-agent-notification')).to subscribe_to('template[/etc/ceilometer/ceilometer.conf]').delayed + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator-rhel_spec.rb b/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator-rhel_spec.rb new file mode 100644 index 0000000..cdc21fa --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator-rhel_spec.rb @@ -0,0 +1,22 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::alarm-evaluator' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the alarm-evaluator package' do + expect(chef_run).to install_package 'openstack-ceilometer-alarm' + end + + it 'starts the alarm-evaluator service' do + expect(chef_run).to start_service 'openstack-ceilometer-alarm-evaluator' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator-suse_spec.rb b/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator-suse_spec.rb new file mode 100644 index 0000000..680f1d7 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator-suse_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::alarm-evaluator' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs the alarm-evaluator package' do + expect(chef_run).to install_package 'openstack-ceilometer-alarm-evaluator' + end + + it 'starts the alarm-evaluator service' do + expect(chef_run).to start_service 'openstack-ceilometer-alarm-evaluator' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator_spec.rb b/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator_spec.rb new file mode 100644 index 0000000..3aac374 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/alarm-evaluator_spec.rb @@ -0,0 +1,29 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::alarm-evaluator' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the alarm-evaluator package' do + expect(chef_run).to install_package 'ceilometer-alarm-evaluator' + end + + it 'starts and enables the alarm-evaluator service' do + expect(chef_run).to enable_service('ceilometer-alarm-evaluator') + expect(chef_run).to start_service('ceilometer-alarm-evaluator') + end + + describe 'ceilometer-alarm-evaluator' do + it 'subscribes to its config file' do + expect(chef_run.service('ceilometer-alarm-evaluator')).to subscribe_to('template[/etc/ceilometer/ceilometer.conf]').delayed + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/alarm-notifier-rhel_spec.rb b/chef/cookbooks/openstack-telemetry/spec/alarm-notifier-rhel_spec.rb new file mode 100644 index 0000000..97c7822 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/alarm-notifier-rhel_spec.rb @@ -0,0 +1,22 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::alarm-notifier' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the alarm-notifier package' do + expect(chef_run).to install_package 'openstack-ceilometer-alarm' + end + + it 'starts the alarm-notifier service' do + expect(chef_run).to start_service 'openstack-ceilometer-alarm-notifier' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/alarm-notifier-suse_spec.rb b/chef/cookbooks/openstack-telemetry/spec/alarm-notifier-suse_spec.rb new file mode 100644 index 0000000..9e825f9 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/alarm-notifier-suse_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::alarm-notifier' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs the alarm-notifier package' do + expect(chef_run).to install_package 'openstack-ceilometer-alarm-notifier' + end + + it 'starts the alarm-notifier service' do + expect(chef_run).to start_service 'openstack-ceilometer-alarm-notifier' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/alarm-notifier_spec.rb b/chef/cookbooks/openstack-telemetry/spec/alarm-notifier_spec.rb new file mode 100644 index 0000000..9d5d4ee --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/alarm-notifier_spec.rb @@ -0,0 +1,29 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::alarm-notifier' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the alarm-notifier package' do + expect(chef_run).to install_package 'ceilometer-alarm-notifier' + end + + it 'starts and enables the alarm-notifier service' do + expect(chef_run).to enable_service('ceilometer-alarm-notifier') + expect(chef_run).to start_service('ceilometer-alarm-notifier') + end + + describe 'ceilometer-alarm-notifier' do + it 'subscribes to its config file' do + expect(chef_run.service('ceilometer-alarm-notifier')).to subscribe_to('template[/etc/ceilometer/ceilometer.conf]').delayed + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/api-rhel_spec.rb b/chef/cookbooks/openstack-telemetry/spec/api-rhel_spec.rb new file mode 100644 index 0000000..96f6b9b --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/api-rhel_spec.rb @@ -0,0 +1,30 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::api' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'creates the /var/cache/ceilometer directory' do + expect(chef_run).to create_directory('/var/cache/ceilometer').with( + user: 'ceilometer', + group: 'ceilometer', + mode: 0700 + ) + end + + it 'installs the api package' do + expect(chef_run).to install_package('openstack-ceilometer-api') + end + + it 'starts api service' do + expect(chef_run).to start_service('openstack-ceilometer-api') + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/api-suse_spec.rb b/chef/cookbooks/openstack-telemetry/spec/api-suse_spec.rb new file mode 100644 index 0000000..ba2726d --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/api-suse_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::api' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs the api package' do + expect(chef_run).to install_package('openstack-ceilometer-api') + end + + it 'starts api service' do + expect(chef_run).to start_service('openstack-ceilometer-api') + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/api_spec.rb b/chef/cookbooks/openstack-telemetry/spec/api_spec.rb new file mode 100644 index 0000000..0366eb4 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/api_spec.rb @@ -0,0 +1,37 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::api' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'creates the /var/cache/ceilometer directory' do + expect(chef_run).to create_directory('/var/cache/ceilometer').with( + user: 'ceilometer', + group: 'ceilometer', + mode: 0700 + ) + end + + it 'installs the api package' do + expect(chef_run).to install_package 'ceilometer-api' + end + + it 'enables and starts the api service' do + expect(chef_run).to enable_service('ceilometer-api') + expect(chef_run).to start_service('ceilometer-api') + end + + describe 'ceilometer-api' do + it 'subscribes to its config file' do + expect(chef_run.service('ceilometer-api')).to subscribe_to('template[/etc/ceilometer/ceilometer.conf]').delayed + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/client-redhat_spec.rb b/chef/cookbooks/openstack-telemetry/spec/client-redhat_spec.rb new file mode 100644 index 0000000..2439262 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/client-redhat_spec.rb @@ -0,0 +1,15 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::client' do + describe 'redhat' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + it 'installs packages' do + expect(chef_run).to upgrade_package('python-ceilometerclient') + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/client_spec.rb b/chef/cookbooks/openstack-telemetry/spec/client_spec.rb new file mode 100644 index 0000000..a7579e4 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/client_spec.rb @@ -0,0 +1,16 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::client' do + describe 'ubuntu' do + + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + it 'installs packages' do + expect(chef_run).to upgrade_package('python-ceilometerclient') + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/collector-rhel_spec.rb b/chef/cookbooks/openstack-telemetry/spec/collector-rhel_spec.rb new file mode 100644 index 0000000..6c8837f --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/collector-rhel_spec.rb @@ -0,0 +1,27 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::collector' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'executes ceilometer dbsync' do + command = 'ceilometer-dbsync --config-file /etc/ceilometer/ceilometer.conf' + expect(chef_run).to run_execute command + end + + it 'installs the collector package' do + expect(chef_run).to install_package('openstack-ceilometer-collector') + end + + it 'starts collector service' do + expect(chef_run).to start_service('openstack-ceilometer-collector') + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/collector-suse_spec.rb b/chef/cookbooks/openstack-telemetry/spec/collector-suse_spec.rb new file mode 100644 index 0000000..fc52e17 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/collector-suse_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::collector' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs the collector package' do + expect(chef_run).to install_package 'openstack-ceilometer-collector' + end + + it 'starts the collector service' do + expect(chef_run).to start_service 'openstack-ceilometer-collector' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/collector_spec.rb b/chef/cookbooks/openstack-telemetry/spec/collector_spec.rb new file mode 100644 index 0000000..0582949 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/collector_spec.rb @@ -0,0 +1,44 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::collector' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + include_examples 'expect-runs-common-recipe' + + it 'installs the collector package' do + expect(chef_run).to install_package 'ceilometer-collector' + end + + it 'executes ceilometer dbsync' do + command = 'ceilometer-dbsync --config-file /etc/ceilometer/ceilometer.conf' + expect(chef_run).to run_execute command + end + + it 'does not execute ceilometer dbsync when nosql database is used' do + node.set['openstack']['db']['telemetry']['nosql']['used'] = true + + expect(chef_run).not_to run_execute('execute[database migration]') + end + + it 'installs python-mysqldb' do + expect(chef_run).to install_package('python-mysqldb') + end + + it 'starts and enables the collector service' do + expect(chef_run).to enable_service('ceilometer-collector') + expect(chef_run).to start_service('ceilometer-collector') + end + + describe 'ceilometer-collector' do + it 'subscribes to its config file' do + expect(chef_run.service('ceilometer-collector')).to subscribe_to('template[/etc/ceilometer/ceilometer.conf]').delayed + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/common-rhel_spec.rb b/chef/cookbooks/openstack-telemetry/spec/common-rhel_spec.rb new file mode 100644 index 0000000..639fb90 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/common-rhel_spec.rb @@ -0,0 +1,33 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::common' do + describe 'rhel' do + let(:runner) { ChefSpec::Runner.new(REDHAT_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs mysql python packages by default' do + expect(chef_run).to install_package 'MySQL-python' + end + + it 'installs db2 python packages if explicitly told' do + node.set['openstack']['db']['telemetry']['service_type'] = 'db2' + ['python-ibm-db', 'python-ibm-db-sa'].each do |pkg| + expect(chef_run).to install_package pkg + end + end + + it 'installs postgresql python packages if explicitly told' do + node.set['openstack']['db']['telemetry']['service_type'] = 'postgresql' + expect(chef_run).to install_package 'python-psycopg2' + end + + it 'installs the common package' do + expect(chef_run).to install_package 'openstack-ceilometer-common' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/common-suse_spec.rb b/chef/cookbooks/openstack-telemetry/spec/common-suse_spec.rb new file mode 100644 index 0000000..5ee7227 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/common-suse_spec.rb @@ -0,0 +1,26 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::common' do + describe 'suse' do + let(:runner) { ChefSpec::Runner.new(SUSE_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'installs mysql python packages by default' do + expect(chef_run).to install_package 'python-mysql' + end + + it 'installs postgresql python packages if explicitly told' do + node.set['openstack']['db']['telemetry']['service_type'] = 'postgresql' + expect(chef_run).to install_package 'python-psycopg2' + end + + it 'installs the common package' do + expect(chef_run).to install_package 'openstack-ceilometer' + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/common_spec.rb b/chef/cookbooks/openstack-telemetry/spec/common_spec.rb new file mode 100644 index 0000000..6a91396 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/common_spec.rb @@ -0,0 +1,181 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::common' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + context 'with logging enabled' do + before do + node.set['openstack']['telemetry']['syslog']['use'] = true + end + + it 'runs logging recipe' do + expect(chef_run).to include_recipe 'openstack-common::logging' + end + end + + it 'installs mysql python packages by default' do + expect(chef_run).to install_package 'python-mysqldb' + end + + it 'installs postgresql python packages if explicitly told' do + node.set['openstack']['db']['telemetry']['service_type'] = 'postgresql' + expect(chef_run).to install_package 'python-psycopg2' + end + + it 'installs the common package' do + expect(chef_run).to install_package 'ceilometer-common' + end + + describe '/etc/ceilometer' do + let(:dir) { chef_run.directory('/etc/ceilometer') } + + it 'creates the /etc/ceilometer directory' do + expect(chef_run).to create_directory(dir.name).with( + user: 'ceilometer', + group: 'ceilometer', + mode: 0750 + ) + end + end + + describe 'ceilometer.conf' do + let(:file) { chef_run.template('/etc/ceilometer/ceilometer.conf') } + + it 'creates the file' do + expect(chef_run).to create_template(file.name).with( + user: 'ceilometer', + group: 'ceilometer', + mode: 0640 + ) + end + + context 'rabbit mq backend' do + before do + node.set['openstack']['mq']['telemetry']['service_type'] = 'rabbitmq' + end + + it 'has default rabbit_* options set' do + [ + /^rabbit_userid = guest$/, + /^rabbit_password = mq-pass$/, + /^rabbit_port = 5672$/, + /^rabbit_host = 127.0.0.1$/, + /^rabbit_virtual_host = \/$/, + /^rabbit_use_ssl = false$/, + %r{^auth_uri = http://127.0.0.1:5000/v2.0$}, + /^auth_host = 127.0.0.1$/, + /^auth_port = 35357$/, + /^auth_protocol = http$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + end + + context 'qpid mq backend' do + before do + node.set['openstack']['mq']['telemetry']['service_type'] = 'qpid' + node.set['openstack']['mq']['telemetry']['qpid']['username'] = 'guest' + end + + it 'has default qpid_* options set' do + [ + /^qpid_hostname=127.0.0.1$/, + /^qpid_port=5672$/, + /^qpid_username=guest$/, + /^qpid_password=mq-pass$/, + /^qpid_sasl_mechanisms=$/, + /^qpid_reconnect=true$/, + /^qpid_reconnect_timeout=0$/, + /^qpid_reconnect_limit=0$/, + /^qpid_reconnect_interval_min=0$/, + /^qpid_reconnect_interval_max=0$/, + /^qpid_reconnect_interval_max=0$/, + /^qpid_reconnect_interval=0$/, + /^qpid_heartbeat=60$/, + /^qpid_protocol=tcp$/, + /^qpid_tcp_nodelay=true$/ + ].each do |line| + expect(chef_run).to render_file(file.name).with_content(line) + end + end + end + + context 'has keystone authtoken configuration' do + it 'has auth_uri' do + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote('auth_uri = http://127.0.0.1:5000/v2.0')}$/) + end + + it 'has auth_host' do + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote('auth_host = 127.0.0.1')}$/) + end + + it 'has auth_port' do + expect(chef_run).to render_file(file.name).with_content( + /^auth_port = 35357$/) + end + + it 'has auth_protocol' do + expect(chef_run).to render_file(file.name).with_content( + /^auth_protocol = http$/) + end + + it 'has no auth_version' do + expect(chef_run).not_to render_file(file.name).with_content( + /^auth_version = v2.0$/) + end + + it 'has admin_tenant_name' do + expect(chef_run).to render_file(file.name).with_content( + /^admin_tenant_name = service$/) + end + + it 'has admin_user' do + expect(chef_run).to render_file(file.name).with_content( + /^admin_user = ceilometer$/) + end + + it 'has admin_password' do + expect(chef_run).to render_file(file.name).with_content( + /^admin_password = ceilometer-pass$/) + end + + it 'has signing_dir' do + expect(chef_run).to render_file(file.name).with_content( + /^#{Regexp.quote('signing_dir = /var/cache/ceilometer/api')}$/) + end + end + + it 'has metering secret' do + r = /^metering_secret = metering_secret$/ + expect(chef_run).to render_file(file.name).with_content(r) + end + + it 'has hypervisor inspector' do + r = /^hypervisor_inspector = libvirt$/ + expect(chef_run).to render_file(file.name).with_content(r) + end + + it 'has bind_host set' do + node.set['openstack']['endpoints']['telemetry-api-bind']['host'] = '1.1.1.1' + expect(chef_run).to render_file(file.name).with_content( + /^host = 1.1.1.1$/) + end + + it 'has bind_port set' do + node.set['openstack']['endpoints']['telemetry-api-bind']['port'] = '9999' + expect(chef_run).to render_file(file.name).with_content( + /^port = 9999$/) + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-telemetry/spec/identity_registration_spec.rb new file mode 100644 index 0000000..4a591e0 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/identity_registration_spec.rb @@ -0,0 +1,83 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-telemetry::identity_registration' do + describe 'ubuntu' do + let(:runner) { ChefSpec::Runner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'telemetry-stubs' + + it 'registers service tenant' do + expect(chef_run).to create_tenant_openstack_identity_register( + 'Register Service Tenant' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + tenant_description: 'Service Tenant' + ) + end + + it 'registers service user' do + expect(chef_run).to create_user_openstack_identity_register( + 'Register Service User' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'ceilometer', + user_pass: 'ceilometer-pass' + ) + end + + it 'grants admin role to service user for service tenant' do + expect(chef_run).to grant_role_openstack_identity_register( + "Grant 'admin' Role to Service User for Service Tenant" + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + tenant_name: 'service', + user_name: 'ceilometer', + role_name: 'admin' + ) + end + + it 'registers metering service' do + expect(chef_run).to create_service_openstack_identity_register( + 'Register Metering Service' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_name: 'ceilometer', + service_type: 'metering' + ) + end + + context 'registers metering endpoint' do + it 'with default values' do + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Metering Endpoint' + ).with( + auth_uri: 'http://127.0.0.1:35357/v2.0', + bootstrap_token: 'bootstrap-token', + service_type: 'metering', + endpoint_region: 'RegionOne', + endpoint_adminurl: 'http://127.0.0.1:8777', + endpoint_internalurl: 'http://127.0.0.1:8777', + endpoint_publicurl: 'http://127.0.0.1:8777' + ) + end + + it 'with custom region override' do + node.set['openstack']['telemetry']['region'] = 'meteringRegion' + + expect(chef_run).to create_endpoint_openstack_identity_register( + 'Register Metering Endpoint' + ).with(endpoint_region: 'meteringRegion') + end + end + end +end diff --git a/chef/cookbooks/openstack-telemetry/spec/spec_helper.rb b/chef/cookbooks/openstack-telemetry/spec/spec_helper.rb new file mode 100644 index 0000000..c04f453 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/spec/spec_helper.rb @@ -0,0 +1,52 @@ +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' + +ChefSpec::Coverage.start! { add_filter 'openstack-telemetry' } + +require 'chef/application' + +LOG_LEVEL = :fatal +SUSE_OPTS = { + platform: 'suse', + version: '11.03', + log_level: ::LOG_LEVEL +} +REDHAT_OPTS = { + platform: 'redhat', + version: '6.3', + log_level: ::LOG_LEVEL +} +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '12.04', + log_level: ::LOG_LEVEL +} + +shared_context 'telemetry-stubs' do + before do + Chef::Recipe.any_instance.stub(:memcached_servers).and_return([]) + Chef::Recipe.any_instance.stub(:get_password) + .with('db', anything) + .and_return('') + Chef::Recipe.any_instance.stub(:get_password) + .with('service', 'openstack-ceilometer') + .and_return('ceilometer-pass') + Chef::Recipe.any_instance.stub(:get_password) + .with('user', 'guest') + .and_return('mq-pass') + Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_identity_bootstrap_token') + .and_return('bootstrap-token') + Chef::Recipe.any_instance.stub(:get_secret) + .with('openstack_metering_secret') + .and_return('metering_secret') + Chef::Application.stub(:fatal!) + end +end + +shared_examples 'expect-runs-common-recipe' do + it 'runs common recipe' do + expect(chef_run).to include_recipe 'openstack-telemetry::common' + end +end diff --git a/chef/cookbooks/openstack-telemetry/templates/default/ceilometer.conf.erb b/chef/cookbooks/openstack-telemetry/templates/default/ceilometer.conf.erb new file mode 100644 index 0000000..ebfb464 --- /dev/null +++ b/chef/cookbooks/openstack-telemetry/templates/default/ceilometer.conf.erb @@ -0,0 +1,76 @@ +[DEFAULT] +os_auth_url = <%= @auth_uri %> +os_tenant_name = <%= @service_tenant_name %> +os_password = <%= @service_pass %> +os_username = <%= @service_user %> +policy_file = /etc/ceilometer/policy.json +verbose = <%= node["openstack"]["telemetry"]["verbose"] %> +debug = <%= node["openstack"]["telemetry"]["debug"] %> +<% if node["openstack"]["telemetry"]["hypervisor_inspector"] %> +hypervisor_inspector = <%= node["openstack"]["telemetry"]["hypervisor_inspector"] %> +<% end %> +<% if node["openstack"]["telemetry"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% end %> + +notification_topics = notifications,glance_notifications +<% if @mq_service_type == "rabbitmq" %> +##### RABBITMQ ##### +rabbit_userid = <%= node["openstack"]["mq"]["telemetry"]["rabbit"]["userid"] %> +rabbit_password = <%= @mq_password %> +rabbit_port = <%= node["openstack"]["mq"]["telemetry"]["rabbit"]["port"] %> +rabbit_host = <%= node["openstack"]["mq"]["telemetry"]["rabbit"]["host"] %> +rabbit_virtual_host = <%= node["openstack"]["mq"]["telemetry"]["rabbit"]["vhost"] %> +rabbit_use_ssl = <%= node["openstack"]["mq"]["telemetry"]["rabbit"]["use_ssl"] %> +rpc_backend = ceilometer.openstack.common.rpc.impl_kombu +<% end %> + + +<% if @mq_service_type == "qpid" %> +##### QPID ##### +rpc_backend=ceilometer.openstack.common.rpc.impl_qpid +qpid_hostname=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["host"] %> +qpid_port=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["port"] %> + +qpid_password=<%= @mq_password %> +qpid_username=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["username"] %> +qpid_sasl_mechanisms=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["sasl_mechanisms"] %> +qpid_reconnect=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["reconnect"] %> +qpid_reconnect_timeout=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["reconnect_timeout"] %> +qpid_reconnect_limit=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["reconnect_limit"] %> +qpid_reconnect_interval_min=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["reconnect_interval_min"] %> +qpid_reconnect_interval_max=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["reconnect_interval_max"] %> +qpid_reconnect_interval=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["reconnect_interval"] %> +qpid_heartbeat=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["heartbeat"] %> +# qpid protocol. default 'tcp'. set to 'ssl' to enable SSL +qpid_protocol=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["protocol"] %> +qpid_tcp_nodelay=<%= node["openstack"]["mq"]["telemetry"]["qpid"]["tcp_nodelay"] %> + +<% end %> + +glance_registry_host = <%= @image_endpoint.host %> +periodic_interval = <%= node["openstack"]["telemetry"]["periodic_interval"] %> + +[database] +connection=<%= @database_connection %> + +[api] +host = <%= @api_bind_host %> +port = <%= @api_bind_port %> + +[keystone_authtoken] +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory +auth_uri = <%= @auth_uri %> +auth_host = <%= @identity_admin_endpoint.host %> +auth_port = <%= @identity_admin_endpoint.port %> +auth_protocol = <%= @identity_admin_endpoint.scheme %> +<% if node['openstack']['telemetry']['api']['auth']['version'] != 'v2.0' %> +auth_version = <%= node['openstack']['telemetry']['api']['auth']['version'] %> +<% end %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%= @service_user %> +admin_password = <%= @service_pass %> +signing_dir = <%= node["openstack"]["telemetry"]["api"]["auth"]["cache_dir"] %> + +[publisher_rpc] +metering_secret = <%= @metering_secret %> diff --git a/chef/cookbooks/pacman/.gitignore b/chef/cookbooks/pacman/.gitignore new file mode 100644 index 0000000..54e2457 --- /dev/null +++ b/chef/cookbooks/pacman/.gitignore @@ -0,0 +1 @@ +metadata.json diff --git a/chef/cookbooks/pacman/.travis.yml b/chef/cookbooks/pacman/.travis.yml new file mode 100644 index 0000000..be547a6 --- /dev/null +++ b/chef/cookbooks/pacman/.travis.yml @@ -0,0 +1,8 @@ +rvm: + - 1.9.3 + - 2.0.0 +#before_script: +# - bundle exec berks install +script: + - bundle exec foodcritic -f any . --tags ~FC048 + - bundle exec rspec --color --format progress diff --git a/chef/cookbooks/pacman/CHANGELOG.md b/chef/cookbooks/pacman/CHANGELOG.md new file mode 100644 index 0000000..80053b5 --- /dev/null +++ b/chef/cookbooks/pacman/CHANGELOG.md @@ -0,0 +1,20 @@ +## v1.1.1: + +- Added --noconfirm to to makepkg runs to not prompt on dependencies (thanks [dvolker](https://github.com/dvolker)) + +## v1.1.0: + +### Bug + +- Added package namespaces to AUR package installation + +## v1.0.4: + +### Bug + +- [COOK-2971]: pacman cookbook has foodcritic failure + +## v1.0.2: + +* [COOK-1018] - aur provider fails when a package is of 'any' cpu + type. diff --git a/chef/cookbooks/apt/CONTRIBUTING b/chef/cookbooks/pacman/CONTRIBUTING similarity index 100% rename from chef/cookbooks/apt/CONTRIBUTING rename to chef/cookbooks/pacman/CONTRIBUTING diff --git a/chef/cookbooks/pacman/Gemfile b/chef/cookbooks/pacman/Gemfile new file mode 100644 index 0000000..5815a08 --- /dev/null +++ b/chef/cookbooks/pacman/Gemfile @@ -0,0 +1,11 @@ +source 'https://rubygems.org' + +#gem 'berkshelf', '~> 2.0' +gem 'chefspec', '~> 3.0' +gem 'foodcritic', '~> 3.0' +gem 'rubocop', '~> 0.12' + +group :integration do + gem 'test-kitchen', '~> 1.0.0.beta' + gem 'kitchen-vagrant', '~> 0.11' +end diff --git a/chef/cookbooks/pacman/Gemfile.lock b/chef/cookbooks/pacman/Gemfile.lock new file mode 100644 index 0000000..6a47283 --- /dev/null +++ b/chef/cookbooks/pacman/Gemfile.lock @@ -0,0 +1,113 @@ +GEM + remote: https://rubygems.org/ + specs: + ast (1.1.0) + buff-ignore (1.1.1) + chef (11.6.2) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.3) + mixlib-config (~> 1.1, >= 1.1.2) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.1) + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0, < 7.0.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (3.0.2) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) + diff-lcs (1.2.5) + erubis (2.7.0) + fauxhai (2.0.0) + net-ssh + ohai + foodcritic (3.0.3) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rake + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + highline (1.6.20) + ipaddress (0.8.0) + json (1.7.7) + kitchen-vagrant (0.11.3) + test-kitchen (~> 1.0.0.beta) + mime-types (2.0) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.2.0) + multi_json (1.8.2) + net-scp (1.1.2) + net-ssh (>= 2.6.5) + net-ssh (2.7.0) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nokogiri (1.5.10) + ohai (6.20.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu (~> 2.5.2) + yajl-ruby + parser (2.0.0) + ast (~> 1.1) + slop (~> 3.4, >= 3.4.5) + polyglot (0.3.3) + powerpack (0.0.9) + rainbow (1.1.4) + rake (10.1.0) + rest-client (1.6.7) + mime-types (>= 1.16) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.7) + rspec-expectations (2.14.4) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.4) + rubocop (0.15.0) + parser (~> 2.0) + powerpack (~> 0.0.6) + rainbow (>= 1.1.4) + safe_yaml (0.9.7) + slop (3.4.7) + systemu (2.5.2) + test-kitchen (1.0.0.beta.4) + buff-ignore (~> 1.1) + mixlib-shellout (~> 1.2) + net-scp (~> 1.1) + net-ssh (~> 2.7) + safe_yaml (~> 0.9) + thor (~> 0.18) + thor (0.18.1) + treetop (1.4.15) + polyglot + polyglot (>= 0.3.1) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + chefspec (~> 3.0) + foodcritic (~> 3.0) + kitchen-vagrant (~> 0.11) + rubocop (~> 0.12) + test-kitchen (~> 1.0.0.beta) diff --git a/chef/cookbooks/aws/LICENSE b/chef/cookbooks/pacman/LICENSE similarity index 100% rename from chef/cookbooks/aws/LICENSE rename to chef/cookbooks/pacman/LICENSE diff --git a/chef/cookbooks/pacman/README.md b/chef/cookbooks/pacman/README.md new file mode 100644 index 0000000..6185d5e --- /dev/null +++ b/chef/cookbooks/pacman/README.md @@ -0,0 +1,68 @@ +[![Travis CI](https://travis-ci.org/jesseadams/pacman.png)](https://travis-ci.org/jesseadams/pacman) + +DESCRIPTION +=========== + +Refreshes the pacman package cache from the FTP servers and provides LWRPs related to pacman + +REQUIREMENTS +============ + +Platform: ArchLinux. Pacman is not relevant on other platforms. + +RESOURCES +========= + +`pacman_group` +-------------- + +Use the `pacman_group` resource to install or remove pacman package groups. Note that at this time the LWRP will check if the group is installed but doesn't do a lot of error checking or handling. File a ticket on the COOK project at tickets.opscode.com for improvements and feature requests. + +The `options` parameter can be used to pass arbitrary options to the pacman command. + +`pacman_aur` +------------ + +Use the `pacman_aur` resource to install packages from ArchLinux's AUR repository. + +### Actions: + +* :build - Builds the package. +* :install - Installs the built package. + +### Parameters: + +* version - hardcode a version +* builddir - specify an alternate build directory, defaults to `Chef::Config[:file_cache_path]/builds`. +* options - pass arbitrary options to the pacman command. +* `pkgbuild_src` - whether to use an included PKGBUILD file, put the PKGBUILD file in in the `files/default` directory. +* patches - array of patch names, as files in `files/default` that should be applied for the package. + +http://aur.archlinux.org/ + +USAGE +===== + +Include `recipe[pacman]` early in the run list, preferably first, to ensure that the package caches are updated before trying to install new packages. + + +LICENSE AND AUTHOR +================== + +Author:: Joshua Timberman () + +Maintainer:: Jesse R. Adams (jesse techno geeks org) + +Copyright:: Opscode, Inc. () + +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. diff --git a/chef/cookbooks/pacman/metadata.rb b/chef/cookbooks/pacman/metadata.rb new file mode 100644 index 0000000..3495f39 --- /dev/null +++ b/chef/cookbooks/pacman/metadata.rb @@ -0,0 +1,7 @@ +name "pacman" +maintainer "Jesse R. Adams" +maintainer_email "jesse@techno-geeks.org" +license "Apache 2.0" +description "Updates package list for pacman and has LWRP for pacman groups" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.1.1" diff --git a/chef/cookbooks/pacman/providers/aur.rb b/chef/cookbooks/pacman/providers/aur.rb new file mode 100644 index 0000000..911081b --- /dev/null +++ b/chef/cookbooks/pacman/providers/aur.rb @@ -0,0 +1,138 @@ +# +# Cookbook Name:: pacman +# Provider:: aur +# +# Copyright:: 2010, Opscode, Inc +# +# 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. +# + +require 'chef/mixin/shell_out' +require 'chef/mixin/language' +include Chef::Mixin::ShellOut + +action :build do + get_pkg_version + aurfile = "#{new_resource.builddir}/#{new_resource.name}/#{new_resource.name}-#{new_resource.version}.pkg.tar.xz" + package_namespace = new_resource.name[0..1] + + Chef::Log.debug("Checking for #{aurfile}") + unless ::File.exists?(aurfile) + Chef::Log.debug("Creating build directory") + d = directory new_resource.builddir do + owner "root" + group "root" + mode 0755 + action :nothing + end + d.run_action(:create) + + Chef::Log.debug("Retrieving source for #{new_resource.name}") + r = remote_file "#{new_resource.builddir}/#{new_resource.name}.tar.gz" do + source "https://aur.archlinux.org/packages/#{package_namespace}/#{new_resource.name}/#{new_resource.name}.tar.gz" + owner "root" + group "root" + mode 0644 + action :nothing + end + r.run_action(:create_if_missing) + + Chef::Log.debug("Untarring source package for #{new_resource.name}") + e = execute "tar -xf #{new_resource.name}.tar.gz" do + cwd new_resource.builddir + action :nothing + end + e.run_action(:run) + + if new_resource.pkgbuild_src + Chef::Log.debug("Replacing PKGBUILD with custom version") + pkgb = cookbook_file "#{new_resource.builddir}/#{new_resource.name}/PKGBUILD" do + source "PKGBUILD" + owner "root" + group "root" + mode 0644 + action :nothing + end + pkgb.run_action(:create) + end + + if new_resource.patches.length > 0 + Chef::Log.debug("Adding new patches") + new_resource.patches.each do |patch| + pfile = cookbook_file ::File.join(new_resource.builddir, new_resource.name, patch) do + source patch + mode 0644 + action :nothing + end + pfile.run_action(:create) + end + end + + if new_resource.options + Chef::Log.debug("Appending #{new_resource.options} to configure command") + opt = Chef::Util::FileEdit.new("#{new_resource.builddir}/#{new_resource.name}/PKGBUILD") + opt.search_file_replace(/(.\/configure.+$)/, "\\1 #{new_resource.options}") + opt.write_file + end + + Chef::Log.debug("Building package #{new_resource.name}") + em = execute "makepkg -s --asroot --noconfirm" do + cwd ::File.join(new_resource.builddir, new_resource.name) + creates aurfile + action :nothing + end + em.run_action(:run) + new_resource.updated_by_last_action(true) + end +end + +action :install do + unless @aurpkg.exists + get_pkg_version + execute "install AUR package #{new_resource.name}-#{new_resource.version}" do + command "pacman -U --noconfirm --noprogressbar #{new_resource.builddir}/#{new_resource.name}/#{new_resource.name}-#{new_resource.version}.pkg.tar.xz" + end + new_resource.updated_by_last_action(true) + end +end + +def get_pkg_version + v = '' + r = '' + a = '' + if ::File.exists?("#{new_resource.builddir}/#{new_resource.name}/PKGBUILD") + ::File.open("#{new_resource.builddir}/#{new_resource.name}/PKGBUILD").each do |line| + v = line.split("=")[1].chomp if line =~ /^pkgver=/ + r = line.split("=")[1].chomp if line =~ /^pkgrel=/ + if line =~ /^arch/ + if line.match 'any' + a = 'any' + else + a = node.kernel.machine + end + end + end + Chef::Log.debug("Setting version of #{new_resource.name} to #{v}-#{r}-#{a}") + new_resource.version("#{v}-#{r}-#{a}") + end +end + +def load_current_resource + @aurpkg = Chef::Resource::PacmanAur.new(new_resource.name) + @aurpkg.package_name(new_resource.package_name) + + Chef::Log.debug("Checking pacman for #{new_resource.package_name}") + p = shell_out("pacman -Qi #{new_resource.package_name}") + exists = p.stdout.include?(new_resource.package_name) + @aurpkg.exists(exists) +end diff --git a/chef/cookbooks/pacman/providers/group.rb b/chef/cookbooks/pacman/providers/group.rb new file mode 100644 index 0000000..cf3a0a2 --- /dev/null +++ b/chef/cookbooks/pacman/providers/group.rb @@ -0,0 +1,50 @@ +# +# Cookbook Name:: pacman +# Provider:: group +# +# Copyright:: 2010, Opscode, Inc +# +# 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. +# + +require 'chef/mixin/shell_out' +require 'chef/mixin/language' +include Chef::Mixin::ShellOut + +action :install do + unless @pmgroup.exists + run_command_with_systems_locale( + :command => "pacman --sync --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" + ) + new_resource.updated_by_last_action(true) + end +end + +action :remove do + if @pmgroup.exists + run_command_with_systems_locale( + :command => "pacman --remove --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" + ) + new_resource.updated_by_last_action(true) + end +end + +def load_current_resource + @pmgroup = Chef::Resource::PacmanGroup.new(@new_resource.name) + @pmgroup.package_name(@new_resource.package_name) + + Chef::Log.debug("Checking pacman for #{@new_resource.package_name}") + p = shell_out("pacman -Qg #{@new_resource.package_name}") + exists = p.stdout.include?(@new_resource.package_name) + @pmgroup.exists(exists) +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/default.rb b/chef/cookbooks/pacman/recipes/default.rb similarity index 81% rename from chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/default.rb rename to chef/cookbooks/pacman/recipes/default.rb index bb80e15..b2a9ace 100644 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/default.rb +++ b/chef/cookbooks/pacman/recipes/default.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: apache2_test +# Cookbook Name:: pacman # Recipe:: default # -# Copyright 2012, Opscode, Inc. +# Copyright 2010, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,4 +17,8 @@ # limitations under the License. # -include_recipe "apache2::default" +e = execute "pacman -Sy" do + action :nothing +end + +e.run_action(:run) diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_openid.rb b/chef/cookbooks/pacman/resources/aur.rb similarity index 53% rename from chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_openid.rb rename to chef/cookbooks/pacman/resources/aur.rb index 9a2b352..6079bc2 100644 --- a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_openid.rb +++ b/chef/cookbooks/pacman/resources/aur.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: apache2_test -# Recipe:: mod_auth_openid +# Cookbook Name:: pacman +# Resource:: group # -# Copyright 2012, Opscode, Inc. +# Copyright:: 2010, Opscode, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,13 +17,14 @@ # limitations under the License. # -include_recipe "apache2::default" -include_recipe "apache2::mod_auth_openid" +actions :build, :install -directory "#{node['apache_test']['root_dir']}/secure" do - action :create -end +default_action :install -web_app "secure" do - template "auth_openid.conf.erb" -end +attribute :package_name, :name_attribute => true +attribute :version, :default => nil +attribute :builddir, :default => "#{Chef::Config[:file_cache_path]}/builds" +attribute :options, :kind_of => String +attribute :pkgbuild_src, :default => false +attribute :patches, :kind_of => Array, :default => [] +attribute :exists, :default => false diff --git a/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/instance.rb b/chef/cookbooks/pacman/resources/group.rb similarity index 67% rename from chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/instance.rb rename to chef/cookbooks/pacman/resources/group.rb index f7fe2ae..464e7c4 100644 --- a/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/instance.rb +++ b/chef/cookbooks/pacman/resources/group.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: memcached_test -# Recipe:: instance +# Cookbook Name:: pacman +# Resource:: group # -# Copyright 2013, Opscode, Inc. +# Copyright:: 2010, Opscode, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,12 +17,10 @@ # limitations under the License. # -include_recipe "memcached::default" +actions :install, :remove -m = resources("service[memcached]") -m.action :stop +default_action :install -memcached_instance "myproj" do - port 11212 - memory 128 -end +attribute :package_name, :name_attribute => true +attribute :options, :kind_of => String +attribute :exists, :default => false diff --git a/chef/cookbooks/pacman/spec/spec_helper.rb b/chef/cookbooks/pacman/spec/spec_helper.rb new file mode 100644 index 0000000..e69de29 diff --git a/chef/cookbooks/postgresql/.kitchen.yml b/chef/cookbooks/postgresql/.kitchen.yml deleted file mode 100644 index 0aa1305..0000000 --- a/chef/cookbooks/postgresql/.kitchen.yml +++ /dev/null @@ -1,144 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: - - recipe[apt] - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - -suites: -- name: default - run_list: - - recipe[minitest-handler] - - recipe[postgresql] - attributes: {} - -- name: contrib - run_list: - - recipe[postgresql::contrib] - attributes: - postgresql: - password: - postgres: "iloverandompasswordsbutthiswilldo" - -- name: apt-pgdg-client - run_list: - - recipe[minitest-handler] - - recipe[postgresql] - excludes: ["centos-5.9", "centos-6.4"] - attributes: - postgresql: - enable_pgdg_apt: true - version: "9.2" - client: - packages: ["postgresql-client-9.2", "libpq-dev"] - -- name: yum-pgdg-client - run_list: - - recipe[minitest-handler] - - recipe[postgresql] - excludes: ["ubuntu-10.04", "ubuntu-12.04", "debian-6.0.7"] - attributes: - postgresql: - enable_pgdg_yum: true - version: "9.2" - client: - packages: ["postgresql92"] - -- name: ruby - run_list: - - recipe[postgresql::ruby] - - recipe[minitest-handler] - attributes: {} - -- name: server - run_list: - - recipe[postgresql::ruby] - - recipe[minitest-handler] - - recipe[postgresql::server] - attributes: - postgresql: - password: - postgres: "iloverandompasswordsbutthiswilldo" - -- name: apt-pgdg-server - run_list: - - recipe[minitest-handler] - - recipe[postgresql::ruby] - - recipe[postgresql::server] - excludes: ["centos-5.9", "centos-6.4"] - attributes: - postgresql: - enable_pgdg_apt: true - version: "9.2" - server: - packages: ["postgresql-9.2"] - password: - postgres: "iloverandompasswordsbutthiswilldo" - config: - ssl_cert_file: "/etc/ssl/certs/ssl-cert-snakeoil.pem" - ssl_key_file: "/etc/ssl/private/ssl-cert-snakeoil.key" - -- name: yum-pgdg-server - run_list: - - recipe[minitest-handler] - - recipe[postgresql::ruby] - - recipe[postgresql::server] - excludes: ["ubuntu-10.04", "ubuntu-12.04", "debian-6.0.7"] - attributes: - postgresql: - enable_pgdg_yum: true - version: "9.2" - server: - packages: ["postgresql92-server"] - service_name: "postgresql-9.2" - password: - postgres: "iloverandompasswordsbutthiswilldo" - -- name: apt-pgdg-client-ruby - run_list: - - recipe[minitest-handler] - - recipe[postgresql] - - recipe[postgresql::ruby] - excludes: ["centos-5.9", "centos-6.4"] - attributes: - postgresql: - enable_pgdg_apt: true - version: "9.2" - client: - packages: ["postgresql-client-9.2", "libpq-dev"] - -- name: yum-pgdg-client-ruby - run_list: - - recipe[minitest-handler] - - recipe[postgresql] - - recipe[postgresql::ruby] - excludes: ["ubuntu-10.04", "ubuntu-12.04", "debian-6.0.7"] - attributes: - postgresql: - enable_pgdg_yum: true - version: "9.2" - client: - packages: ["postgresql92", "postgresql92-devel"] diff --git a/chef/cookbooks/postgresql/Berksfile b/chef/cookbooks/postgresql/Berksfile deleted file mode 100644 index 52dc4f4..0000000 --- a/chef/cookbooks/postgresql/Berksfile +++ /dev/null @@ -1,7 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook 'minitest-handler' -end diff --git a/chef/cookbooks/postgresql/CHANGELOG.md b/chef/cookbooks/postgresql/CHANGELOG.md index cdbb542..c0f2b63 100644 --- a/chef/cookbooks/postgresql/CHANGELOG.md +++ b/chef/cookbooks/postgresql/CHANGELOG.md @@ -3,6 +3,45 @@ postgresql Cookbook CHANGELOG This file is used to list changes made in each version of the postgresql cookbook. +v3.3.4 +------ +Testing + + +v3.3.2 +------ +- Testing maintainer transfer to Heavywater with Opscode as collaborator + + +v3.3.0 +------ +### Bug +- **[COOK-3851](https://tickets.opscode.com/browse/COOK-3851)** - Postgresql: reload after config change does not pick up certain configuration changes +- **[COOK-3611](https://tickets.opscode.com/browse/COOK-3611)** - unix_socket_directory does not exists in 9.3 +- **[COOK-2954](https://tickets.opscode.com/browse/COOK-2954)** - PostgreSQL installation ignores version attribute on CentOS >= 6 + + +v3.2.0 +------ +- [COOK-3717] Pgdg repositories improvements +- [COOK-3756] Change postgresql.conf mode from 0600 to 0644 + + +v3.1.0 +------ +### Improvement +- **[COOK-3685](https://tickets.opscode.com/browse/COOK-3685)** - Upgrade Repo Attributes for Postgresql 9.3 +- **[COOK-3597](https://tickets.opscode.com/browse/COOK-3597)** - Fix implementation of `initdb_locale` attribute for RHEL +- **[COOK-3566](https://tickets.opscode.com/browse/COOK-3566)** - Give the user's rules more priority than the default ones in pg_hba +- **[COOK-3553](https://tickets.opscode.com/browse/COOK-3553)** - Remove automatic `apt-get update` + +### Bug +- **[COOK-3611](https://tickets.opscode.com/browse/COOK-3611)** - Remove `unix_socket_directory` (it does not exists in 9.3) +- **[COOK-3599](https://tickets.opscode.com/browse/COOK-3599)** - Automatically add PGDG apt repo dependency on PostgreSQL version +- **[COOK-3555](https://tickets.opscode.com/browse/COOK-3555)** - Documentation Fix +- **[COOK-2383](https://tickets.opscode.com/browse/COOK-2383)** - Update Postgres version in attributes + + v3.0.4 ------ ### Bug diff --git a/chef/cookbooks/postgresql/CONTRIBUTING.md b/chef/cookbooks/postgresql/CONTRIBUTING.md deleted file mode 100644 index 3a99897..0000000 --- a/chef/cookbooks/postgresql/CONTRIBUTING.md +++ /dev/null @@ -1,257 +0,0 @@ -# Contributing to Opscode Cookbooks - -We are glad you want to contribute to Opscode Cookbooks! The first -step is the desire to improve the project. - -You can find the answers to additional frequently asked questions -[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). - -You can find additional information about -[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) -on the wiki as well. - -## Quick-contribute - -* Create an account on our [bug tracker](http://tickets.opscode.com) -* Sign our contributor agreement (CLA) -[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) -(keep reading if you're contributing on behalf of your employer) -* Create a ticket for your change on the - [bug tracker](http://tickets.opscode.com) -* Link to your patch as a rebased git branch or pull request from the - ticket -* Resolve the ticket as fixed - -We regularly review contributions and will get back to you if we have -any suggestions or concerns. - -## The Apache License and the CLA/CCLA - -Licensing is very important to open source projects, it helps ensure -the software continues to be available under the terms that the author -desired. Chef uses the Apache 2.0 license to strike a balance between -open contribution and allowing you to use the software however you -would like to. - -The license tells you what rights you have that are provided by the -copyright holder. It is important that the contributor fully -understands what rights they are licensing and agrees to them. -Sometimes the copyright holder isn't the contributor, most often when -the contributor is doing work for a company. - -To make a good faith effort to ensure these criteria are met, Opscode -requires a Contributor License Agreement (CLA) or a Corporate -Contributor License Agreement (CCLA) for all contributions. This is -without exception due to some matters not being related to copyright -and to avoid having to continually check with our lawyers about small -patches. - -It only takes a few minutes to complete a CLA, and you retain the -copyright to your contribution. - -You can complete our contributor agreement (CLA) -[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). -If you're contributing on behalf of your employer, have your employer -fill out our -[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) -instead. - -## Ticket Tracker (JIRA) - -The [ticket tracker](http://tickets.opscode.com) is the most important -documentation for the code base. It provides significant historical -information, such as: - -* Which release a bug fix is included in -* Discussion regarding the design and merits of features -* Error output to aid in finding similar bugs - -Each ticket should aim to fix one bug or add one feature. - -## Using git - -You can get a quick copy of the repository for this cookbook by -running `git clone -git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. - -For collaboration purposes, it is best if you create a Github account -and fork the repository to your own account. Once you do this you will -be able to push your changes to your Github repository for others to -see and use. - -If you have another repository in your GitHub account named the same -as the cookbook, we suggest you suffix the repository with -cookbook. - -### Branches and Commits - -You should submit your patch as a git branch named after the ticket, -such as COOK-1337. This is called a _topic branch_ and allows users to -associate a branch of code with the ticket. - -It is a best practice to have your commit message have a _summary -line_ that includes the ticket number, followed by an empty line and -then a brief description of the commit. This also helps other -contributors understand the purpose of changes to the code. - - [COOK-1757] - platform_family and style - - * use platform_family for platform checking - * update notifies syntax to "resource_type[resource_name]" instead of - resources() lookup - * COOK-692 - delete config files dropped off by packages in conf.d - * dropped debian 4 support because all other platforms have the same - values, and it is older than "old stable" debian release - -Remember that not all users use Chef in the same way or on the same -operating systems as you, so it is helpful to be clear about your use -case and change so they can understand it even when it doesn't apply -to them. - -### Github and Pull Requests - -All of Opscode's open source cookbook projects are available on -[Github](http://www.github.com/opscode-cookbooks). - -We don't require you to use Github, and we will even take patch diffs -attached to tickets on the tracker. However Github has a lot of -convenient features, such as being able to see a diff of changes -between a pull request and the main repository quickly without -downloading the branch. - -If you do choose to use a pull request, please provide a link to the -pull request from the ticket __and__ a link to the ticket from the -pull request. Because pull requests only have two states, open and -closed, we can't easily filter pull requests that are waiting for a -reply from the author for various reasons. - -### More information - -Additional help with git is available on the -[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) -wiki page. - -## Functional and Unit Tests - -This cookbook is set up to run tests under -[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It -uses minitest-chef to run integration tests after the node has been -converged to verify that the state of the node. - -Test kitchen should run completely without exception using the default -[baseboxes provided by Opscode](https://github.com/opscode/bento). -Because Test Kitchen creates VirtualBox machines and runs through -every configuration in the Kitchenfile, it may take some time for -these tests to complete. - -If your changes are only for a specific recipe, run only its -configuration with Test Kitchen. If you are adding a new recipe, or -other functionality such as a LWRP or definition, please add -appropriate tests and ensure they run with Test Kitchen. - -If any don't pass, investigate them before submitting your patch. - -Any new feature should have unit tests included with the patch with -good code coverage to help protect it from future changes. Similarly, -patches that fix a bug or regression should have a _regression test_. -Simply put, this is a test that would fail without your patch but -passes with it. The goal is to ensure this bug doesn't regress in the -future. Consider a regular expression that doesn't match a certain -pattern that it should, so you provide a patch and a test to ensure -that the part of the code that uses this regular expression works as -expected. Later another contributor may modify this regular expression -in a way that breaks your use cases. The test you wrote will fail, -signalling to them to research your ticket and use case and accounting -for it. - -If you need help writing tests, please ask on the Chef Developer's -mailing list, or the #chef-hacking IRC channel. - -## Code Review - -Opscode regularly reviews code contributions and provides suggestions -for improvement in the code itself or the implementation. - -We find contributions by searching the ticket tracker for _resolved_ -tickets with a status of _fixed_. If we have feedback we will reopen -the ticket and you should resolve it again when you've made the -changes or have a response to our feedback. When we believe the patch -is ready to be merged, we will tag the _Code Reviewed_ field with -_Reviewed_. - -Depending on the project, these tickets are then merged within a week -or two, depending on the current release cycle. - -## Release Cycle - -The versioning for Opscode Cookbook projects is X.Y.Z. - -* X is a major release, which may not be fully compatible with prior - major releases -* Y is a minor release, which adds both new features and bug fixes -* Z is a patch release, which adds just bug fixes - -A released version of a cookbook will end in an even number, e.g. -"1.2.4" or "0.8.0". When development for the next version of the -cookbook begins, the "Z" patch number is incremented to the next odd -number, however the next release of the cookbook may be a major or -minor incrementing version. - -Releases of Opscode's cookbooks are usually announced on the Chef user -mailing list. Releases of several cookbooks may be batched together -and announced on the [Opscode Blog](http://www.opscode.com/blog). - -## Working with the community - -These resources will help you learn more about Chef and connect to -other members of the Chef community: - -* [chef](http://lists.opscode.com/sympa/info/chef) and - [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing - lists -* #chef and #chef-hacking IRC channels on irc.freenode.net -* [Community Cookbook site](http://community.opscode.com) -* [Chef wiki](http://wiki.opscode.com/display/chef) -* Opscode Chef [product page](http://www.opscode.com/chef) - - -## Cookbook Contribution Do's and Don't's - -Please do include tests for your contribution. If you need help, ask -on the -[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) -or the -[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). -Not all platforms that a cookbook supports may be supported by Test -Kitchen. Please provide evidence of testing your contribution if it -isn't trivial so we don't have to duplicate effort in testing. Chef -10.14+ "doc" formatted output is sufficient. - -Please do indicate new platform (families) or platform versions in the -commit message, and update the relevant ticket. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] - Updated pool resource to correctly - delete.' - -Please do use [foodcritic](http://acrmp.github.com/foodcritic) to -lint-check the cookbook. Except FC007, it should pass all correctness -rules. FC007 is okay as long as the dependent cookbooks are *required* -for the default behavior of the cookbook, such as to support an -uncommon platform, secondary recipe, etc. - -Please do ensure that your changes do not break or modify behavior for -other platforms supported by the cookbook. For example if your changes -are for Debian, make sure that they do not break on CentOS. - -Please do not modify the version number in the metadata.rb, Opscode -will select the appropriate version based on the release cycle -information above. - -Please do not update the CHANGELOG.md for a new version. Not all -changes to a cookbook may be merged and released in the same versions. -Opscode will update the CHANGELOG.md when releasing a new version of -the cookbook. diff --git a/chef/cookbooks/postgresql/LICENSE b/chef/cookbooks/postgresql/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/postgresql/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/postgresql/README.md b/chef/cookbooks/postgresql/README.md index f544eda..5342429 100644 --- a/chef/cookbooks/postgresql/README.md +++ b/chef/cookbooks/postgresql/README.md @@ -46,6 +46,8 @@ The following attributes are set based on the platform, see the that should be installed on "client" systems. * `node['postgresql']['server']['packages']` - An array of package names that should be installed on "server" systems. +* `node['postgresql']['server']['config_change_notify']` - Type of + notification triggered when a config file changes. * `node['postgresql']['contrib']['packages']` - An array of package names that could be installed on "server" systems for useful sysadmin tools. @@ -57,6 +59,13 @@ The following attributes are set based on the platform, see the by the PostgreSQL Global Development Group, which contains newer versions of PostgreSQL. +* `node['postgresql']['initdb_locale']` - Sets the default locale for the + database cluster. If this attribute is not specified, the locale is + inherited from the environment that initdb runs in. Sometimes you must + have a system locale that is not what you want for your database cluster, + and this attribute addresses that scenario. Valid only for EL-family + distros (RedHat/Centos/etc.). + The following attributes are generated in `recipe[postgresql::server]`. @@ -72,11 +81,11 @@ generated from attributes. Each key in `node['postgresql']['config']` is a postgresql configuration directive, and will be rendered in the config file. For example, the attribute: - node['postgresql']['config']['listen_address'] = 'localhost' + node['postgresql']['config']['listen_addresses'] = 'localhost' Will result in the following line in the `postgresql.conf` file: - listen_address = 'localhost' + listen_addresses = 'localhost' The attributes file contains default values for Debian and RHEL platform families (per the `node['platform_family']`). These defaults @@ -110,6 +119,11 @@ Will result in the following config lines: (no line printed for `ident_file` as it is `nil`) +Note that the `unix_socket_directory` configuration was renamed to +`unix_socket_directories` in Postgres 9.3 so make sure to use the +`node['postgresql']['unix_socket_directories']` attribute instead of +`node['postgresql']['unix_socket_directory']`. + The `pg_hba.conf` file is dynamically generated from the `node['postgresql']['pg_hba']` attribute. This attribute must be an array of hashes, each hash containing the authorization data. As it is @@ -388,6 +402,12 @@ certificates and distribute them in your own cookbook, and set the `node['postgresql']['config']['ssl']` attribute to true in your role/cookboook/node. +On server systems, the postgres server is restarted when a configuration +file changes. This can be changed to reload only by setting the +following attribute: + + node['postgresql']['server']['config_change_notify'] = :reload + Chef Solo Note ============== diff --git a/chef/cookbooks/postgresql/TESTING.md b/chef/cookbooks/postgresql/TESTING.md deleted file mode 100644 index e29ff7c..0000000 --- a/chef/cookbooks/postgresql/TESTING.md +++ /dev/null @@ -1,25 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test diff --git a/chef/cookbooks/postgresql/attributes/default.rb b/chef/cookbooks/postgresql/attributes/default.rb index fd49c39..ce23614 100644 --- a/chef/cookbooks/postgresql/attributes/default.rb +++ b/chef/cookbooks/postgresql/attributes/default.rb @@ -17,6 +17,9 @@ # limitations under the License. # +default['postgresql']['enable_pgdg_apt'] = false +default['postgresql']['server']['config_change_notify'] = :restart + case node['platform'] when "debian" @@ -37,9 +40,9 @@ when "debian" default['postgresql']['server']['service_name'] = "postgresql" end - default['postgresql']['client']['packages'] = %w{postgresql-client libpq-dev} - default['postgresql']['server']['packages'] = %w{postgresql} - default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} + default['postgresql']['client']['packages'] = ["postgresql-client-#{node['postgresql']['version']}","libpq-dev"] + default['postgresql']['server']['packages'] = ["postgresql-#{node['postgresql']['version']}"] + default['postgresql']['contrib']['packages'] = ["postgresql-contrib-#{node['postgresql']['version']}"] when "ubuntu" @@ -60,9 +63,9 @@ when "ubuntu" default['postgresql']['server']['service_name'] = "postgresql" end - default['postgresql']['client']['packages'] = %w{postgresql-client libpq-dev} - default['postgresql']['server']['packages'] = %w{postgresql} - default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} + default['postgresql']['client']['packages'] = ["postgresql-client-#{node['postgresql']['version']}","libpq-dev"] + default['postgresql']['server']['packages'] = ["postgresql-#{node['postgresql']['version']}"] + default['postgresql']['contrib']['packages'] = ["postgresql-contrib-#{node['postgresql']['version']}"] when "fedora" @@ -80,8 +83,14 @@ when "fedora" when "amazon" - default['postgresql']['version'] = "8.4" - default['postgresql']['dir'] = "/var/lib/pgsql/data" + if node['platform_version'].to_f >= 2012.03 + default['postgresql']['version'] = "9.0" + default['postgresql']['dir'] = "/var/lib/pgsql9/data" + else + default['postgresql']['version'] = "8.4" + default['postgresql']['dir'] = "/var/lib/pgsql/data" + end + default['postgresql']['client']['packages'] = %w{postgresql-devel} default['postgresql']['server']['packages'] = %w{postgresql-server} default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} @@ -92,7 +101,7 @@ when "redhat", "centos", "scientific", "oracle" default['postgresql']['version'] = "8.4" default['postgresql']['dir'] = "/var/lib/pgsql/data" - if node['platform_version'].to_f >= 6.0 + if node['platform_version'].to_f >= 6.0 && node['postgresql']['version'] == '8.4' default['postgresql']['client']['packages'] = %w{postgresql-devel} default['postgresql']['server']['packages'] = %w{postgresql-server} default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} @@ -101,7 +110,14 @@ when "redhat", "centos", "scientific", "oracle" default['postgresql']['server']['packages'] = ["postgresql#{node['postgresql']['version'].split('.').join}-server"] default['postgresql']['contrib']['packages'] = ["postgresql#{node['postgresql']['version'].split('.').join}-contrib"] end - default['postgresql']['server']['service_name'] = "postgresql" + + if node['platform_version'].to_f >= 6.0 && node['postgresql']['version'] != '8.4' + default['postgresql']['dir'] = "/var/lib/pgsql/#{node['postgresql']['version']}/data" + default['postgresql']['server']['service_name'] = "postgresql-#{node['postgresql']['version']}" + else + default['postgresql']['dir'] = "/var/lib/pgsql/data" + default['postgresql']['server']['service_name'] = "postgresql" + end when "suse" @@ -147,7 +163,8 @@ when 'debian' default['postgresql']['config']['listen_addresses'] = 'localhost' default['postgresql']['config']['port'] = 5432 default['postgresql']['config']['max_connections'] = 100 - default['postgresql']['config']['unix_socket_directory'] = '/var/run/postgresql' + default['postgresql']['config']['unix_socket_directory'] = '/var/run/postgresql' if node['postgresql']['version'].to_f < 9.3 + default['postgresql']['config']['unix_socket_directories'] = '/var/run/postgresql' if node['postgresql']['version'].to_f >= 9.3 default['postgresql']['config']['shared_buffers'] = '24MB' default['postgresql']['config']['max_fsm_pages'] = 153600 if node['postgresql']['version'].to_f < 8.4 default['postgresql']['config']['log_line_prefix'] = '%t ' @@ -181,8 +198,6 @@ default['postgresql']['pg_hba'] = [ default['postgresql']['password'] = Hash.new -default['postgresql']['enable_pgdg_apt'] = false - case node['platform_family'] when 'debian' default['postgresql']['pgdg']['release_apt_codename'] = node['lsb']['codename'] @@ -190,11 +205,13 @@ end default['postgresql']['enable_pgdg_yum'] = false +default['postgresql']['initdb_locale'] = nil + # The PostgreSQL RPM Building Project built repository RPMs for easy # access to the PGDG yum repositories. Links to RPMs for installation # on the supported version/platform combinations are listed at # http://yum.postgresql.org/repopackages.php, and the links for -# PostgreSQL 8.4, 9.0, 9.1 and 9.2 (from 2013-01-15) are captured below. +# PostgreSQL 8.4, 9.0, 9.1, 9.2 and 9.3 are captured below. # # The correct RPM for installing /etc/yum.repos.d is based on: # * the attribute configuring the desired Postgres Software: @@ -204,6 +221,51 @@ default['postgresql']['enable_pgdg_yum'] = false # node['platform_version'] e.g., "5.7", truncated as "5" # node['kernel']['machine'] e.g., "i386" or "x86_64" default['postgresql']['pgdg']['repo_rpm_url'] = { + "9.3" => { + "centos" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/pgdg-centos93-9.3-1.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-1.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.3/redhat/rhel-5-i386/pgdg-centos93-9.3-1.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.3/redhat/rhel-5-x86_64/pgdg-centos93-9.3-1.noarch.rpm" + } + }, + "redhat" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/pgdg-redhat93-9.3-1.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-redhat93-9.3-1.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.3/redhat/rhel-5-i386/pgdg-redhat93-9.3-1.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.3/redhat/rhel-5-x86_64/pgdg-redhat93-9.3-1.noarch.rpm" + } + }, + "scientific" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/pgdg-sl93-9.3-1.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-sl93-9.3-1.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.3/redhat/rhel-5-i386/pgdg-sl93-9.3-1.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.3/redhat/rhel-5-x86_64/pgdg-sl93-9.3-1.noarch.rpm" + } + }, + "fedora" => { + "19" => { + "x86_64" => "http://yum.postgresql.org/9.3/fedora/fedora-19-x86_64/pgdg-fedora93-9.3-1.noarch.rpm" + }, + "18" => { + "i386" => "http://yum.postgresql.org/9.3/fedora/fedora-18-i386/pgdg-fedora93-9.3-1.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.3/fedora/fedora-18-x86_64/pgdg-fedora93-9.3-1.noarch.rpm" + }, + "17" => { + "i386" => "http://yum.postgresql.org/9.3/fedora/fedora-17-i386/pgdg-fedora93-9.3-1.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.3/fedora/fedora-17-x86_64/pgdg-fedora93-9.3-1.noarch.rpm" + } + } + }, "9.2" => { "centos" => { "6" => { @@ -236,16 +298,21 @@ default['postgresql']['pgdg']['repo_rpm_url'] = { } }, "fedora" => { + "19" => { + "i386" => "http://yum.postgresql.org/9.2/fedora/fedora-19-i386/pgdg-fedora92-9.2-6.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/fedora/fedora-19-x86_64/pgdg-fedora92-9.2-6.noarch.rpm" + }, + "18" => { + "i386" => "http://yum.postgresql.org/9.2/fedora/fedora-18-i386/pgdg-fedora92-9.2-6.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/fedora/fedora-18-x86_64/pgdg-fedora92-9.2-6.noarch.rpm" + }, "17" => { + "i386" => "http://yum.postgresql.org/9.2/fedora/fedora-17-i386/pgdg-fedora92-9.2-6.noarch.rpm", "x86_64" => "http://yum.postgresql.org/9.2/fedora/fedora-17-x86_64/pgdg-fedora92-9.2-5.noarch.rpm" }, "16" => { "i386" => "http://yum.postgresql.org/9.2/fedora/fedora-16-i386/pgdg-fedora92-9.2-5.noarch.rpm", "x86_64" => "http://yum.postgresql.org/9.2/fedora/fedora-16-x86_64/pgdg-fedora92-9.2-5.noarch.rpm" - }, - "15" => { - "i386" => "http://yum.postgresql.org/9.2/fedora/fedora-15-i386/pgdg-fedora92-9.2-5.noarch.rpm", - "x86_64" => "http://yum.postgresql.org/9.2/fedora/fedora-15-x86_64/pgdg-fedora92-9.2-5.noarch.rpm" } } }, diff --git a/chef/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb b/chef/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb index eb3084a..41f1fcb 100644 --- a/chef/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb +++ b/chef/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb @@ -28,12 +28,12 @@ describe 'postgresql::apt_pgdg_postgresql' do file("/etc/apt/sources.list.d/apt.postgresql.org.list").must_exist end - it 'installs postgresql-client-9.2' do - package("postgresql-client-9.2").must_be_installed + it 'installs postgresql-client-9.3' do + package("postgresql-client-9.3").must_be_installed end - it 'makes psql version 9.2 available' do + it 'makes psql version 9.3 available' do psql = shell_out("psql --version") - assert psql.stdout.include?("psql (PostgreSQL) 9.2") + assert psql.stdout.include?("psql (PostgreSQL) 9.3") end end diff --git a/chef/cookbooks/postgresql/files/default/tests/minitest/server_test.rb b/chef/cookbooks/postgresql/files/default/tests/minitest/server_test.rb index bd9fdbc..51c3a21 100644 --- a/chef/cookbooks/postgresql/files/default/tests/minitest/server_test.rb +++ b/chef/cookbooks/postgresql/files/default/tests/minitest/server_test.rb @@ -26,7 +26,7 @@ describe 'postgresql::server' do end it 'runs the postgresql service' do - service("postgresql").must_be_running + service((node['postgresql']['server']['service_name'] || 'postgresql')).must_be_running end it 'can connect to postgresql' do diff --git a/chef/cookbooks/postgresql/metadata.json b/chef/cookbooks/postgresql/metadata.json new file mode 100644 index 0000000..022e437 --- /dev/null +++ b/chef/cookbooks/postgresql/metadata.json @@ -0,0 +1,47 @@ +{ + "name": "postgresql", + "version": "3.3.4", + "description": "Installs and configures postgresql for clients or servers", + "long_description": "Description\n===========\n\nInstalls and configures PostgreSQL as a client or a server.\n\nRequirements\n============\n\n## Platforms\n\n* Debian, Ubuntu\n* Red Hat/CentOS/Scientific (6.0+ required) - \"EL6-family\"\n* Fedora\n* SUSE\n\nTested on:\n\n* Ubuntu 10.04, 11.10, 12.04\n* Red Hat 6.1, Scientific 6.1, CentOS 6.3\n\n## Cookbooks\n\nRequires Opscode's `openssl` cookbook for secure password generation.\n\nRequires a C compiler and development headers in order to build the\n`pg` RubyGem to provide Ruby bindings in the `ruby` recipe.\n\nOpscode's `build-essential` cookbook provides this functionality on\nDebian, Ubuntu, and EL6-family.\n\nWhile not required, Opscode's `database` cookbook contains resources\nand providers that can interact with a PostgreSQL database. This\ncookbook is a dependency of database.\n\nAttributes\n==========\n\nThe following attributes are set based on the platform, see the\n`attributes/default.rb` file for default values.\n\n* `node['postgresql']['version']` - version of postgresql to manage\n* `node['postgresql']['dir']` - home directory of where postgresql\n data and configuration lives.\n\n* `node['postgresql']['client']['packages']` - An array of package names\n that should be installed on \"client\" systems.\n* `node['postgresql']['server']['packages']` - An array of package names\n that should be installed on \"server\" systems.\n* `node['postgresql']['server']['config_change_notify']` - Type of\n notification triggered when a config file changes.\n* `node['postgresql']['contrib']['packages']` - An array of package names\n that could be installed on \"server\" systems for useful sysadmin tools.\n\n* `node['postgresql']['enable_pgdg_apt']` - Whether to enable the apt repo\n by the PostgreSQL Global Development Group, which contains newer versions\n of PostgreSQL.\n\n* `node['postgresql']['enable_pgdg_yum']` - Whether to enable the yum repo\n by the PostgreSQL Global Development Group, which contains newer versions\n of PostgreSQL.\n\n* `node['postgresql']['initdb_locale']` - Sets the default locale for the\n database cluster. If this attribute is not specified, the locale is\n inherited from the environment that initdb runs in. Sometimes you must\n have a system locale that is not what you want for your database cluster,\n and this attribute addresses that scenario. Valid only for EL-family\n distros (RedHat/Centos/etc.).\n\nThe following attributes are generated in\n`recipe[postgresql::server]`.\n\n* `node['postgresql']['password']['postgres']` - randomly generated\n password by the `openssl` cookbook's library.\n (TODO: This is broken, as it disables the password.)\n\nConfiguration\n-------------\n\nThe `postgresql.conf` and `pg_hba.conf` files are dynamically\ngenerated from attributes. Each key in `node['postgresql']['config']`\nis a postgresql configuration directive, and will be rendered in the\nconfig file. For example, the attribute:\n\n node['postgresql']['config']['listen_addresses'] = 'localhost'\n\nWill result in the following line in the `postgresql.conf` file:\n\n listen_addresses = 'localhost'\n\nThe attributes file contains default values for Debian and RHEL\nplatform families (per the `node['platform_family']`). These defaults\nhave disparity between the platforms because they were originally\nextracted from the postgresql.conf files in the previous version of\nthis cookbook, which differed in their default config. The resulting\nconfiguration files will be the same as before, but the content will\nbe dynamically rendered from the attributes. The helpful commentary\nwill no longer be present. You should consult the PostgreSQL\ndocumentation for specific configuration details.\n\nSee __Recipes__ `config_initdb` and `config_pgtune` below to\nauto-generate many postgresql.conf settings.\n\nFor values that are \"on\" or \"off\", they should be specified as literal\n`true` or `false`. String values will be used with single quotes. Any\nconfiguration option set to the literal `nil` will be skipped\nentirely. All other values (e.g., numeric literals) will be used as\nis. So for example:\n\n node.default['postgresql']['config']['logging_collector'] = true\n node.default['postgresql']['config']['datestyle'] = 'iso, mdy'\n node.default['postgresql']['config']['ident_file'] = nil\n node.default['postgresql']['config']['port] = 5432\n\nWill result in the following config lines:\n\n logging_collector = 'on'\n datestyle = 'iso,mdy'\n port = 5432\n\n(no line printed for `ident_file` as it is `nil`)\n\nNote that the `unix_socket_directory` configuration was renamed to\n`unix_socket_directories` in Postgres 9.3 so make sure to use the\n`node['postgresql']['unix_socket_directories']` attribute instead of\n`node['postgresql']['unix_socket_directory']`.\n\nThe `pg_hba.conf` file is dynamically generated from the\n`node['postgresql']['pg_hba']` attribute. This attribute must be an\narray of hashes, each hash containing the authorization data. As it is\nan array, you can append to it in your own recipes. The hash keys in\nthe array must be symbols. Each hash will be written as a line in\n`pg_hba.conf`. For example, this entry from\n`node['postgresql']['pg_hba']`:\n\n {:comment => '# Optional comment',\n :type => 'local', :db => 'all', :user => 'postgres', :addr => nil, :method => 'md5'}\n\nWill result in the following line in `pg_hba.conf`:\n\n # Optional comment\n local all postgres md5\n\nUse `nil` if the CIDR-ADDRESS should be empty (as above).\nDon't provide a comment if none is desired in the `pg_hba.conf` file.\n\nNote that the following authorization rule is supplied automatically by\nthe cookbook template. The cookbook needs this to execute SQL in the\nPostgreSQL server without supplying the clear-text password (which isn't\nknown by the cookbook). Therefore, your `node['postgresql']['pg_hba']`\nattributes don't need to specify this authorization rule:\n\n # \"local\" is for Unix domain socket connections only\n local all all ident\n\n(By the way, the template uses `peer` instead of `ident` for PostgreSQL-9.1\nand above, which has the same effect.)\n\nRecipes\n=======\n\ndefault\n-------\n\nIncludes the client recipe.\n\nclient\n------\n\nInstalls the packages defined in the\n`node['postgresql']['client']['packages']` attribute.\n\nruby\n----\n\n**NOTE** This recipe may not currently work when installing Chef with\n the\n [\"Omnibus\" full stack installer](http://opscode.com/chef/install) on\n some platforms due to an incompatibility with OpenSSL. See\n [COOK-1406](http://tickets.opscode.com/browse/COOK-1406). You can\n build from source into the Chef omnibus installation to work around\n this issue.\n\nInstall the `pg` gem under Chef's Ruby environment so it can be used\nin other recipes. The build-essential packages and postgresql client\npackages will be installed during the compile phase, so that the\nnative extensions of `pg` can be compiled.\n\nserver\n------\n\nIncludes the `server_debian` or `server_redhat` recipe to get the\nappropriate server packages installed and service managed. Also\nmanages the configuration for the server:\n\n* generates a strong default password (via `openssl`) for `postgres`\n (TODO: This is broken, as it disables the password.)\n* sets the password for postgres\n* manages the `postgresql.conf` file.\n* manages the `pg_hba.conf` file.\n\nserver\\_debian\n--------------\n\nInstalls the postgresql server packages and sets up the service. You\nshould include the `postgresql::server` recipe, which will include\nthis on Debian platforms.\n\nserver\\_redhat\n--------------\n\nManages the postgres user and group (with UID/GID 26, per RHEL package\nconventions), installs the postgresql server packages, initializes the\ndatabase, and manages the postgresql service. You should include the\n`postgresql::server` recipe, which will include this on RHEL/Fedora\nplatforms.\n\nconfig\\_initdb\n--------------\n\nTakes locale and timezone settings from the system configuration.\nThis recipe creates `node.default['postgresql']['config']` attributes\nthat conform to the system's locale and timezone. In addition, this\nrecipe creates the same error reporting and logging settings that\n`initdb` provided: a rotation of 7 days of log files named\npostgresql-Mon.log, etc.\n\nThe default attributes created by this recipe are easy to override with\nnormal attributes because of Chef attribute precedence. For example,\nsuppose a DBA wanted to keep log files indefinitely, rolling over daily\nor when growing to 10MB. The Chef installation could include the\n`postgresql::config_initdb` recipe for the locale and timezone settings,\nbut customize the logging settings with these node JSON attributes:\n\n \"postgresql\": {\n \"config\": {\n \"log_rotation_age\": \"1d\",\n \"log_rotation_size\": \"10MB\",\n \"log_filename\": \"postgresql-%Y-%m-%d_%H%M%S.log\"\n }\n }\n\nCredits: This `postgresql::config_initdb` recipe is based on algorithms\nin the [source code](http://doxygen.postgresql.org/initdb_8c_source.html)\nfor the PostgreSQL `initdb` utility.\n\nconfig\\_pgtune\n--------------\n\nPerformance tuning.\nTakes the wimpy default postgresql.conf and expands the database server\nto be as powerful as the hardware it's being deployed on. This recipe\ncreates a baseline configuration of `node.default['postgresql']['config']`\nattributes in the right general range for a dedicated Postgresql system.\nMost installations won't need additional performance tuning.\n\nThe only decision you need to make is to choose a `db_type` from the\nfollowing database workloads. (See the recipe code comments for more\ndetailed descriptions.)\n\n * \"dw\" -- Data Warehouse\n * \"oltp\" -- Online Transaction Processing\n * \"web\" -- Web Application\n * \"mixed\" -- Mixed DW and OLTP characteristics\n * \"desktop\" -- Not a dedicated database\n\nThis recipe uses a performance model with three input parameters.\nThese node attributes are completely optional, but it is obviously\nimportant to choose the `db_type` correctly:\n\n * `node['postgresql']['config_pgtune']['db_type']` --\n Specifies database type from the list of five choices above.\n If not specified, the default is \"mixed\".\n\n * `node['postgresql']['config_pgtune']['max_connections']` --\n Specifies maximum number of connections expected.\n If not specified, it depends on database type:\n \"web\":200, \"oltp\":300, \"dw\":20, \"mixed\":80, \"desktop\":5\n\n * `node['postgresql']['config_pgtune']['total_memory']` --\n Specifies total system memory in kB. (E.g., \"49416564kB\".)\n If not specified, it will be taken from Ohai automatic attributes.\n This could be used to tune a system that isn't a dedicated database.\n\nThe default attributes created by this recipe are easy to override with\nnormal attributes because of Chef attribute precedence. For example, if\nyou are running application benchmarks to try different buffer cache\nsizes, you would experiment with this node JSON attribute:\n\n \"postgresql\": {\n \"config\": {\n \"shared_buffers\": \"3GB\"\n }\n }\n\nNote that the recipe uses `max_connections` in its computations. If\nyou want to override that setting, you should specify\n`node['postgresql']['config_pgtune']['max_connections']` instead of\n`node['postgresql']['config']['max_connections']`.\n\nCredits: This `postgresql::config_pgtune` recipe is based on the\n[pgtune python script](https://github.com/gregs1104/pgtune)\ndeveloped by\n[Greg Smith](http://notemagnet.blogspot.com/2008/11/automating-initial-postgresqlconf.html)\nand\n[other pgsql-hackers](http://www.postgresql.org/message-id/491C6CDC.8090506@agliodbs.com).\n\ncontrib\n-------\n\nInstalls the packages defined in the\n`node['postgresql']['contrib']['packages']` attribute. The contrib\ndirectory of the PostgreSQL distribution includes porting tools,\nanalysis utilities, and plug-in features that database engineers often\nrequire. Some (like `pgbench`) are executable. Others (like\n`pg_buffercache`) would need to be installed into the database.\n\nAlso installs any contrib module extensions defined in the\n`node['postgresql']['contrib']['extensions']` attribute. These will be\navailable in any subsequently created databases in the cluster, because\nthey will be installed into the `template1` database using the\n`CREATE EXTENSION` command. For example, it is often necessary/helpful\nfor problem troubleshooting and maintenance planning to install the\nviews and functions in these [standard instrumentation extensions]\n(http://www.postgresql.org/message-id/flat/4DC32600.6080900@pgexperts.com#4DD3D6C6.5060006@2ndquadrant.com):\n\n node['postgresql']['contrib']['extensions'] = [\n \"pageinspect\",\n \"pg_buffercache\",\n \"pg_freespacemap\",\n \"pgrowlocks\",\n \"pg_stat_statements\",\n \"pgstattuple\"\n ]\n\nNote that the `pg_stat_statements` view only works if `postgresql.conf`\nloads its shared library, which can be done with this node attribute:\n\n node['postgresql']['config']['shared_preload_libraries'] = 'pg_stat_statements'\n\napt\\_pgdg\\_postgresql\n----------------------\n\nEnables the PostgreSQL Global Development Group yum repository\nmaintained by Devrim Gündüz for updated PostgreSQL packages.\n(The PGDG is the groups that develops PostgreSQL.)\nAutomatically included if the `node['postgresql']['enable_pgdg_apt']`\nattribute is true. Also set the\n`node['postgresql']['client']['packages']` and\n`node['postgresql']['server]['packages']` to the list of packages to\nuse from this repository, and set the `node['postgresql']['version']`\nattribute to the version to use (e.g., \"9.2\").\n\nyum\\_pgdg\\_postgresql\n---------------------\n\nEnables the PostgreSQL Global Development Group yum repository\nmaintained by Devrim Gündüz for updated PostgreSQL packages.\n(The PGDG is the groups that develops PostgreSQL.)\nAutomatically included if the `node['postgresql']['enable_pgdg_yum']`\nattribute is true. Also use `override_attributes` to set a number of\nvalues that will need to have embedded version numbers. For example:\n\n node['postgresql']['enable_pgdg_yum'] = true\n node['postgresql']['version'] = \"9.2\"\n node['postgresql']['dir'] = \"/var/lib/pgsql/9.2/data\"\n node['postgresql']['client']['packages'] = [\"postgresql92\", \"postgresql92-devel\"]\n node['postgresql']['server']['packages'] = [\"postgresql92-server\"]\n node['postgresql']['server']['service_name'] = \"postgresql-9.2\"\n node['postgresql']['contrib']['packages'] = [\"postgresql92-contrib\"]\n\nYou may set `node['postgresql']['pgdg']['repo_rpm_url']` attributes\nto pick up recent [PGDG repo packages](http://yum.postgresql.org/repopackages.php).\n\nResources/Providers\n===================\n\nSee the [database](http://community.opscode.com/cookbooks/database)\nfor resources and providers that can be used for managing PostgreSQL\nusers and databases.\n\nUsage\n=====\n\nOn systems that need to connect to a PostgreSQL database, add to a run\nlist `recipe[postgresql]` or `recipe[postgresql::client]`.\n\nOn systems that should be PostgreSQL servers, use\n`recipe[postgresql::server]` on a run list. This recipe does set a\npassword for the `postgres` user.\nIf you're using `chef server`, if the attribute\n`node['postgresql']['password']['postgres']` is not found,\nthe recipe generates a random password and performs a node.save.\n(TODO: This is broken, as it disables the password.)\nIf you're using `chef-solo`, you'll need\nto set the attribute `node['postgresql']['password']['postgres']` in\nyour node's `json_attribs` file or in a role.\n\nOn Debian family systems, SSL will be enabled, as the packages on\nDebian/Ubuntu also generate the SSL certificates. If you use another\nplatform and wish to use SSL in postgresql, then generate your SSL\ncertificates and distribute them in your own cookbook, and set the\n`node['postgresql']['config']['ssl']` attribute to true in your\nrole/cookboook/node.\n\nOn server systems, the postgres server is restarted when a configuration\nfile changes. This can be changed to reload only by setting the\nfollowing attribute:\n\n node['postgresql']['server']['config_change_notify'] = :reload\n\nChef Solo Note\n==============\n\nThe following node attribute is stored on the Chef Server when using\n`chef-client`. Because `chef-solo` does not connect to a server or\nsave the node object at all, to have the password persist across\n`chef-solo` runs, you must specify them in the `json_attribs` file\nused. For Example:\n\n {\n \"postgresql\": {\n \"password\": {\n \"postgres\": \"iloverandompasswordsbutthiswilldo\"\n }\n },\n \"run_list\": [\"recipe[postgresql::server]\"]\n }\n\nThat should actually be the \"encrypted password\" instead of cleartext,\nso you should generate it as an md5 hash using the PostgreSQL algorithm.\n\n* You could copy the md5-hashed password from an existing postgres\ndatabase if you have `postgres` access and want to use the same password:
\n`select * from pg_shadow where usename='postgres';`\n* You can run this from any postgres database session to use a new password:
\n`select 'md5'||md5('iloverandompasswordsbutthiswilldo'||'postgres');`\n* You can run this from a linux commandline:
\n`echo -n 'iloverandompasswordsbutthiswilldo''postgres' | openssl md5 | sed -e 's/.* /md5/'`\n\nLicense and Author\n==================\n\n- Author:: Joshua Timberman ()\n- Author:: Lamont Granquist ()\n- Author:: Chris Roberts ()\n- Author:: David Crane ()\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "ubuntu": ">= 0.0.0", + "debian": ">= 0.0.0", + "fedora": ">= 0.0.0", + "suse": ">= 0.0.0", + "amazon": ">= 0.0.0", + "redhat": ">= 6.0.0", + "centos": ">= 6.0.0", + "scientific": ">= 6.0.0", + "oracle": ">= 6.0.0" + }, + "dependencies": { + "apt": ">= 0.0.0", + "build-essential": ">= 0.0.0", + "openssl": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "postgresql": "Includes postgresql::client", + "postgresql::ruby": "Installs pg gem for Ruby bindings", + "postgresql::client": "Installs postgresql client package(s)", + "postgresql::server": "Installs postgresql server packages, templates", + "postgresql::server_redhat": "Installs postgresql server packages, redhat family style", + "postgresql::server_debian": "Installs postgresql server packages, debian family style" + } +} \ No newline at end of file diff --git a/chef/cookbooks/postgresql/metadata.rb b/chef/cookbooks/postgresql/metadata.rb index 755000e..8bd2244 100644 --- a/chef/cookbooks/postgresql/metadata.rb +++ b/chef/cookbooks/postgresql/metadata.rb @@ -4,7 +4,7 @@ maintainer_email "cookbooks@opscode.com" license "Apache 2.0" description "Installs and configures postgresql for clients or servers" long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "3.0.5" +version "3.3.4" recipe "postgresql", "Includes postgresql::client" recipe "postgresql::ruby", "Installs pg gem for Ruby bindings" recipe "postgresql::client", "Installs postgresql client package(s)" diff --git a/chef/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb b/chef/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb index cf91cef..b5012eb 100644 --- a/chef/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb +++ b/chef/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb @@ -12,7 +12,7 @@ end apt_repository 'apt.postgresql.org' do uri 'http://apt.postgresql.org/pub/repos/apt' distribution "#{node['postgresql']['pgdg']['release_apt_codename']}-pgdg" - components %w(main) + components ['main', node['postgresql']['version']] key 'http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc' action :add end diff --git a/chef/cookbooks/postgresql/recipes/client.rb b/chef/cookbooks/postgresql/recipes/client.rb index e659083..2a0b99c 100644 --- a/chef/cookbooks/postgresql/recipes/client.rb +++ b/chef/cookbooks/postgresql/recipes/client.rb @@ -19,6 +19,10 @@ # limitations under the License. # +if node['postgresql']['version'].to_f > 9.1 && platform_family?('ubuntu', 'debian') + node.default['postgresql']['enable_pgdg_apt'] = true +end + if(node['postgresql']['enable_pgdg_apt']) include_recipe 'postgresql::apt_pgdg_postgresql' end diff --git a/chef/cookbooks/postgresql/recipes/ruby.rb b/chef/cookbooks/postgresql/recipes/ruby.rb index 9a9e4f7..bab1dae 100644 --- a/chef/cookbooks/postgresql/recipes/ruby.rb +++ b/chef/cookbooks/postgresql/recipes/ruby.rb @@ -24,10 +24,6 @@ begin require 'pg' rescue LoadError - execute "apt-get update" do - ignore_failure true - action :nothing - end.run_action(:run) if node['platform_family'] == "debian" node.set['build_essential']['compiletime'] = true include_recipe "build-essential" @@ -48,10 +44,12 @@ rescue LoadError end node['postgresql']['client']['packages'].each do |pg_pack| - resources("package[#{pg_pack}]").run_action(:install) - end + + package "libpq-dev" do + action :nothing + end.run_action(:install) begin chef_gem "pg" @@ -86,7 +84,12 @@ EOS end lib_builder = execute 'generate pg gem Makefile' do - command "#{RbConfig.ruby} extconf.rb" + # [COOK-3490] pg gem install requires full path on RHEL + if node['platform_family'] == 'rhel' + command "#{RbConfig.ruby} extconf.rb --with-pg-config=/usr/pgsql-#{node['postgresql']['version']}/bin/pg_config" + else + command "#{RbConfig.ruby} extconf.rb" + end cwd ext_dir action :nothing end diff --git a/chef/cookbooks/postgresql/recipes/server.rb b/chef/cookbooks/postgresql/recipes/server.rb index 933fced..9912dd5 100644 --- a/chef/cookbooks/postgresql/recipes/server.rb +++ b/chef/cookbooks/postgresql/recipes/server.rb @@ -57,12 +57,14 @@ when "debian" include_recipe "postgresql::server_debian" end +change_notify = node['postgresql']['server']['config_change_notify'] + template "#{node['postgresql']['dir']}/postgresql.conf" do source "postgresql.conf.erb" owner "postgres" group "postgres" mode 0600 - notifies :reload, 'service[postgresql]', :immediately + notifies change_notify, 'service[postgresql]', :immediately end template "#{node['postgresql']['dir']}/pg_hba.conf" do @@ -70,7 +72,7 @@ template "#{node['postgresql']['dir']}/pg_hba.conf" do owner "postgres" group "postgres" mode 00600 - notifies :reload, 'service[postgresql]', :immediately + notifies change_notify, 'service[postgresql]', :immediately end # NOTE: Consider two facts before modifying "assign-postgres-password": diff --git a/chef/cookbooks/postgresql/recipes/server_redhat.rb b/chef/cookbooks/postgresql/recipes/server_redhat.rb index 81ebe39..1f13078 100644 --- a/chef/cookbooks/postgresql/recipes/server_redhat.rb +++ b/chef/cookbooks/postgresql/recipes/server_redhat.rb @@ -59,7 +59,7 @@ end unless platform_family?("suse") - execute "/sbin/service #{node['postgresql']['server']['service_name']} initdb" do + execute "/sbin/service #{node['postgresql']['server']['service_name']} initdb #{node['postgresql']['initdb_locale']}" do not_if { ::FileTest.exist?(File.join(node['postgresql']['dir'], "PG_VERSION")) } end diff --git a/chef/cookbooks/postgresql/templates/default/pg_hba.conf.erb b/chef/cookbooks/postgresql/templates/default/pg_hba.conf.erb index 8db8ee8..55d757d 100644 --- a/chef/cookbooks/postgresql/templates/default/pg_hba.conf.erb +++ b/chef/cookbooks/postgresql/templates/default/pg_hba.conf.erb @@ -12,13 +12,6 @@ # TYPE DATABASE USER ADDRESS METHOD <% end -%> -# "local" is for Unix domain socket connections only -<% if node['postgresql']['version'].to_f < 9.1 -%> -local all all ident -<% elsif node['postgresql']['version'].to_f >= 9.1 -%> -local all all peer -<% end -%> - ########### # Other authentication configurations taken from chef node defaults: ########### @@ -33,3 +26,10 @@ local all all peer <%= auth[:type].ljust(7) %> <%= auth[:db].ljust(15) %> <%= auth[:user].ljust(15) %> <%= auth[:method] %> <% end %> <% end %> + +# "local" is for Unix domain socket connections only +<% if node['postgresql']['version'].to_f < 9.1 -%> +local all all ident +<% elsif node['postgresql']['version'].to_f >= 9.1 -%> +local all all peer +<% end -%> diff --git a/chef/cookbooks/python/.gitignore b/chef/cookbooks/python/.gitignore index c7eb8a2..8fb3f98 100644 --- a/chef/cookbooks/python/.gitignore +++ b/chef/cookbooks/python/.gitignore @@ -1,5 +1,4 @@ .vagrant -Berksfile.lock *~ *# .#* @@ -9,7 +8,6 @@ Berksfile.lock /cookbooks # Bundler -Gemfile.lock bin/* .bundle/* .kitchen/ diff --git a/chef/cookbooks/python/.travis.yml b/chef/cookbooks/python/.travis.yml new file mode 100644 index 0000000..c40abd3 --- /dev/null +++ b/chef/cookbooks/python/.travis.yml @@ -0,0 +1,10 @@ +language: ruby +rvm: + - 1.9.3 + - 2.0.0 +bundler_args: --deployment --binstubs +before_script: + - ./bin/berks install +script: + - ./bin/foodcritic -f any . + - ./bin/rake diff --git a/chef/cookbooks/python/Berksfile.lock b/chef/cookbooks/python/Berksfile.lock new file mode 100644 index 0000000..2415dd8 --- /dev/null +++ b/chef/cookbooks/python/Berksfile.lock @@ -0,0 +1,28 @@ +{ + "sources": { + "python": { + "path": "." + }, + "minitest-handler": { + "locked_version": "1.1.2" + }, + "apt": { + "locked_version": "2.3.4" + }, + "yum": { + "locked_version": "3.0.2" + }, + "build-essential": { + "locked_version": "1.4.2" + }, + "python_test": { + "path": "./test/cookbooks/python_test" + }, + "yum-epel": { + "locked_version": "0.2.0" + }, + "chef_handler": { + "locked_version": "1.1.4" + } + } +} diff --git a/chef/cookbooks/python/CHANGELOG.md b/chef/cookbooks/python/CHANGELOG.md index 6d0e7b0..83d283c 100644 --- a/chef/cookbooks/python/CHANGELOG.md +++ b/chef/cookbooks/python/CHANGELOG.md @@ -2,6 +2,32 @@ python Cookbook CHANGELOG ========================= This file is used to list changes made in each version of the python cookbook. +v1.4.6 +------ + +- **[#61](https://github.com/poise/python/pull/61)** – Python packages specified for Fedora platform. +- **[#62](https://github.com/poise/python/pull/62)** – Use tgz archive instead of tbz2 as new releases no longer offer tb2 +- **[#72](https://github.com/poise/python/pull/72)** – Set $HOME in virtualenv provider +- **[#74](https://github.com/poise/python/pull/74)** – Allow setting of virtualenv version +- **[#78](https://github.com/poise/python/pull/78)** – AFix the pip version_check_cmd to use the virtualenv pip when applicable +- **[#79](https://github.com/poise/python/pull/79)** – Add ability in pip lwrp to send custom env vars +- **[#80](https://github.com/poise/python/pull/80)** – Update to yum3 yum-epel cookbook +- Remove ez_setup.py usage and upgrade get-pip.py to 1.5.2. + +v1.4.4 +------ +[COOK-3816] - Including ez_setup script with cookbook instead of downloading from the internet + + +v1.4.2 +------ +### Bug +- **[COOK-3796](https://tickets.opscode.com/browse/COOK-3796)** - Virtualenv can fail + +### Improvement +- **[COOK-3719](https://tickets.opscode.com/browse/COOK-3719)** - Allow alternative install python, update pip location +- **[COOK-3703](https://tickets.opscode.com/browse/COOK-3703)** - Create symlink for source built python [python3 support] + v1.4.0 ------ diff --git a/chef/cookbooks/python/CONTRIBUTING b/chef/cookbooks/python/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/python/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/python/Gemfile b/chef/cookbooks/python/Gemfile index 2fea7a8..5334438 100644 --- a/chef/cookbooks/python/Gemfile +++ b/chef/cookbooks/python/Gemfile @@ -1,15 +1,5 @@ source 'https://rubygems.org' -gem 'rake' -gem 'rspec' -gem 'foodcritic' -gem 'berkshelf' -gem 'thor-foodcritic' -gem 'vagrant-wrapper' - -group :integration do - gem 'test-kitchen', :git => "git://github.com/opscode/test-kitchen.git" - gem 'kitchen-vagrant', :git => "git://github.com/opscode/kitchen-vagrant.git" - gem 'kitchen-ec2', :git => "git://github.com/opscode/kitchen-ec2.git" - gem 'kitchen-lxc', :git => "https://github.com/portertech/kitchen-lxc.git", :tag => 'v0.0.1.beta2' -end +gem 'foodcritic', '>= 3.0.3' +gem 'berkshelf', '~> 2.0' +gem 'chefspec', '~> 3.0' diff --git a/chef/cookbooks/python/Gemfile.lock b/chef/cookbooks/python/Gemfile.lock new file mode 100644 index 0000000..aa6b168 --- /dev/null +++ b/chef/cookbooks/python/Gemfile.lock @@ -0,0 +1,215 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.16) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.5) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (2.0.10) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.5.0) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) + buff-extensions (0.5.0) + buff-ignore (1.1.1) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.1) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.8.0) + chef-zero (~> 1.6, >= 1.6.2) + diff-lcs (~> 1.2, >= 1.2.4) + erubis (~> 2.7) + highline (~> 1.6, >= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mime-types (~> 1.16) + mixlib-authentication (~> 1.3) + mixlib-cli (~> 1.3) + mixlib-config (~> 2.0) + mixlib-log (~> 1.3) + mixlib-shellout (~> 1.2) + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (~> 6.0) + pry (~> 0.9) + puma (~> 1.6) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chef-zero (1.7.2) + hashie (~> 2.0) + json + mixlib-log (~> 1.3) + moneta (< 0.7.0) + rack + chefspec (3.0.2) + chef (~> 11.0) + fauxhai (~> 2.0) + rspec (~> 2.14) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + coderay (1.1.0) + diff-lcs (1.2.5) + erubis (2.7.0) + faraday (0.8.8) + multipart-post (~> 1.2.0) + fauxhai (2.0.1) + net-ssh + ohai + ffi (1.9.3) + foodcritic (3.0.3) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rake + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.1.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.20) + httpclient (2.3.4.1) + httpi (0.9.7) + rack + i18n (0.6.9) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + logging (1.8.1) + little-plugger (>= 1.1.3) + multi_json (>= 1.3.6) + method_source (0.8.2) + mime-types (1.25.1) + minitar (0.5.4) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.4.0) + mixlib-config (2.1.0) + mixlib-log (1.6.0) + mixlib-shellout (1.3.0) + moneta (0.6.0) + multi_json (1.8.2) + multipart-post (1.2.0) + net-http-persistent (2.9) + net-ssh (2.7.0) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.5.0) + nokogiri (1.5.11) + nori (1.1.5) + ohai (6.20.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu (~> 2.5.2) + yajl-ruby + polyglot (0.3.3) + pry (0.9.12.4) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) + puma (1.6.3) + rack (~> 1.2) + rack (1.5.2) + rake (10.1.1) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.5.3) + addressable + buff-config (~> 0.2) + buff-extensions (~> 0.3) + buff-ignore (~> 1.1) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + nio4r (>= 0.5.0) + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.7) + rspec-expectations (2.14.4) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.4) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + slop (3.4.7) + solve (0.8.2) + systemu (2.5.2) + thor (0.18.1) + timers (1.1.0) + treetop (1.4.15) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.2.0) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.3) + gssapi (~> 1.0.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0) + chefspec (~> 3.0) + foodcritic (>= 3.0.3) diff --git a/chef/cookbooks/python/NOTICE.txt b/chef/cookbooks/python/NOTICE.txt new file mode 100644 index 0000000..7f4a2b9 --- /dev/null +++ b/chef/cookbooks/python/NOTICE.txt @@ -0,0 +1,2 @@ +The script located at files/default/ez_setup.py is licensed under the +Zope Public License, found at the root of this cookbook at ZPL.txt. diff --git a/chef/cookbooks/python/README.md b/chef/cookbooks/python/README.md index 30af70e..dd0b046 100644 --- a/chef/cookbooks/python/README.md +++ b/chef/cookbooks/python/README.md @@ -1,5 +1,8 @@ python Cookbook =============== + +[![Build Status](https://travis-ci.org/poise/python.png?branch=master)](https://travis-ci.org/poise/python) + Installs and configures Python. Also includes LWRPs for managing python packages with `pip` and `virtualenv` isolated Python environments. @@ -35,10 +38,11 @@ This cookbook includes LWRPs for managing: - pip packages - virtualenv isolated Python environments -### `python_pip` +### python_pip Install packages using the new hotness in Python package management...[`pip`](http://pypi.python.org/pypi/pip). Yo dawg...easy_install is so 2009, you better ask your local Pythonista if you don't know! The usage semantics are like that of any normal package provider. #### Actions + - :install: Install a pip package - if version is provided, install that specific version (default) - :upgrade: Upgrade a pip package - if version is provided, upgrade to that specific version - :remove: Remove a pip package @@ -47,6 +51,7 @@ Install packages using the new hotness in Python package management...[`pip`](ht - :purge: Purge a pip package (this usually entails removing configuration files as well as the package itself). With pip packages this behaves the same as `:remove` #### Attribute Parameters + - package_name: name attribute. The name of the pip package to install - version: the version of the package to install/upgrade. If no version is given latest is assumed. - virtualenv: virtualenv environment to install pip package into @@ -54,6 +59,7 @@ Install packages using the new hotness in Python package management...[`pip`](ht - timeout: timeout in seconds for the command to execute. Useful for pip packages that may take a long time to install. Default 900 seconds. #### Examples + ```ruby # install latest gunicorn into system path python_pip "gunicorn" @@ -78,7 +84,7 @@ package "django" do end ``` -### `python_virtualenv` +### python_virtualenv [`virtualenv`](http://pypi.python.org/pypi/virtualenv) is a great tool that creates isolated python environments. Think of it as RVM without all those hipsters and tight jeans. #### Actions @@ -93,6 +99,7 @@ end - options : Command line options (string) #### Examples + ```ruby # create a 2.6 virtualenv owned by ubuntu user python_virtualenv "/home/ubuntu/my_cool_ve" do diff --git a/chef/cookbooks/python/Rakefile b/chef/cookbooks/python/Rakefile new file mode 100644 index 0000000..70a846d --- /dev/null +++ b/chef/cookbooks/python/Rakefile @@ -0,0 +1,5 @@ +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) + +task :default => :spec diff --git a/chef/cookbooks/python/ZPL.txt b/chef/cookbooks/python/ZPL.txt new file mode 100644 index 0000000..44e0648 --- /dev/null +++ b/chef/cookbooks/python/ZPL.txt @@ -0,0 +1,59 @@ +Zope Public License (ZPL) Version 2.0 +----------------------------------------------- + +This software is Copyright (c) Zope Corporation (tm) and +Contributors. All rights reserved. + +This license has been certified as open source. It has also +been designated as GPL compatible by the Free Software +Foundation (FSF). + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the +following conditions are met: + +1. Redistributions in source code must retain the above + copyright notice, this list of conditions, and the following + disclaimer. + +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + +3. The name Zope Corporation (tm) must not be used to + endorse or promote products derived from this software + without prior written permission from Zope Corporation. + +4. The right to distribute this software or to use it for + any purpose does not give you the right to use Servicemarks + (sm) or Trademarks (tm) of Zope Corporation. Use of them is + covered in a separate agreement (see + http://www.zope.com/Marks). + +5. If any files are modified, you must cause the modified + files to carry prominent notices stating that you changed + the files and the date of any change. + +Disclaimer + + THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS'' + AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + + +This software consists of contributions made by Zope +Corporation and many individuals on behalf of Zope +Corporation. Specific attributions are listed in the +accompanying credits file. diff --git a/chef/cookbooks/python/attributes/default.rb b/chef/cookbooks/python/attributes/default.rb index aeeaa77..d0a32c4 100644 --- a/chef/cookbooks/python/attributes/default.rb +++ b/chef/cookbooks/python/attributes/default.rb @@ -31,12 +31,15 @@ else default['python']['prefix_dir'] = '/usr/local' end -default['python']['binary'] = "#{python['prefix_dir']}/bin/python" +default['python']['binary'] = "#{node['python']['prefix_dir']}/bin/python" default['python']['url'] = 'http://www.python.org/ftp/python' default['python']['version'] = '2.7.5' default['python']['checksum'] = '3b477554864e616a041ee4d7cef9849751770bc7c39adaf78a94ea145c488059' default['python']['configure_options'] = %W{--prefix=#{python['prefix_dir']}} +default['python']['make_options'] = %W{install} -default['python']['setuptools_script_url'] = 'https://bitbucket.org/pypa/setuptools/raw/0.8/ez_setup.py' -default['python']['pip_script_url'] = 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py' +default['python']['pip_location'] = "#{node['python']['prefix_dir']}/bin/pip" +default['python']['virtualenv_location'] = "#{node['python']['prefix_dir']}/bin/virtualenv" +default['python']['setuptools_version'] = nil # defaults to latest +default['python']['virtualenv_version'] = nil diff --git a/chef/cookbooks/python/files/default/get-pip.py b/chef/cookbooks/python/files/default/get-pip.py new file mode 100644 index 0000000..5d61b80 --- /dev/null +++ b/chef/cookbooks/python/files/default/get-pip.py @@ -0,0 +1,21531 @@ +#! /usr/bin/env python + +# Hi There! +# You may be wondering what this giant blob of binary data here is, you might +# even be worried that we're up to something nefarious (good for you for being +# paranoid!). It is a base64 encoded bz2 stream that was stored using the +# pickle module. +# +# Pip is a thing that installs packages, pip itself is a package that someone +# might want to install, especially if they're looking to run this get-pip.py +# script. Pip has a lot of code to deal with the security of installing +# packages, various edge cases on various platforms, and other such sort of +# "tribal knowledge" that has been encoded in it's code base. Because of this +# we basically include an entire copy of pip inside this blob. We do this +# because the alternatives are attempt to implement a "minipip" that probably +# doesn't do things correctly and has weird edge cases, or compress pip itself +# down into a single file. +# +# If you're wondering how this is created, the secret is +# "contrib/build-installer" from the pip repository. +sources = """ +QlpoOTFBWSZTWaLGsW8G3aj//////////////////P////JFBGREQFYCBFQEEBQAQBQIZGUOeijx +zr0PrbAAAAAAAyhNAE2e7FdGgAA9AoHQAO2IqooAOgB0NB0F0AVqwy7dUDd3AUARTrjua+t87nFe +nOu7zngdberj52u2C9eHVAoIIAHgUAoogd6aHnu7nzauXvb1H1vtjJDANHI4nTa+CgHexzlfDyvu +2ZavN3r7vu32227LmNbmyKgNmIaeBpSmjTIOefffXTaoe7KWxq02AAAPvmwUCQpPQ9YPvtN773y5 +tGhed928H2sgpQF0Bdn218E69TGNXWc+9zwj27N8HnwpHXDmwAAAAAVQKAFBEU6fes71dZdNgG+O +cABO65C3XD0a6W2zZvcdHNTWz68CKBfZrZqhYAH3PewAABqkRoAAGQACFmMbrW+uDkFCm1jQVQAA +XsDvAAAAAAAAAAAAAAKAASq8Rq7qNGn2x0NzPq7J1t98DO8pJ7GtaIfeAfR7zMVOuOQPo+gdLsA3 +2GgttB0Mi+iEPPe3noHdiu853DZRr21ucDpR2O7VAOsXs7mqbeXXqe4eUge7z2+9ffd3nfT4+87w +KLssZvL7u59PST3lLt953u716FPXPdpc+A9MvbOgBn11PXnJe1YW+uaGF2xbUPRXDQOg3fdu906s +UKs0PPqN9voW8HIou8AegDOzu8F7zY7d7nhx7u9c3PR28G5jcWY93eh4labtbEPe71V57a7zjrk0 +AKG17W71vYD6NsAABoo+bAXu+GPedPWnLveVz3p9Ou9943eAB0GigDGumiwbSe9977tiLfOcstgK +rh2OmgBo5SzN1TrG+klurqJ2toTuNzXkdmqsetzjOIvTQvVaSYBMWNqlPdenvUA91I4QhtbWxau5 +0Vbu69TGA6teAAAAAAAAAAAAAJAoACKKddOa+eufXOtmlALAfT75szedHz7Ze2WSgAM2NJAFKUAF +BoNORoJKFAAAferSHVAEtAG02wAHmX1xd1wKkXWIS+ueHd4SduwV73bd40dA9OAABQUfe67N59nX +23ec77euK6bJbYG7AfCvRRtBlKAFmEpFRyGVIFB7uXNTzvdqveCDRgK1gRQDQc+x7zMAAbMFRATw +AAAeEqpIAAOB7jiiREAHA+j3AA8QOAIA0AiYVCJAHz6NfQMQRFOnOqzTU1q0A3oWrQAOPPgEa7AC +PfYOPfDZA+gFB4GfT6ZS1rh4++vV2nu1KOqA477t117fCe9pptr4++tVhc4DewBzz6ffajXt2Ab7 +QAABAEFAAQdPTPbwADobfHz6Th6GoVsu7lPrecAAOujgCAACAgAEGjAO97eN5gAAAB0HQX3fHgB9 +AAd9vrk1tgAAAB6AoAB3drN9zO2mPAHM9u8zvZitts96bXC9deeq1p9dNAAAApqelnd8r67x7vT1 +u7lWfXdp9zH16uPtqc+nU0zvOr0DWn333va63t9c9lHoAHo3b4ABRPbPr09T1Hnz33d0vp5zyS74 +eg1darWJo9u4SV3uO97ncjSs2Mt64d3vAfXtmlm1PvbgApzruAH1oF3vWNsA8D0sdtEAtu6qJHqW +U3nbs4wO7Wyjaxo073PK5wtcre2se949Zu3FDN98e6+wCi+7H106vdvvHgdue8p72FaAU17iO3Hd +3ds9tO9w5q7O6N3d95rzBvXXMd1l0h19G774Dpqrpq3bhSkPvPr416ne8oCtB7Nw+56eNs0Pnxe+ +3x8PmwF94MV3cW7gK9eo7r3PbnesHt995777AF7Ge066O93UQ7MbZUSKfY05b64c11rk+G6OEs9t +W3Lvt76wd8ZQDfN9NAvG8AA66AAHQttO2BvrnXXOjTrcBDexguDMLBjKfWBePW6PfA7vWO9u9rYn +rlvee8pvXOGy73u82npRba4GzlV8NlwS3ubmCgDVQ4VEY7tQDQZfbuNKVG8zfYdDy9fbOCmyz7t9 +oX1o6Plshu+nPn331wnPNNep3tzzoY9a01UWvmJNevusPrbu7cEcaK09zdIunw+S3qvWpQqTxpTR +3eT0+203mPrlKIK2zoYvTMln3vu6uMdpW4Cgp2xTu33PaSwdL6Doqu2NtYLe6dAfM9ym19w7vee6 +ERPfUAHV3s07afZm3OgJo09Hdm+qcdUDIbjUO6wAGgAAXASKAKpgAb6W1oaAAPfAAAAAAAAKAAAA +ElAABSIq33entvV4dMsye73fV6urm++AAIKyZu52S13AdAAdA6ABoAADIOh0AAaN99uoAA+gB9AA +Ohjffd05jX3rWN99t7ve64n20bVVVSoF2ALWsK3znQfVPLrpiV7a8GbWPtzpg1x94AHsLD3bu759 +pz0DQqkrrd8MbHyFAGnNXW526c18rvuHnbPU7Y3AJu7j296Hq3YSqppZJ0MQJO24uu7u7KBnqtgI +AzUyPvsAe9dgPr17o9ZfR05V3iGwvudzXO64V6+Xdenfe5zxtWUAALbRTTKsz26ffYfed6HOUhvb +3M2yRi7u+9y+oWMH0sOWvvvePNLzdNOmPBrcg7RWe3XWgZz69eD3M30rvHtdbu4uvDhyHSuuvba3 +THtq8gyaCEjTaN8U469ql4tV2ZyOxN9jXH03vuAAfL4nx8F82+7uWvhz3wA7Nt1d3c3EBB4H0iqA +VSKCE8K7bAdqfXHwBoMkhI0xnr17nd7g5Sg7xa+mRPTXt8dsABoA0GzWwDdddRwBbAAAADGp8+AA +AUpBSJQA9gyRUlSAMAdgAdUHABAA0EWB2DBPYAj7x9FagaYVs3291Nt93Ae9t269sF2e+72h99p6 +2xQo4nM+NfO71y0ipfY99Z69bfO93TOxs3Tde7dWm3pZ3gAAG9gpnudNRDjGaArgN8PQ07Y0NgAA +Fvn287nw1VpyChUD7l53K69c97lx8AyADvjKkFd4HIa+AAA9KSqBIoADrCKCqSAAB2wAFAAk6AAx +SAcHSh58fbQAABcUdep8Tnq9Yje3XPSrVrNN5gG2+fY92+onvtuGJ5vmic261ttGQNwuFfJhr7mg +y919nifewoezzvjY2LgOtG214AN6YdV6q8s72SDoAA5zkrXTivfO5wTK2Zaa2cTR31W7O4gDTRCA +AQAAAgAAARCERNJ6mTEyAU/KHk0TamKe1HqepiZGymk8psp6g0yAhEQQIAIATTTIAT1CFAGgDQAA +AAAAAAAACU8lJShENFT8jIm0Sn6npgSmNNpoIn6UZAZGRkYI0BkMgADRkGgAA0CElFEIRk0EVPaM +U9U/Ep7SfqSflQeptT1MgAAAAAAABoAA0BoGgCFIiCCAJiAAjIJ6mBAKZR6UBBgAAAjJgAJgAAAA +JNJIQIEATCAAE00ABMiEUnqaMgAMjIAADIGgA0AD1H/fh/8//uxyf9NP/1//f/77PLX/n/8f+L+t +NUcP7f8f+NzZNPZ184/9P3/qREJJIIQrGUoiimpooKWmgKoYooppn/n/845VPDxBE0rMKUIn2ssq +QpkLQUUFKFAtJX3RmRfiR1BRCnnkVY652aoGBoIiQpGYUpiKoiSkKVoSVkopKkkmQmaX/R/z//z/ +occABQ0NMQqRItIB2kTJJilClCqAhJCQCUSi2DN/n/8LKcxIQC+bRRBQQf8i1RC9L/odikDpDShp +bb/9/6P4+eIe//jaAH/s3F/+v27eIwnaJSriTaOzwf3a942P/4bedtEn49OPb/r/rft/zf0yvKH6 +ElQf9isVf7L2/LvOi/YTIirVf03vr4f9//+R/7PT/c/8jv/zjDFYh/xf8//d/899aXw/64f+2JR/ +7n6YBBEiBMJnZR/zVRe/jVP/y//eKf/T/zyBAkKP/Wb+gNHgiFb1AoIOJXJYSFv/+qLXtvj83zH/ +x75KN/+foAeh/9nB+2T5SfMQn/dTqa0CgOeO7/2StStUn/nzrX5GuSnazmgRJgnGJgpJ2lzF/nG7 +wCHIDz0ektFBD/1IaAWbvrBogDdMcBgXubHmu/cU8donYx3cWcX1KiD/9P/bZkiihfjmiqFR/lf/ +dmUOPR/8M88e4H/WQBgKMBwHIQA/5ugZNChw0VjdT1f+gP7yMcSYgWIT/pVRhAGzH2vAWMZAap0I +i/3VGgS6dWS//0xNKhUSgWf+89CXXjniHT//sdDDDFQNTbC9AOPko5v+mjU5ANAN6h+HOdPoyf++ +kge5Dn4/lnh1rPMqI9ArPjhmWoUqUJVdXUvm9N7QZYiFWNGbGXlyF4X/p9CkV/MIu+xTqRq40ffE +B8mNaaxsZ5X4ddf7YPb7uwlgsDvh36XXl+ff+WxpnjKVB2hRFfBpny8tvA6E6t/Zk8Fw2X4pr+pQ +QgiGQTy754Y90omVZa5BORoI0np3dNwdzFAApREGUpXZXQog+/tVO7yUdRSMYFuI1zKE1mdVH/WU +GIgB0ijLSuQomnco/6Maxjv301bdS97Vx5YgphLkI8sPbezvEk3wwTPKo336A0f/wiF3LXn0O1aN +nMca4ad/04AJ0JY/eE6qFCJUUORUm6jRsVPbgv/VQKmKIiUhANONQXInc8eUXSKzCsqgDulyC8H1 +PFBoupx/YzPhrUJkpXlt29VG+B6rYET9T4eIfoJESkZtI2O/68WkWwdNvG1eRA1bhHUmsPVBehg6 +Ky3TEMAI/6lA0C2O86ACT6MUrDsL4HjnQNz3LhcKmz21JINtqoiSpFQ0CAE/8pRE6wRUBxKAB5Qq +iodIcMl/XbzzxeKoJQhJ8jX1uHQt8MP/XYFwRDbCNn0GVwH/v1RvisgGi+VvOs/9801WCegRzFt0 +mUVSCmkz9RRfWj/+Nv/W1VUUhAYtE/yLgJh2dfu5AOOARbC4awMsrLcuC/5MBAFEkOoo8xVc3w5N +75GYom6Cxdy/fqJqS4DpI589U2J93NYwCfOeK+5KDuOCHm0xgq+AUeSi/eeH4QfcpHqTYds7xkx7 +scSIEdPh9HAADc6MoEFBUearmhAYVyDb9e3fcaq70UupHNB6gAgqb5FYNrPiFox+D4+fzRBJ+lAv +ViudxVQE5gQkQlfLO+L33xwRJh0dG2O2B6v/7ZpTt7Ov/+m+V61e9zfhEcV0FHEwldH7CRIKVM6B +EQAavfMOy1+bN7ZUb06mboI4EC3gqnT+sjVvBWlTZ7KgH684Zk/sPji3d+7vSIKQxrk6YqOSYfcM +ANHeoCCv9qiBEd99+Jp3hB3AhAg6CgACgjb05H2X7ok9iAk+5dMRHJ1Osi8/DM5HhsU3d5iXzXqe +IHX1D1a0Cz46pLY4NN32PeOm/OOmr3ulwRYL61u+GA18/b754q7QAPgjxIAm/Qv7J63ZzEmwpX9p +rtVwJIdQwyy5ds3rqiFwNCp0yaljEYyjhysH510Bv0Qc2z+X3pglHlfY5V6dyjhkzDcvKPJ4KGqR +ERABP13MgHeNzKz1CnXe8vPdo6+8SAiQS3muSYZoi5Z6l08c7/SLJ+/1i0D/rnp03pdXxbB/VN49 +nhj9Jxek4VqMpUnzIDMOOOh4zQWARFKBSCUSUojac0jw+K6h0UeyKXyLjC7vYMfHUoo3yiRlchDH +5KOeoeOddrADICUOw7njI9GcoJ4EszCeGVz61RERAAssqL6eQt3OG8/DV34nivFNBqyvOYPXuLw0 +wvBBU3UotC3SV/U1bsnTEED7KYpRfvLvcLbceWdB6lAB1c1w+C8r5FYCh6ALZrFGTPfXvhFywO9P +yb2v+8903Tsc2XQAQi+4sLnaH48/+PTFXlvx4rq0RMnKTVWlrKdPs+bv9fvLZkk1LVrs/jjshT5+ +3to9+Zfynf3V12U9r5FXeovxTZm5ZvjM1C8vLrxxvX1zWzpfZsFjFnz0u/Gc+/Go29q4DNf57pKI +AyrY3fkwzrdEp6s2lsP2Z4nAK6PtdgLCDBgioNLg5OZ6ajAOBUA2MsUTMj+luOYXBQhkCBCANVGE +OiOPqv0wE8KceUCJzqGa2yNA5BRwmySzTTS7Kzw2fq3LL84dPZmqnc7803ccazTU9OlK77jRe6sf +P2+kLZPPN0jgJ8+FWKpE3VvHHLVzX8r/lvud/n5tv8ab1UsfLjNkfB8tMiyT9hgLa8s3q9cmF+JB +N2qECkS9V35HtAcEQhACgGOieLAKMnvRzTza+qklvLxOnQGs0mz5jc3hKtEjKkWrKsncs/SvRmmW +ZQBRJJHPU57vD0Lnb35qeb8o89aC80/jrt33yy/wzzJ3ri6PW++udwIBYZ4Ua5I1qRl63rXXPI6h +pPx4PGaLsL6gROWKamaoPkQoPee/fXkVKDNUrVSEjAommRBdGrethFqIAohyiRclw6QQ94AfzT/K +oPvdD4/LTvWw+6PxL18expbU0/y7rve+taGwo6hR4kpvC79KB110J7FLehvhQLDhAUCMfhEthvys +wVrIkkEykNkUMytXxWJSXWdTYz2v/P+gP+yPw6+LfCpGkUCooGnUfqJE9c1TIPAj4n5246xkoolU +CnXGJvz3ud8Htv+Xpg8aR+kgNt/wMY9J2Snr69HPfhqyR1hES4gAP/t1PD7tqbCSCsEDz/f8Pu61 +244lNuLjJqbm23q/HX0ibe+g7R0ThZ8NIvEeEPeVR99V6IkO+g3WpvfpR9QiTqvOwcWHr4Rx8+Js +R4meCKAOBKqlqoushBgGs9ejTyj82Il+bIvXvaN1u7u/dc0vlImEVqhLx8zdDXofGKN50rnx4Lvx +338S1D4r6uRn+iuqP5/Wf0w0d5jb2XxzxCqnbfr5pZ59PXvjkr/X5hbXe57hr2679xwe2Hpevzqz +a+dwHtC1B5NzuDtFQOljxwyyxQmGCZUt9QoUFECQKAY1UJVBgJRx3eGO2Z6GVJWkHgQgSr7yFYwN +WKEoFVetHONBQKHEDIFcGcooyZXveos4a+V+sfO6WkmUTUuVgFBIczKpda4PcpV7BheDmcqUPYXo +97lO5UU7Kt08k86EvjcyhzMWJZjXhPxH3M67cPH+s8dfD2Wenv7ziaIiHMtFxct9NxdLqvza9vlv +se5OE3dC3MMMxWxMFmV8FQ0IIIp3JKChfh7jdrtuj8/1P8euhnPkJl29iEcQwgsCWPpdsoQKMSrB +OuTtkhuWbLtVJCkgK38UKJKLXFQTt8fHHyfAjZStK02BbyrjiZzoEqxUKQQwdLsaxThcj/ZrvJ5n +sn9rbc8qdWXprprSRHcpupkAUFornP1cvQ4ci3G++lqfSZEnrgaylVAjUYeUUxwrEO3fGl4hKSjy +BG79b69L0iInKg3FFli3umG8h88X6dDHNeIj4QD8L8NFVnYs/E4YwNhCP44JKBNSGrvFVvOARBAd +5AWJQQbxBUrLulnZ+d36E+7XLWutnPKWpmVMMM/keMmCWxJ3YNOcmYTRRYRTYJRKY2LDcJHRVxvv +TTX1H6roptQ7+Hvn5x5qulGoHXtXoJKBc9fD4eN1V+9CdHPhLi/mcsNe2UidaYkstPbSAyecJogO +nMLzW2C1weMMsQk9ZximNEIET2qL9+dh2xLeUS5HBiNMYWKV7DGcl31KHBLDFiWZYrsP5XFZfgik +EGNyIL/xu5tLxGRVHKbswV4S0hAkfUk/HC/Jx08tckhySSHVgCdDbywzNcn3mpI7+6eXZHBykBTs +0MKcKo/4UIg65Kp55MN+MDOREkhp9BVhm/I0rNa24llexJBIQG2MG/Xiwc+fR9RCaPNjj2zPwahO +VkVPpeE6ulUrBbsQD/u9n3Psh3QowQ+46vqiXlwfGAffE19woC8FLerIONOIN2Gs/Ij8h+8Ubn61 +lvQxZ8q0fFPdJG0dF0k09MmT9SzXg+JzYVnrLyIsZDh8rbElMZcFCgB/OtfLvb042Z3vomr9+MKK +u7wofMKn592dz+sJMRv7Bonc84TTeEifnYqEoBJgCqixJTJ4SVRoOcvnUDsKN4KlhAkKIETr5l1x +b7XBJFwN0lEFZCfL0CoE3EJg8FQBxAAY3EJk0aAyBA4h+PTgzjG5RZeok8I6WUzWZ+b3EGzRreIn +dKK7OJvWrbADEDLWIxdHFHobZJR3GHSo56FrknxrYu2FlD7sADSY2QNKFA7y0yuRoc2Rc1Dus2ff +PGK5Xt+izwvB7g7NXQHYFwNFFpYPlcOhnBy0PChwYfpd9/SlDa4qcumSYvBt3K30QJcxv787LRos +loJ9+K4S6s3zcRUQ1Wul74XlSncqa3wkDgpEhdKqDmOX9avPW+F7I3hwXhT9N+v15BZ+ptuXLq2L +tGkb4Pz0y944ebV9dn6jRTCjGUsK6w/MDg1R+XZ9d+OYCQb8714Vc/XRoEWyaKudK4lDfecTkVvZ +clT3grEGAs2ry/OhsBAUUrb2c3w4AbQfJ/LX55IJ0Jfxj98AP3/zMWIoPqhTU5L9nxwP9P2f15OR +D4rxmdsVXHRdx29AgEAsOkaV2MmwiU1xcoh3FETvA61Axjs7ei+F1bBBREO/UqOB8CEwdPkNddkK +VpZM3wqBNFufsiF9/pvaMIAvXfsdvjVAMfSFnUKEHCUnGdfv0PR3HlfDhNVqa2hZAZ4nJaQOngBz +ckBvsihleAmiWGzR1U7e/jlmRS8EBedU+SP39MGGc3eC9pQ/UTAYcf66fXxv0TEEX5cpF52dkh1v +o3fvp++h3qVJhF6zBEGvBEwT7RAFMV9f7u/P8ueVN+x3r13+ML2hHiAMO/sPR2ckBDVmkjdCULFz +SBOvmp9x3F8YjI+jzz8MDh2kDLtVEA/MeVgOu7JZzzX1LwQRpXyw6MUnGjysrMfbgr3+Pnem1GBG +QaIluv4L8uKGbSOvyi+MnGwWCoUhhwDlBRPu0MWYfR/oX1jI/K/aYyCeer1cn5YPBo048VRNZJSZ +6IMp+COrQop0otggB/fwGVv1b+wn6EgFJ1diFapSu54mKNpXjx5dnzF5Z4I/qxQpNRTp6SikEKbj +yNXyfpGCmVbQw+dah+5QB+jnh278TNj/oHp4E+EQrgX6dJKfs5+fVgalADDohPVpYOBK8FQOZFiD +KbFvxLLZMdt8d1/4dCGQhcGjTbigAE1iaALPj5uEm2ArNkSglEjUUXyHMb16zW96W1kYaVt/jNPo +fTp1c3vTEOxZ6iI1+JhUWswx4SM5C8tpBFw3hHjty/jAeAKkTY5kbcQmMpjvfxqIcNkfTM5Cyir9 +KORFhTCQBGENepQgyX/JkydcWmaO6LgLEMiAdTkdFw0SPbueBaNKxkFQVAgjlUR1M57jN6RB+I7G +AUEjRwZxBIu72XsFusFfCaMcX/ox0CFcTX14AeBdqb6NTwiVAh24UIEHMA8SjGRDw3MPofksXg8z +KhM4ClAgyWOuPyCEUoQQqEEXwwdG/7+RWPEVUQGG6ozzzQocHFgx9Roezwr3KjiP0vQ15k49B6jh +e9JJYGsuEhHufJyijoYMiQKlLkIoFfRU4ReNcOqOEDKugylwHd+lYgSFE3ogIQXin0UllpakEcCN +WJUmEZWc+Ud2AiyE1cTBEx3z/pnDr3bYGITcSSAglkWEnhH4fLxdjr0+NThAJAJ2LTDUIf1tQsrI +PV3N5gGN8x9MNbERWVRvvVd1wFX7lTmjRFdq44yiIjp8MVJDrQr77gGgUSUMqIl8hdW2+AyOTWY5 +Z29MO/JHjoaOOMZZaI60CKON4TXSIKppdKrQuT9olMXfqZVhfbzDCPhFR+UpJfFN+z7p+lCx5NuN +B9EL0XwLuEik5ZOPobpNo6IEkCpHJqSNBJt2LqI2QdymeyfBR60X8a/J8D9YSeuONkuCHFRoiT/Y +NP8ZKAIR5Kue/9tLVgYxya/jRLoKaPFY1/DVs+IgWLaZfobAqN++FmRv+Di5r1Mg/bxNOkQv1x3I +HDcO42dcCknFLRZf5Ona1clVMK1qutqEKDJO/uk12xURWNHPW+1hHSKrF5zb3Xjk6NXv1eqI8bsv +NjPVB/+AKdmYN6/Ywb1OYjzKBlXrMA3ouAH0fbzJw3fegnmqxSauMo0aqoaOlReKc0/YwXFztzCR +cKFAgExEVlwaGT7/hFPMm4UJhG8SP3oFIpX27X94cICcLYRNc63dJTCIZMQmmDycIdqiSQxW8jct +Pn7cDOIZh/EgUQPvLcYgkUW39jcu5ENsHcrDYQHFBUecyyOdseAEft7GFBA6LYEIEwYH3EJOqojF +M4przu54+4sEnrXPTqendhEOJNYlNkg2RL7mUjIqg2qp7VhKs6yp+srk3pWii7ZW2uJZCJctDxt7 +YMKAs3s4M1tOnwDTjTFjKCoEEEyvfEPdyjwP0wUlBxxrcA4R8zDdPhqU1TeKxAwsey+bwzFO8GQZ +J5Hc3nja7QokMezLU62cKYBa4wwmQIxDg7rBeQxh1655XB6rBsfT33j8xDNmKxm6XKIvrLE+QiMs +VKIpATWqaGUg2vlyPE6QEBx7VQIHTRRdmXTKML1U0N4WT5FGkZoOomOi2oU2xyoLemyFimDKSSoj +nnYgPgrhCGWS5lEJvud00k0YUTkymXFPLCsTF92dQAOfLkCBkGz+WJIH/dQD/iIUJ0Q1arm89ehk +mEXlkdnfn0eeHqh+Re8Xpr/Ors489Y+sWvSv3R+yOeD6Cf95PhNK/of03qP4Xt/y7iv/XACign1Z +ezvuz2R9tXjuQqpU3EDEU72wAkFG12j68Q4BEHOkE+BEL/qOoXRiIgEo8jJpVNxr9lGT+0jld1lM +TQeig/rau+J7KE1Hm13BgMdup8T+Pi9tvp963EjqjChMJKeyrZSONLo9e+PE0fPsobu63L77viWX +9GP8lYTEfpcgjc80D+ZA5xzLZgWPc6w4WVAgwEXFKRj0Y3MVysGDecrXvfukdo4tuqHTPr8vPqzT +/2OkaMxJ/q2kBV3xB79t4OCZ7MTDrbpeTqegJA8s37lDk2UqIig2YtkzthJRiHsorvKnHmmZBr6V +RORF7iqHs0BqYSZXDKOGvUIDKYbIyoeUeCbMkT8NoMQScoJP5zBkn3BUJQKaM3r0eHUXkIQuhPX7 +Fw770Q4zztV4SSHM9EFFt4M61vk94VR79NX88OWwxUcDkB2QU8Evu64GP9xEpQbe7ujf9a67If8T ++zQvDLEE0xQf9eyAJHIgB7/rRLvrF4gggB/1ob4GaCIwGD+bBpxCOAn6wMDIiSgjnSuqZNeYc/fl +eHamSdUcB0EBgEl5fK6vYkXRS6xqA1J7WvOX3PjLkOcPWKWGq11MYj468WHVrrd656fCSnRrEx/6 +Tr292rtj9uxHemuiLI3XiARAIglH9p+845P2F0bDKcQPij67qeD/V5Dn7yTnE437NKvhTRNvCZwi +TdekDrD3NzkHVr/Wby3cNPWPUjVT1dUnt+frZnbLF57ZtsJR8eOxyOMOb754x7+2TU4OvF1Hu7Nr +iBHnRbDbASgeQ15kE2bDqs8fTo5B2wdjdr+oZu5m4cR46natBXGqnGUyFEpKYTO9yfQg9SB+Bhq0 +M7dzNxpD8nOX6o8z6YgoPQR4CVao44WI9Xxrm7qBQ5ImEFAppFwnQi5Wdhf0/5kxJoj/AO5PFAgh +4KiEtJB+aT/fv5/6dJ3yqvCqziCD0RSCaitFXzFKusIey3o7Q5g7SdOX7TVE+mPC0Z0JpKDxQ4ip +IBDKgWZUClEuVUuookmBwonsuwSLg9rRqeMCvgJXjvlEHhiIziko+dSN925o8DDey05QwJsNDwxl +FApg/QoHJ7t/Botf4DJlz4r0XdbVQ5Z3mGa5BlYLey3HM4RlwdhdCaCmNROjgMrCGKIB0VWmcTxh +RDpM0IKVZglhBBNqGNRJYJ3pc0m2tj263M1gb8H8cDpOexAoWF4wMkQ5YJgiwVsJjYd/nAEuSkUE +1GfGwqan1QupMJFiYqA4gGySBb01ASwmA+pURnNTbmXgu1HQqsUVGmzBBOPH7JcaGPzNEPp3lg41 +mMnsK6oChOHHd3c0rLjah4s48MJBRP0LitOA+nTwEg0axQSVudQsorC9obqsU1XUaAxgU0uFZpnS +GggmRFkC4GpaATAId4wrSnHCLbhRDUQkMQDtIViQLiDgh6UWUBBglxNuIAhGTJcCJOpkaywXBw2X +aGcATIpa070AKYj1ATmRvNCIW4yQGGtVNXXPY2x0uJQwlm/JXbkINN83MBNNiEGACHG1Tppa3uW0 +cVtYUiyzSIhkaKIBRHYnZwMc2FsEIOC0htEfAF4cpjDAG2xiBJ9frMjeKhRHZmoRCYDpvcOwghve +Bz/PoN7fPfJa2lCQPTeK9VAutkEqYh5udMi246UYUuxlq8Co0Zc6F4fdz/M7J3erfziRhyEChXmy +sUPazwpfjZfOFjGq6W/ryM22+6Fh/vg9A8D0Ri+i7a9GbG367+PmfcFKwvH1hz9PQKIOhpYR8XO7 +uuGx52lwxx07RyDZq61j3oacEnps7+fs/hk6TSiVJDtoBEsYmKQkr6AVEiHd28+mD4mIf9tC8E5t +518EBG5mPymDJUEllDy0jT8zlmk4NQuspbyy9OkDcJzT1RLBmBXUsmiR1pCVf42bykUA7+31nrD2 +x+Gs+nPocfcfIzLQi4dR07+AGPfeBzJQPqQBwt4uefqcexvPoTmEknQuaZFF/GosKVWvdoTie4U4 +70UTCAe4vDe8y1ziR/nd2NLyy1PGAdU3HzhxPQ1Zrcj6en0NIk9JpJhIkpDqKloaSCGJLEwG0oLC +FUsylmQZWX2R9nvIlrl7p/iwYN0O+L3T+gjx6ez2LVO3rgmabDmmYNp/ataEqLqp1KMPUu6Z6vQY +SLrLnLsp53sMzWNDe0NsN7o0wrUy9Jg+nnbrEpMiE8o7DBgNkMAyjdScETvReMYBgLYAhmYPZsmb +SjNZgYNmhvSC1eAzE1sStU+5mcuZxZrUqiqmZnX9fl2B0WDDz+we7D4+3wHzY/GQiwwbQruWwret +73t1ouxbH5Zhvrri3bpLyMzDZNXX45tDZSaZ7Jh0yUMdDsy6AqIXhQqEAhSqXotrxsYRXsHubkxO +xDDPkfjUssTi0kCFUSwen+utEn7R9o5SYKU39COXtx0b6t2jRl2GW70QqKFyheZMYYuagfy/T/25 +H936h+Px+Y9d9lkT5Denu/sa+E+vrrUOGgtXshRA1NO2pTSkBM5MywUpy2Kv2fifoO6zh35HqfQa +fXmtdeY8CDwgk+lovVd+I05z0u6cNvSA+RF5UwzY6GRlQ3CKIFkBETdxVXYFvdQNPcw/oH5i64/U +c6LK+UHtrQzgoPOoD8Q2U/21vKAuMdhMrMQETzBgpgQC5zAONnX0DVKOiWasziZSxCmt1IpekHoa +A8BPrLlg+ITSDW9VM/NmPv/8/1v9NbzfP994HLdSmg9xicLV6iyETMv8P5i/z9e8cD59eLnoPiHQ +n3ReiKb6T4Hpiao24WITjuxqXaSzmru4FwjaVVXYbvUZw0v1gfv8RPfOtiSA+1LbhxDZeK/GXhBy +7Wv8FqTuXx5wvUzWtfcfAC8f5VYqGA2B+7rnQVHOlbal6avNVIlWMmSovxAibehRETMRH/Dz+r/p +ptBuBdlZ3ypPbyG6Kz7qmZrxLvBiE+VJmor9zvFFh+UQ+7S5Lq0A7aQ21XNv1ms0syJdPpQRUwXe +FD6H2fv+H2fwH6PqmBDzUj6UCh6dWD+gmfFcb/ZjmG4HeH14Z89l+865XH7mj6uyJIsMvFcJDunq +sp6yEzTAKkRDgItoz/VaTJY+K8LJQ6USdCyF38fETVpWUYEoLokXmJIWBUjKLSgpy73iOPCJml6g +AuIH0O5a2YMU30wR1WaDWN+jbB2EYZnHoAflLR+tAQ5960IFaph5nrkCO15BHu5qhESEcbxv/uqF +T1CV0FgjH0hTXhfEYrwUB4FFgpYIBGcNY9GEC8MqCInyvlx3f8/H1fEU9PpsT09fo49XPL12lCDp +I2qB3wQQqaWIBDxCTlhpIDc+gKsYfeTDnlCWxu7QIHqB33QDtzA76NLAeQw35eYzNqmUGJRh0Qor +rebZpOWi61DYyk5MrthszWzCggZF3GiCHNlnF2t1qHI31OI7wQ2PoWCAYn7rqvb9df1z6+rP2ipA +RgsH5HIEAsZj+GI9/3AHleiMIWgpKRp/BA3+3xH9nnnQ7e4n1fb4AK8SKpSUCwQxBA+/j1pIGHl4 +J9M4Li7lqaKGmAsnY4V0I9DkE5T+HxxA0dJ84i6W9ZEm3vh6BuTSHP1j1+pHniChzsoJ9D3o5N6s +WRBKRDB5k1z7c8/9A8tH6aPcek/Sk6Mnd4dvP4BPR66Q+wPOm/gkIDB8alvncS3EndwaLudzOrke +qKMkm6osYyIqT5wiejhee8wvdxW6NuCmHDnrieu4eg4+JxzxBUjLGvD6OU6mgPNTfQQnFU4vqORT +qdk69ddoKN5jEB1jF3YiU9Yy843yN09NWQ21YcOE2bONrI7FADa3A1djUEposs1g8qW+vRr5y+vo +PGGkRYi7R6Q1vN9G5EEt1qmoWFNFIXVE5HseqdifI4iCioC5Dp3lOcsVyLeGo1MOdeW1Bq2bhkVR +mNmch7f6M2DZ2nPhMFELw1SYD6CElkKtvgw0GA8DBDAR0JYECG3o7eacvp/l+B069EVSEgiKHa3x +SXvyeIwM10zsw9EHZ2YXlcxMynDYpBYs5ajIPFHu65vLKREXmzSJ5cbClJr3P6eD443sHJx8dXGM +ob3l8RIV7b4n2SCe2AAyRClQTcPJeHyM/O840dRsr4MEY8403CpmPAzM686Gq9xlJ/dLcOQNgBBh +goWXYhoI7bMmSbUCjHZSw6oUS4YGVOTyx1kFIl2FhEVXt96ARD3Z8HqOPcneekB71xixn1xo5jlM +mDDsTjzz5gPUY9on07q6gnYwR3w46KeNiimj4U+Hk97cdjn6q+IfBEQwvUOETQhpNY875kLrDhfl +1erKh2WJlzvy9A8LlDv0jBPLFAMAAwnzuOgU5Diz2YciXwIoaBxdn5cCbNkjwdcsuujUf352qff/ +Ljeu45dYBpFmUApKDmAP3dkHoBw3qM8LXUdqHv9T7pH6fli9ObPtepgwuZn34puJVBiIqCiKiCgn +xiFE8NuwavpdwWtuPoNg8IcAgd86fNzOjr39Ic9ZZkpApQkhCVDxC+2DaFTliFPX677WbqhJ8SWj +AURn4n9uRrv2mILghAeUEV0piE8w7gwohFgk8LRmAV6K34c4Q3Qvhow2glNswMgvJhZubbcN1oUv +CEOFKThBRbfEBiPp6j13/WRv26AQDsw/CI07ug+hQ2Ah35zX9/M8KS6onmHF7jpuOJsQAdy0JpDQ +9vR9P6+32/e9j9/o8fA96Ps9SE07nTJl7LfrsQ37PZB1DJ4UQkM2KQXIAqoswKoiLIgxuc9SkY04 +Pp+Dz660SEnT/HHv5UR9t09wpHj2evl5ZuGkWitaRt6PPy5Xa1YVgARhCiIAVFoKsE+NlREZ5iib +Ux2pYCiMYV/P7A7XOGEdNjwmIPsDx0nVNBjX9atDXQua2pMG6FR0a0GmHy5MMtsCHdxub48frEIO +GEg9hV4EhyZBBgISCYSgO5nEYdzzv7dvF0AFrEE48t6OGPKpVOYPAQNGze5OYGUzVFcswTgGYmCW +GBWzn0wzbYB50NAxBhpCslQoOucqjBKw86V0IHNMd4aCgIWkpbSJjh66QztTkyYk2vTwHQYwNPqe +7KdWTx/JKdwwA0nLrYcJRIVFgYD4h21vpwJ0Z1FUu4HN4bIFSn2QmQo/F8JAHsjviM4XjL09aaAa +Aq4MBNIRx0MHgJTjDFQ9P7f1+v4n2gBesejmdOWH4VA84h2qO6WYRDCPJw23Lm0sMmNFQDjCQnCb +70Xy2t09ojr4SGLwyY8YhwbTiARZkOcQEgZGkTuYfsQKbhmzog9pGQEZx3Yt9/n2LFz5raubXY3M +sG/cU7SAbJN4XAyyob82fO8PdSOhY69T1FHAfq3gFldKw1tErATaMCsgPDYW5s5e9pt5t+ejXKj9 +tNvmC8hKRviah0+9E97NUSSxWhtc4UPnvOHDfv5nxxkBJigKfV7frLqATZA2BOx57ksVMzY18OXo +CH4erzoRrChLkBCtDwvBkskETRRBDoKITMGUOBccO+6tN5lsQIijE+5rxiQq3Ys+BwCo0gCZBgJL +AQ4O0FJRuG8efXnl5DNjCXp+bKEycGaF0icDG5EXLY2cFEYCNFSDoZNisggQAIZ5snTrPnu506Jj +KFmUZxS/+ufx7XJFJk2MatgyWDdapgqcunl9xjn6/PiyLbHqi6SEs1LdxVB6nWb1lwQ3uL7/h2fV +v2M4SAoCPWJlAVQqAg+RCohJImrNpx6P7fJ5vsPTwDJUkv2ZwGGp17u0oFV7KaFcQ5HAkAKqL11C +X9fOn0QVKdChRJSRVIO3vMQCqLIgNbxbZ8ZfTJRzavG0VAPKds2kO7zHfG1F+8F+YWGmIRX9sopI +G7ndEOBDx4t3/qZ4iRAT1KJhi8ZvPv/Q6JzF0l/h4S+PXdixWXA3pQw0C7Uwl22zwywQdhTtxhUI +ICICvPS8AS+OZV5/FBK6GtK2gHRQUIPTUxlbZGH+tC3fEvGumtzL6L9fp+Efdwm0+5kh1iiHs3kC +yHpTPvZ6z08ENmGCawdwwMl/n65Rx9OAqMizlvApyBJULcUNws3ZJiMyFpm4nbWpoEkNoBwEDRSJ +QscuHBdIYUQ0ZMvGaDzmcbwlUZp3wwDc1JxmCIQRGKEMFIR7B0ALpYwIhuwgRDPuAT63dnr92yQk +7PPyrjv9vLHb9aFl5qM3u8COxesplG9E7w6ZJ6FzqISaZvXGGFTbSXKBVhB5il/0r9Ox9mEZBcOi +q3tv6dz+nqr7L+mhwsphEHOKglQr1ZGKLIGREDFEv8cEBg5q7+PHFFiCwafsc4D02i/32jbL3TYL +6aDiIchcw2ZHB7lClIFAjURShbsIHN/j6dL822azy4qpM776uHgysQMOFZSH5sB/WfFB2cKdPVIM +wXlHzaApk4aQJXgAel51EWPSPgOZ1vRXHOqvwq6xwW1yLC8rnWYFtA0OtAzBA4JhFwcOBAeAU5qh +LYoF2q+HpODWqDpvxrjKakCqoRVIU4HDHKubHK19YihNOAuF2OyyXnHNuFE4QSEBM1agYsO52jEg +jv3s2TnvASc1wEqABU4RBeoETWULfXmhS2tbUCYF2dMlAFIBG7gqT4NmC511PuM/Tfc8bJ6+84Pb +EGBC7qTQuh7oWNp4yiUKnQKNHAEwd9DQBIBjKXkzUB8nWt4hEJ7a4jT/drgZhFtzJG5GxuQZUnmX +BngWne5Y6nYAKibovMyJV62NgRVUCbaAAzM5VVNStKtHYogK71VA6jRbRnDLCBm4xNZCSGiaRuLR +w6qgdHYDNQ3vBtW5KNABgQ9Y41IDASWBLAk7zaKzUajVaFRsxrInOjjjWQPig6EkUUjnUsgGrDX5 +8bw0u30neMmU206PjNJg4r4n6fL5Nz+8oNETBdfTUGJpnTj66hpjU4HuQq/sZmzMBVPisFPCmbT1 +Eym+P2fwdvl5/w/21tti9cXcJR3TCHUyocCTK9KsGwIT5GHCCagzEnJuxNpCm/kB8/2fs6AH76bp ++b+PjszAJs+PDXEVjgBg5DHVPN6T1cu8oiVTy8iJozLhnrUywYWSxIIBq3g3KGjoswZmxHNK9PUr +NGZLl4gzKqSamrP9oAtif17Kb3kn4EIg2KzriqD1HffNO9ygWlTNgCre3WXmrfFMqcqp/aAYd6vg +GL36p6wgkB0KKbB3r6c95p+nDz1C/AXibVcgDX1E6f4tdVAYMzfF/lqm9CBD3ht0Xkwm+TM6TDwm +Zp7A763BBHe75KUzQEMK+IL/ADxqvR/j9XkNmr78F434DpAFxHt7D0yJqZjJGB5gvVVlKvkIALFH +BXOgKwKOKOhes98A+rqKa7LXTunssVSA66CxOS1cUCJFG80ErUpVRKp5+t5izY2ZD7nfnkVNa1rS +m+RQttLFqohHCnCh6Xt+z29/x78HY710OiOhJZEQgYXbzMyEjEUfSKevs/v+z8vzYlOzugrMxBUE +MVYFSpwfYGhMKTEDGR4zYsO8V0xADh0R5dAl0RAOMuQPxO68f9U4Zt+PW6rhsnE3WohiA/DW7u4q +g5ykSsjbgFIBzYRmAG5QBzA5Mexuma7L/Dh8SwilnzX0SU4ZWF5KWAyQy4OekDCgKQT6K3pkRsQv +5/RyXwB3bdyAYCAASEBKBB4HnGDBNrsw8cRBpwcnoSHVZYrqvstDQEHsfc4QAZcM4iw7tXTNGUBI +kAK8VSEYegTxR8wXEff4bA8+/Pz/U/gD43PlKOdNZrCKpoqmk12dP99AT783x1555lyaPbR42p2U +tUURNJG8edC7U0e0d87E1wcwkaBH0v086AQjr4hB90+BKEaTZkxO4eD2QBKpI+fPREIlWtEvlHIE +CVgIgEYNEMUf8zzaHg6XUAPSLs7J7T3zoIh7HQWJaUqoSGglPpRzOPn8X/Qb6+MbsvXwewH0bwXz +cDHfn9P6+lPmh9PaexpM1eoS6oJ3zxhNcqp4h1AM4OB4qEjZY4DtJslJjH3mYbobs2oFN2mmVj4Z +Z6frv8U/475PMGHg+dO+CUgiTOmFe9GCGkAV3ToDBThyBnlUWDnPLQD7BAhMwIz933206b2nRBqY +S7oMUFkQjKGNRWGWd2AI6UPEYCB4wd8KmtocfX7v7fuv5dAOiCj4uIJnZKKnV9WRGUDJHD1Xu0bo +FERRrRbaJbRZRgpUMTuDkGwGb7scwrdaNe8mkqYckbCb+x+b4R+SeUPk0tlyXSBnPH4z6uEZGj3P +8z738uNEhTaw4mNHZBTLcB83+HknCUXfnSdoR5qtWVhbCi21b7jJUIwUPabDPP28bD03EoBrs6Ah +wOiJ2PNJuPArDbgkneyFA2Z17yfG7enr8vsnkHwQNdaHxAkoOugKICqYZzs6eUBp+pN5dQoUiNVR +vQQ748CCVNZAUBSAoHbHplSbRLm1+3hZzdOjuFWu7vjAx3xPGL8tozJPn4/j6gwPH+/1NzY/VnlD +tTFSHpQxnSbQM5cp6lqDAYQoFkDAExAILAtuhEFZaYXFg8B+ng3kQUFG7ryGHlU1dxTbWlSspUDk +ROe50FYm58U/d9Xo35J8/aJ4ncwNvmeIUUT7Pu547Fkj/GMaToYAVPnnVPs7Hj2oSxX5nlz5idEe +n7M5m225JWb8vF+e9IvthDxgQpiSJBpKEUMgAIKUIQMgaa15YqmpTzi98+V4u0fAjnqhG00lvgs1 +D49GcdcyERR+UONkHQgUq4jSvvnvxx4IcAEcSIdE1h267NAhvPfBsINDxLUDLtMLFMlWKYzSXfkf +z6+j9/qz/n+dJVAlQIRNixyGVMJw90YH2QAUcgSxkSKejezq7lAp8/eZ+Owz2t7n3+h09HGSlMIs +tbOwdk3ZNsWIxblZrw1PbTniQ1ajr2H8/N3e7fgO4ec2/weOSE8X7/lszjzaVPJabB+yEkP9vSCq +DKKIt/ILjCzy8OfT4549kgbGQLCCoQkrWm+z0skiAHCTn5hWLAQCUtKJ4FLMUea4EEXDAioorG7O +PSFcgGJBivvTOX4AqaE6HkFFQWuRAxJ2zl5HjrZSfJrzZA7drvrRkSc2RQKM2b1u4Z/v5f9/f93P +z8/rcH5RgdDO+ILJ3g6ggRiIgRiAqFAE4ZI3OJzOXB2+eSSIdYSqqjX48aIgIwQKNmfMPvoGDrpe +QkBfK9Al6k6Id1YEwKIEgUSZ4mmVJQ4ocoIU82YZxVE0YCo0hZEkzZvWnR+4DN4Ap/6erUGH8e6h +E2FMJEly+XkoTseoBGP81veiBE3kEoN5QI6qE+uD9m57mjBdB+XgbU/nwdqiagoKTmdv9hdFivCE +IbAnXGagU+mAwrbgiYfs664P18Pr/T365T+MvlKP1SAHMA/slExkpfCF9BMTEVFSAH+cPhCBvrnM +gH+0P84QT1Qsf3sXj+2B/hAPVJCkXgPdL70JDIoIokqb4AQefAuchiAJlhOenHS9zrwhsklQ3SfD +gF0PJA+nodAnsdUfIDR/i8EkhsyAHtO9C7H5cs4+fH5Nd/qPLeguhgdsdazqXmTv/XEozLEJCgl6 ++yn81gK6v6QIc+W/39u2/fnl1fV7RPr7iHkyCUqMzEPu6h5XVPNTBF4nWSBhGrSaEtXVVkSWA+Jo +QoCglhKoQ36Q2ZHlkoT8WdUyETRIFAEMEEhSlABLDQcq4uI+9H8fxfI6dJXlDoke5e3AR3NxURcz +hVEO815bNEMHkQ9Q0r0V6Hw954ibMN3du+cObs/wG2ZYxr21zTiOkgkHGqPvYtQ89Et6dZcmMVdt +YA7JI/v5W8PrTfvAylWZlK33OViSXNFRrXt/dMJK7t+ROyjy7eh/HTob7wSaYmQDmIAgqoECaECB +UwoyBE0sIYnQ8W2l+f2OHwjjzOnsuydoPfJEhQHh5IvEPuk5DyYqAoiFO/PlvroYnbbAPedDiZKo +IiB2M7ZIcxBPFlBdFyQmFwHP0Pw2C87MwfODkYZAo3HlDww4BKPXti8HCm9fs1393y9/631/puIf +eS9gv7ZHWKVUWV56HPIDDg7xOLIJ2wDrWvXZlARAWoc4sjIfR5jJ7rsYT6xkFCTD/fHn70/0e70m +Qo6zUBi4fYy3nskk5c7WLzSRo8PDnxQaAaIM5Y4wBoAQoUf4nN1PW1sQxm8gRVX9f39SOA9aTJ+b +6bsDoVPd4ZdsubnQTnRuXyLmq1MTdOHqwJiXQu7ypU4sLCtQ2FBqaw+hmnUlVihPetWIerfVO75e +KIuKwa2GZkzEAC6lQn0Fb2qrV1syZkvupysmcynkxGRrV/fQ+zf9dQ3tr/EeuJA4SFcw16OzOpnn +RSBTfutWFd1+J8IwL7RBoGMlVhxEw+JF7qpFPMPH8vh7f8+tt31O9lt08hVHO+1EVmPKi3UQnmHH +8zMT5YUulX5rva6N67OqGmcVSfvF0dPMBI3NpORKKh3iHeHUfHtmZZOUt4gS1VyeYz5YuHx3hcgx +Al5eYgy8Z+dcAAH0Ib+yf1ePuffI8eEeyRJljPhvNLwqT3cxBMyblRFV9P8fX+X7hXPHNLx31Yfw +gqewlXh7VuTTxby5h5dPU/4/tuhWv2gfEBvmKADIMAwnndv2fHiuSSQqOj40+ihGOwVvFPJqio+d +T+IXyfRvWOnt/lx0p80aCgICvR6V+ODkx19MCmasr8CnAcj9bSHQvkBxBQUmRxgQKchAAAeljcKn +Pp6+KMCaQnOzaVVTpZCEPERh6MVP4j/bMFhudcmRD7rZGzVDp6crWnD2QiUVkJ05iA+ROHTvQl02 +qjCLwUCHK98jmdFG0T5oRksXOTPi/kah+qQOfAD0KB0Hl2VqNOj87Ixpj1S+Z0doTlfNy4Ol0Ai2 +ecO96JQ8Zm3kBxRI/HEdwPiX/SO/Ng3h+mbjz/aB+ZfUP3fedxEDDZi7fL79ImbMQIGXzzCQgUgk +iMRSMiBApJBKBAjoOTcjnMrigwCrFev+brLP5pfv+ZMVU/ve6vz1qgPOC4wHjMFPm7b0XOcvSVE9 +x8h3T+cXs+6Aj0FSFDBf6sMYXSnSbF2+Ii77Aa6SuXjxMH3OXPjFo++jx5nzcp999j3xLjsTy8Io +KGW4vrtqESeCQ4ioOo2HRShocTm266rrTGtUOlopaQv5Tb+qNRFk+EMvHLwgpz4QCVNdMD48UT5x +JY7chDHLXBZeIBKUWKRUYI+elUzWBDJ7OVD0jxBBEOoKf6sjQh0HuBIDwSYxkGDGCh26yUjrdjFY +7nhqqR5TjjnhBZDqzh4q8JhFCFLbBDCIe3oBvhwKMSGwUOaPIlxN5Dh5iXUTYlKKizcD2IFpBE4D +5/HDZwBwwQRdzyV3TRTpPLsgctBRspeRTjrzwUNiEjRIHLtywgxDMLEUA3DdxdBcsK0gYxGAtBwD +wwwTYKXdhFkNIZhZvb8gGnWnNNm7baLugty1FmfITPOV3vnCAyIxRURHIBEQA2HE81pEUsCWbMOX +3h4aYRjv2Y/auMn2UKEYhQUvYWzYUHbg0qiowS9Jbo5IjToaT1oUk+N78PFWP79sNO8Mp7NQ0QMU +YLvlOd05e/zU+6VE/PycVPy8cB+wPmGPy12/p5H7QvdhHJ/AKir+KMroQ2MAoIPGYChdpBJieBow +rKS3bHcKez3bNhJ9ZCAyJLDB7MLFg26BYFA2CpppiEQktRsSKUawqGtpt6d4ejgbeMtUMbzLvNF5 +RwzsMsntqvgg9njxf6P1/t7QPD24+z7fdfV9ByJzHTAicqXRI6hZIiH0p8ohC8HZDwnqZ656p5i3 +AJnpjCqiNdqt6ohiEgjqmm5aqHsAOsP2YHUgqCiURk5TtLsJ8x1A2frr7YoPKP9v79kX+EDKdsOJ +FD6PDITAvu2JIH3snnPiFNwnIwpzTtnM+j47fenVdbRt6IZZ0RZl8NVc7sxGnPsZEDigEpAAUgBS +CUosxiSkiRqgcF9ze0/5aM2h8uYfp5vmNtHt/ZZPoP8fLM376FIjEHpeuQiHKyKQS0kMzKwEkERB +MUyVSgSUxIn7nTv9dqvElD58KUcGmCeRfphgpikkkXBQkI7XjFDRwWJ+F5A31N7wfQJeUlQUgCop +kROeDnukKMIgAkaowHP+hRbx4WROEX8EOF+/Hqt/TOYWVC21EctIGJQiIguegQQCop7SpRSAwH5o +devs9rpwnQn9vD2tDrQ/YrzTFHGAcPeQqhcXgAzUWww1your5Gg11hzgMNJinJ1DkeS8noaehimo +F6cx9PcU6xxg+ITNxYg2PVDiRH4IcBOQXlHBBTh2wtgEve2HkbjQ4z58njht4J02jGL2VUG7HT4B +wiIGfuVEVAci+L1w9d92/uIT93pPd7+PRweztzZ6bYCJ3fJcRYpBlatF4unTJzamHHjnrSaBmwMO +TJJfAIER89Lgym9vnykhnCpjPBweRAFCUmApRPruhGXiMIFIQEohSvnEyeVkQugk4w9aWzRyXo9z +i731e2YUCFuEk5ZlmI7Ghn/daIIIZCiIBpKuBr5pX8ZiWgKDjjIdawfy+t+g+iOz2u3Rdn0PXEyD +rIndLpBFS9OtQB6+hsXcHI9v4dupoDxpJg75okkmko6mFl9E0lH5LOCX9eYf0L4+9fPdIEQkDRiX +2rgjYAQMpU939fK1nOH/USL9NuLywyeIW/1xX3Qej2cKJcUGV4QISBQC5eZ7JUoczEJ0W2tqinES +jroyLtq7O3NZJo4EE4ltLpHjH+9fB3lfnu68A0FIsQA2ICB1uA9wDkOOZe0cBtkZ+YVveNdKfn50 +/ey8F7tN5YX741PF/vKoCDZvAAyi/femBhjmZ4xK+QObcquuHv5+P1RPLXGCOHVgGGKvx9MsuHCN +0QvyPFdWDBECAqRYVcNqBz3ywDAwIVQvOgAE1CAJ/BRidNAePEvD189L/12YNOiXt0fMTvYRkH0i +h3dT1kAZE+KdceEQCDrKBAO0NyIDIhBQhPy0zAyBeuros83X+vrJeQd8J86STkAPKxsM/CUyU/nB +80PHTgEhfQEjsjIHOUzbdbkfLlMOFXw04CTvHjrPLwBgAogFn6z3aa30aek319R3ZMWlWg+NYxGJ +HvETEozQczBhE8ernvjezi6NYw+Mtqm/NVPP9UZeirnTCBEqhXFGLBSpbirEyaUGypOniqPzjQg7 +2yfS6NWqTGOYL7zfN79MFPhfh93f5s+QaLcZl82jA5V/j8MU7RftwFH9soKdjp+++r9fPLn9GBUO +8D+UqIfUPwwF993g+mT7BP4MdjE9s7EPA9a4KHrkhah8V24xAZEUELHQP3EJYOgMCnMof5wNblCK +ALA/YhzXLAHVgblNKcnZxeqayBXw8BKaTT4OBiI9+o9rb0OqP0HQB2Qnn/S/fGfDj6f4/gej6zsh +/b4+poMewn+DmF/Z9T01DiOnh0tZuxiA1b0IL2Hxay6T1rHdXIW7zAgarJ9B/rGcyrhkJmdVoRR2 +7zDTRB4ZBmH0nOjQjLUadhMkF1MuZyjWVo285gJoQrbNXIBk6bLJo21ZWCiKRgVOPrSx5uHuksvN +RUo0dBZpxrR06Q6Akks+9ZrV6i9xu6UzqInLjpvJH6vVAn74P8fj6/gHme7FJkJq6E1oOvbGd3hn +g62y+ZrJ5O3CuHLjHiPYzLzoQId3EcluEFIAJApfcluHXYGr2Y11BaX7rvSMAR3qIWGlD5LGtGYu +J2221ddQr2rRiI2EO62ij46M6WXas2d0QXmA4chEP/irOeO99dDsUsyA7WahQ71UwQXd4mJMRMw8 +zLjs8Q7cIEAgEoA7TBZiLMVh/TvptttN4U0OhKaLmGXxKgDkMGAJaP2N574uu/PJELyyGpMOMfKX +rc1pI/MfqfjholMjK1NS0n55j5Wbhp0hJqQPH3sWaCYfYnqPboDWxLB+uWtQa1YPKGEB/T3BQYUJ +FD39/SueN9+i9NZMQqEeqAt3nc79/ZyBOIC0maDrSDurJWOHINTEBGYUAD8fhA5vfZO2QdMVt9Hu +eXWsHUgamHZEBzosUm32ofZmZJ0ykeNca4h9D4nsjzuFBBiJkqYsxGuycDx6+I4IQuJuAKAPkFKO +rIHk0W1Cc27uBPinCO3nQr3DfKwmYIhTtAXtfEM3v2dH3rinxlOKjYnHfND6rRxvi1scIE3Skw4M +qXsqL/LZrpxrTzN5B5aNbPT6Tjr1yfak4qgh7l89p061pwPYRWOwPukQMgmXDUpdvJHmdfgzR+3U ++epGmLT1T8IjzyPHzQHka9O7M6hO61YTaIKfYiU3b9chiQJuYMMnTTSCpNRKQiYp/HM4lvp8Giz9 +OHahdVscA1iHR0Q6fUN0XLTCAVphRbUy4mXz8fxkf3HxoM9OkpztTkGZM9k8NyYsTrm56RxwyAnK +CLxRmUtJV0Adk89BG2dzjPPOFChUBQqOWHo/PWcSmEo59xdEaHyrQn4UkP4EhpkSfq46eNok3YsR +kxtbaHoOhIiujUVJVL1VLcsAIAjwmezp7IBghaGGCYpiyyNwojg5EKTXTjNblfYwO/cM4TrmJNUT +QXODk0sGUYhUiqKURaTtmAvzgwpNUFqSuA00Fkw5MQbNOIoAoKfjnt8epuBufXSgM9eGJihGKxZA +98oYTkOmdBlOmQqOeosutEucWUSYCVHDAaDAv62AfR/n5moXwPZB1doAO2BmgGSKCRZASRAiJRok +fwxPdoA2r2WlAswCJDIyQIkiSYNkxVRlkT4Qvm8AoIhqqAHYb2fOmHA+XkBzAyp8WT6+RwA7KdCV +LQy5qrByTXuwDY/mbUXP02+OnGvPFcVeNYKNCMQUItUqfiSZcdPloOkUxUoUrEEoUWbitXxm5y7J +vOfn1CiXNzRMvU02ePFK75kkjKhAA9CGewS7WSqPOwCw7rQT358/TQHPdOpd9H3yGp8e2deMfpU4 +8NGlA9QR5/JGxDTB9RQ3B0/drx+v6F0ASgeaWDi4xgLiknMDJLAPjoAOodj4/HNSJ+dAF2MLwQYE +e3bWMVEmzGD7ZCrlxHJPC1JXy+05BiWCQTsdqiOAMc36vzr+nyDIIsPnkYFD/l+LJ6U93x9eaZ4p +WsPx538fGXtGZ4C4tTJg706iZiMfhxRB2FFAAcA8CGgsAUD734+r6z6uj5+kvnuT9UzPHfkb8QQ2 +h5mOKENxH18d/xTxPWT95GF2fwwNeSgYlBMRxhqQ++cYUyAyQ9L35BNZry93dH+8+hp6hhiZEwy1 +P66uhRqoehP6pDTCdy9C93sztVkO+DDshIOJ28NOwlaBdSAebMApb3fw+LhuoHeN9aKp4PI7WB+v +Sd4QARAaurAE9cN5OHa/v2qER3jWJwcdKgm1EJKFBq+2a5fOg9KBhtAPe8TJKn2/OUPlPpfsh/gd +H705G0llZL9c/a5moWb0p8x6O+PV8Dw+Es13xTj5ewB6fmPAAdTgT95e/Y/YaGFZ89IAAwAZkBZB +SNfMkcA4eagC0oAMmrXJlG3h6dPHsitvxXhf0KMCIlgIkHgCqaYkycMvlzbdtnOBNx1yOMt7zTIj +su/MrpxegJi64KBhk+2YIQEKZvNFREAqQeb+7n2/GncNWNcg2zinBeXI/bS3Zr8oMq5AWzbcoO7F +Yg6RJYBbf3xjfTPr+bpNYQZQQBBBzdRT9EAM75UXrhdqvW5pE6CLsejVy2UhCiGa+BguxYfrowU9 +QjF/RBLzLf6BTKmBDz1DfYKjoZUx48t9qvSwIgEwQUIgH8HO2IeA9Yuyyfpn/NHr38+qbLJayACi +0ASggght3OPtki+iPoXSW6RMAmIpIQsgr6emrnn6hH5Gsd4PdMpLn9PNAvMee+nng6+NUiYSiRhp +G4dy1xW/jYpZUlBIF27C5SUS9+suK9IMPa1X73wAhdeEefDlJTUhtxen48dKCDnCyPy787p8Xv4U +AfZ6M+aW9LS3mqgYZE4wUXiF4RXsfK/b5Yh7VWj7k6auxkj5yOzJm7f60tfdX3ubKdEyonr6IqOx +CcfwEOdCZOabfifjE6HBR2MM1R9YJaPS8/tqU7dUw0W3oi4dPjF1ef40V8Xq3VG6fVpy4t0488sY +mCTPYYVDnI4GneFCRYp+6bFK94i5ph1aezfXsCZOvFj/Jvy/M/DRcXO2jpPYAv0NGVSrBXIxcrlc +quj9Npk07R2fGoPHPj8bpPR040dff1yYOn67iO81v9reE2mWZp2zxDofzSKGs0xzGaOdpXxjS4Ty +OnicvDOOezc9/505HHlpRYsQ2jt4XSWGdVikkqbwhrH8RhYcye+DTu/1fxkzATAZkEaDaBpQYQIA +aIAWxARBKJMFfT1hR+gsTAOI/cV6O++XYYPgdcUqh+W8kY8SiTGgCc+nno3qV62/HBQSQWVzIxZX +vHmoZ4h+MzH6ab4AjvjWKCAIzSgO8FIuQITQNJpgh1KLErkp7Zt+/82R8e/PXV73r77ej8vrVj80 +n79svQ+uwj3X6J+eL74xuyOLgyEfuXFjXwvxO2zJJMkx8KNeSajRr1YerY50T0zxIoZ73owRUrIy +os7VxNWYl3DzoKDcrJOq2abah2TjSwXtH7daEi6pnzZ0hubFxbKMyno8A0JFTQysyHCi6lTj5YEa +m05FyoIBiqa1aIEXFayYrFWZo0NZk3DyocPZeIN1V5RekrOUYsmjEnATmnFy7w5OXUyqm5nLn94C +sZYpZZXvlYSiURvafKglHYOsMPTlu+g87R7i9nuFsPigz+nwT+8yYj4UviD8fhOXmR/f6c0h4gi+ +ITt3AxRMUWESgOO7tbpg6TWUPkD3+xKkfGu99ECEIFVFz4wRxuszePLQkEkxdAS6aIdx2VSYlqp3 +aHK8dtzW9eB7bHnnnvzEDgrdNfHqqiV4A8miziXyACUWBLO6ktMR3AFEQfLeUP2ee9+V554XI6rt +DIjzVAGnL5Q6FQmzceYZ3l2bKdWgzQcAr1SBAlaDOIL1qi5fPLTuA9/c74juAe6oUiq/XcGSq8Lr +8l6+WBSBRxSQTKiqqQMjlSVXsASjD1Fywj6D/j/X8fHnfR6ePMbU993MR5TeAT5LYH07VpIg4m07 +u32vOogi5TMTD5xoHpt55hjfOzt6CiFl1XbmuAltqnAKKbLQ6KhNJbt00FotCjEzMd8IOR3xdLcH +QvrgUxIrg1mVAEQUlVcyBxL3ve4G/nGYntqS9tTNWJKK52orsraNY2IpPXwu2F5aJL+7zoePouTn +3trD+rX3f57rW/DnwPPbRJr0y1netZL4IOC3R1p3RTeB8B/cmth/P0b+/5f8ht4dtvZfJ8AM/9T4 +rw7anbCBB4rzO2kIkZAwlAAMA5jWR1OlPd2bCaM3Key/6X2F+oPx9WCwSWZJZCA/aqCZzyxZPUlh +aUd2gwnDCeH+/vzT6tqHpfP+Fn+u79n48Iootkvh0W/L822mfFOIlWcGWnzvV7VdEBCJQ6ZgyRkJ +0qvaXSuZpFyG6vbOJ8QZBLH5wRQRI4bJmZC/JI56YZ+OJSMaufBHLlLAmYakGxdFoRETLlw+AcO0 +FIuYKgFyEVAX8LxDPVxkRUkRAkBC07Hs6VGK6SOUaJIgIqHjB9Hh6uvv4D+cocrKP3QIfdKPs/TX +b+0zprQoizqALO6u54zXwnXFOOj1mAo/P3XfFwvCFwlHaa0K/f+wifvB8HIDAxhvhiGKCzRfENQi +hoWH87UUn70rNklEF9nTn2p1XN5pjg6oOuCGbQXjbHYXcyZlzLmnE2OhdZeJOo60GrKqULwcK5yt +u5AMNqONgwFRbbBm6VK05PIzTFiddBw9RLmA00agqocTUR8XzOOe8gKVTw/rx/jcYPcwp1UIETpT +ERxEYnNhWKMxUOiUtDNXxqH92sg8wM1ctIk8I88pwq9woeSeFFMPQLU8dV+oloeI4ipYjHxx0+a1 +QwlGhKFoXRGiQ01xOOiMLp9x5hry6ny9XF7P19w7eg8l69WftLyqZCHBIgpAmPmL8TAkUASY0rbS +qFUqnnuDjyNPhvCXl1e4z6fD55Jo4pUUh+fefzJ0pAXinxQqeBJAbgs6wYA4+LRF1epepr7DVBYo +lYCUhH2qKZ1HZE2yfCuwCnNnhoLonhYhtcttHDJPoE+DJibJWBRfo+F31+mXAWcWYqY/48/V9l8O +AXmG8cAyaVoiqooqSlfr/XT33lVAyYZ7w9wbEscivJIQE9MFDgQJDNNMt/VfwI4oJVXtqWmCkSnw +4D5e4657M9F6+WgeUKSVleyREmdnblHu552G5IyK0YxHM2csC4x9rmBy9kAwcloIiKOLU7oI5iGE +zhmZ2Euv1VZEvTILQQhIQEED7UKEI37rKEeiB/oc1wHwtlb4igDaRgWDnmptxRAoeBqbS4+37eU7 +8n8D278A8SGqfX5Xt+Pb2CH7oXtC6TDIQZZ4g5OeELkWp+x65TTmsoRnAQKHVRtGzBTorRQSK8jF +zV3t+2jNJVTVr11BL1arfDEooTNZ1YF6OuqSQrKGlDV6WfsiwYdw3kIg66wgCgIF6Oce94P0GqTV +XNj/fj4kmPhoZy/q35pHvYqBBA+iH5GS8GTAUxi9FHwe7MRzkdPTEPqnraJFDUqDQnjL+EIjo6v2 +4dKX8vn+Ryncp4KnUnSBui9sHBsJgBuOUA5Xg52Qg6UA88I2ER20Q/7b/H8unUce68SH7j+nQvK1 +YYaOZ3AH8bBM8pYj+NwknQinuetRuxBhq0+Pu5DfHAc7UeyQ70RvjiNTp2Y7Adf67/VxLxvxo0BR +lMbjBQelLqQnLjnHGj+ianrkjslraLZfyLi0Yp4ZvX9dUi5ppJ0w+82r3pg5E15x9/m0ee1IImID +SMOcT8YuygIKbBqj2DdOBk4RqSPIFVRR7+j0AxwMsM2nX+oq/eSbw7e7Hf6hhzYsOdhjp85p/n8x +jHNsQDvjpZAUd892IYRyozpgwoBat9ESWzj783fsuS2UZL9E3y1rOyCxz1lRHkAYNWTeQNgPBVCZ +CGVsIcHn8KZHKkGv9kQztUMIGMQkHeFp6IKBGVBuAHIpODYpmXLgteGc5W0mi83ny1OIY8ohgwS5 +/QYgKBadgTpO+FzNLTbUhzt151+A9p4dgQAIoEsv8/kUCH/rHiIvceDN3ZRjsAqM+CjCGF5lJsGJ +ESK0OJBjUMzCAVy4lVwVXUsGOd7Bn5YWnxb4H5fdAQQX5P/bVWrc2f05VuBCsy+odiNHQkRMXkzF +75OhYVVojUDTqFxuRvoAjyV+CRHgpvU+j8G2514jx5HlD0a3j0smX1dxVUmFItLk1AT3dZkfsIDx +oSGoDeQRsLbuoczOk+XJjHWVg1mhiMvOhrEKl1mBzZjUTGppXoF9PL1JJCDo5JYPYlxkaWSG2XFd +jbTeseLVvRK94UZGeWJTY968+PTz6eA4EewXx+OnSFv8DsXs6URNXk0NWNah0FMpOn+79jft+rVD +vE/XW+V3cuLsXLx2CCQ5IWN7lUNlTdy7hoGXBMK6jXVVXAsWqrKooaOOtzxBWKyAQ7p4Kg704zEh +monKJ1WhUsKh53Si7T6SiJT3EnA7zZdB7m7dWNGBOIPFu6JdXZh0Jb+ZmGGpyxlvSJM7uGFIxi29 +LV5IKLmkrKEz+32Gb5L73L7k74pqQH4cx1p9TJFovbmHdFR1+mWESr44fNQ4fUW7WuVE1j2ZhRcO +528vEKHIJH03EdOb9ItmfqdkxykFoDTnKN2ssIGYBRdXbUx6MPq3FKqjC0uUYSQcEV7jI+Dgyq4M +SRGQKAB/rXLeinmxDgMIzoH2VFA0VQ/FswO388f04X+f9/nPd8w9Pxj9MUZSoBEuZzg/w+x4VpV/ +HSwDcEF7NXZzwdC0P2/rKnjk+ecZyZwEdRsoTMEAQar7tce4He098TJQyffajhjGIb1oxVoA18QY +IegTXACglCAQRfn6pejr6wgnA1PUWBUUASogocJ2CBAJ+34EYCXWYMg9K89eEYqxZLhkzDv2eG8X +nc4CMoUJwRIgIssbDoDBRaFES8bAm+6J6CVsPbvWLVc0YFF7NwmkySRILNORP3KMMABZNCtBnAtd +Hp0SZEwqAmkO3sgSTYEQDaBcn8aUI0FOf0if4FE0iPcAJE8fS+YiDiMkc+J4PujDyNfT1OiHZflQ +OwVJPr8tyQs3hJ5f6D5/vfT8+8h1OvmpWoYiegIQQQg/jRwHMMiJDTz9VQZPV1vL1Xf7X4w6oNtd +CT1hK55U9uqsDo50aWAf04NPSB0cAQqggE8OHQCCS2J92f8hl/fBzsnoUUEBuOJ4dEJSJUymZKpl +qT/VqGoJ8PpIfV9P8f4fl+fPmXUqJnZEnxTUab9cAl5kUXKxLiwIChQqLAi5AbkO8sqFEsW2Um+o +HGQPx+Hv5fJraOs1z8/j+OL158ejz+N9H30pei1yjpSFbGMTI9i6XQUhruIPuMcWlquQRW8teSHz +6d9LxIO9XTt1RrFo2pVB9H8g9+JwveRHvU/L+bUDbSRIPWZOeVKMXk0KBtm6MZXIa3y4m6XVhXGk +41qRMBKss0lEAznOfRbb2wq5EmEavHA20dEduBA44r59lIifjYOk1gd2g8afh+Po4N+UlNu4yB8R +k0MFFDz3D+fRf39DF8oYlU9CD0gLo1ptGrC1QKeNJqGQlhViC6A8kANa0ZDbNRbtpzoDToHARlT9 +Po+/XjIfD+eI9oVPGFQD1/LFwkdkKeXmeSj57Q4lXSp5YvO9O8EN2Ac88aMj0UhwYcMIhBHqA1Us +MhNJMRE0RSiemAMiF6GxT+76p2fPmeLNGxu0sbrSjNtwNexhd0E4mTfkrFJxS3lmAZa5UdzeVOEM +jxYsbAmjbfITcDdJuZmZFEESVQ49pmRhrlyebMMg4MV6ALjo5OY0a4i5j3D7392011stOCBkOImh +K4GdACYIZBJJLp4fs2CAcMrLAJoIQDOeOeC05Hwd73nnxnFi4QxeDno955efB88hwHyPT+G+icB+ +cZJrr20Vt9fAJ6NuLxgCmwIHiae1GitlLGUJmrAvLfRlcZrUiEmqZajrMzUJPN4n2c7tvyvLIUlB +FxJdtOnS/QUD+GFM84FP8zzuD3fF9EPnsIVHS95OyRsvFxPiHsG71VMWNimTIUAkxAuZiK/n/Xxg +miyITIEYEol/QGNW/auM/ttuD8Ox+eWSpaaAwA1nLmB75z33ET/f77Iek8/1e6lsvzc++TJXoHfm +I77+qZAwmWCkL0CXbKIr0w3JxtUTJ4ZeaAJhog1Bynq7ba63QkxLEfUcnLHctrvWcCd4F8ITrLS0 +myVclWgt4pkmHfMzub4gMTON1+nl8j8ugHg3i5heWE5PmqtJS69WXc7bw1N0EGpKicRmOF3lxfv6 +HlxNmG3z+qe/Y0zpCrelsaZhmNUE4wK61MZjDMaQxRhi4OK7s+v7e6R5WQ86H6yK7GEiNKYsFxKl +CUP6P7MbnxfEldd3K8SNcFJLEkUkERCfJH3wvCmBPg+93eD7xD2FfTjjWQ8MYyD3HhRL7n0+jd0x +Hsdc9R6niedwAnngiJ8Cd6r41O944A4NoJwNJiTvZEHEphr+J/x/f0e/6/8vo5RERXogTURxify8 +h85Yc2fb+eXLn2dDs+/1axAEiGCRgMd7g12ZP4DPGZ22p4f/WX2a/JpaSD7dLd8e0/bvMYZn9DgH +w6187o68u2aLN5CJw+rL2P+ZU/mP53UNG6uZX9Q6e6imR7Z+r7C/xF5el+ymJZOYNA80WspDRHAe +4dXI6SNM0f6Z5yDIFiHtWmi/tliEWIcqL1qw+vdpXPjmzCW/+uEceFVfgPy0bWWU38mrms7L9epo +9+WfmeBjjCRH7dvRqVPC6bgezjY2QrrLcap9DTRXXLD6HRbs5qjmf8Z5Gtvmy4ut0hiP5oCyHJoL ++juA4kLTucIcGTiQSGeMD3UwjWnsGFbu0wg7eJA6iKMEDwUYIoi+t11IKRtEk9KHUpEDrVPcs79d +ceQX0b0ImMTRq4pjKtErAUM8TRmXQRdJiy5Cip5W4dku6UF7rTKXpypjF6NY5YpyeWofRgF1nKaZ +MEO6TEeBcQEZcSxIJJfEHbvU33TZngrUeMRBCgp7l2pqlyMICc0eos7bXvdaeQk5oqx5YG01p1qG +COS1KMYJowzfUhtmyOoTVc2KPGtHOm21ER1czucN7wU31AhtyskTiBClSG/UpF5651PDF6zuQt4h +3l5hi7Q8ytd46s6pIG6bKttSIm7MPz91zuO2/y9NfZ7/L6KO02XXPxWtRXWbmWpyWMltO7fz2VOl +p1atfPzT6r9smfBVwju8LYUySfq6mLkceSgdJimrGvt9ebcsdsRyXOvV34VtqbjwjXbpmoxu7JNM +hnzF1nKWbGDsaWx3Ti+eUW+Obpu4cyx2a6lkuty8hCvNjz0ZuHRdmNR6p71t7nDOzIgUpyCEIAsH +45niJ5UPETplB2B++dQBIpJhIIqJlg4HgbqG0wLSQ6/2TtJEY3rMtGlTCWMnGBhbpn/QRqUSQ1W2 +qYv83UzbAt7k5uoAGtsLp0Hl6PdkT5c367bFiis94buOAHnyP1a0Uas1WKOh9eU+eKnP36sDorcS +irDIDKmTLmf7uEZ0BcJ+TeNkyxXrWMZ39sVcmW9JeWiyjdui/iZcS6eNO+Xli5rdf75qymPpay44 +uHf5OhgvcsJrp64030VoShJKPjuj0NG88yW7w9+iGjaDWszbi3bJf8sNltM2nj9Tv7eejdgGGKF+ +Jq+WPBo1j9ZaovmLIMPMqwGrqxulAz3acyJpqf1R+wky90jPqUadUVzkAxWgjESqFR6aV6dbUbKZ +P4duvbMrso7bRgfuz0R8u2ieE/x1jzn5d4E4UV3UYY+2K7zzcN/VCj0w80u0eOHwM0vVFFg/GHV+ +otztuWuhbKM7slGq2ZKN3lHkx7oRSwvBA6yCz70TFLdMMUSBIq+uYIGITbGJZL8Qk5JorfdFDWqQ +iATtxFUAiBCDvw6UAljEdODX6xCWZEmYoQNpZ6+cvGvAclUtc2DVHHjgAgEJ1QM7vI+5nh+ahtK9 +t8OkHOM2BQB+OAR+By1fv6wvAEQp4Bf/5fl0zcuv+dIkHQYV87CTaq4PpvA883ofvp74D8q10in0 +30cyiAklH8BaWRAoQlMH2w0jCHsBKQgib/Q1fKGe5LnVvDeUSOXRidvjvr3OT1r7pMT98q+Qk8ez +S7RPTLIt7nvOGE9xERjjYSK4kUxM/bWwMOp++9Njmi3e68j0XCF1b82CcahyxN0Qi8NeLY4iIQhf +XgnsQgQTwCIB/6Sif5F27fispXBXuphqfom8jpM1NA7P7VCA9D9x5m/BMfk/+/4WUgAUxTdoyskk +AgS2JYDols0/qH9/T+zS0VUL6Kr1eRtc4lsPdc4PFMfSXu8RVKFLnBEEZDSjW1JYWaMIMr3B8Br3 +JncpA/HwKCIqodWzedQIqF/fJRSMivk7P3tp14K/aa4pII4hP2jBRAi+Cb8fvJ3xKOeJeif8uSUP +F0w72Ffn0sTIRUY6hHfZgKP70yup1+4HZTFAATYdmeYV3YMLqkgfPYovESYxL3MHV4O2jRk/f96t +vue5ublPh53EX/qsOOcB1o8Mmp6RcXsM+KWlWxgM0UJuKtJK0rOQfHEPf5BOoacVkV/sDX4zR/Vz +RW8pJGBlwKwlywHQQAL21Uhy6oT/dsN8a/Q7HDnnAvMO+q5nOekaShAO6TkCKOdEQgEIgGIYokCb +1eiPWzg3FHANsedgW3hRHL5DPLywxjzhFHoOft/V//D3c6oBnNwCaERfGXGm9yAWC8MtIShNRGTH +Vl0j+/eL+Xdfz12TOgGVP5bFyxLrIjzz72dhuACihecowByqOojoLgBfN+Nb2xY4l8SKxeBRSnqi +AX4AD+/BBsHhB4OMaFRBNMBSgRTuKO3XOkwmNO5V3ZmvB+uiJleJxvV8Nxly9LmIzU4pZRZRlqy6 +uv/E3dRMM3NlnoHr43u2rH5oiS5QoRKERO3twDwIQO26M/8ukQJ+DN8DXL74Nuz9jJq0+DK+AUXj +jDlDzTcKg2bxg0U7AsCXBmKtxD51vgXwYO+GjcnjpwfE822b0m7MnGz3hw1/e3HmcLGXtxaGA/wJ +OX2Ydf0IhgI+yE9Z+7POa9O/9SlTWsQlQDggf+El/ue528l5B3EFwRLr8iSBwHEhvt/hz8N3L3Ve +uv0mGndDFzTxJEUkecHsaWoiLOzXVPz+bP87fQ/y79uWaBgfJ1zKeOwdGz9c/z59Qm2ZRjPnx75r +0aaG7h8hHDknsgOniEklph9qq/YEPn+N3N30w04O7E+Y9V19r65W5yE8oBUA6/ZdWdfr9LEbUL7W +9toPPNnr8+OSnorvIa141UIzlbpcnXLg69o/iUAJ8kbD2kIB096/1+Aot/48XH/NVk6abk35Jv3p +6NEJd2b8kHChfmIcswhu6/hwmRneQlqUKQ9dmVUSIhw/imcNffAahfFJezgXjYOEkcIXA1SPiGO5 +Yxgi2R46Kd88X3VngjdA6cbgPB6cenDyTObOupdeGBQK8sEZ6vTx9+g3SR5fP6Z3Wo62QDb9144J +5a7MWmyZMcB0zfyxegCg71kiafaDfpSlCU1+Ph1uELhmkiigKvxMKU3/JJsPRfMQHTSwDxFLvrp9 +nfxy8O5JpxDrkKcjqfEHyXtHk8dBGspQJRd6ztOHnBPeCmzofpeHkfu9QMo02ZmTT9W64mC3KAID +yE3t0PQNQ18NxcIwvg39eXpXhsHNSJvHutWX+Gc4c9TY3ALK63Suc66xk1h4JuAzzJJywxdmADQJ +cj2GCioYoUpu22RzzBv5iCDqB0a15vaNB7e3GVub1n4DDPirAkOhQHsRRgAx4vbkoGwdkeiXVuZE +YhLPmhJBMCoJQgkPMycIn1WxIElCJ2VBnOgqBMIKc0z+kDkZGGEOBUFp2NKoMGZXAj/z/0op4IiJ +UhQZCgCSFJDZVicoHnbNeEOV9+Yd/8HDYFdL95qff5ttva+M7/0pPm1Y7WDbe2XCjPPlmJQUU0EP +VkkLOfPNGBDDXzyzL1QHl7CKI7hOmWIEAcb/B4beoVAX8Itqbv7Qi1Lf7Y2jv/woE7gFKmfhVGER +/eQPoi9GvhWrF3GlyDzpiCSXu8hsYEZG3FWgwmnSpp7rgtQBM6EcPf5kDoXph/ZZ8QRPcwEQVEEa +DpKAbLA4cIPw8sTsXZDqI0aOiFid4HZs6tTgQT4ngPcKdwKB7FB/j/x/TCdfyd3DgQKzpbFCLiFE +oKsRZq0F1041v9e24YnkM2EwZBb9llVkFkMVY+hqEFJMr+XpPn3OD47z378eb0Hv2+3njvkPBByK +t2dORGAZFHhP8qMbkGEoCPKfPz+IqDh45RUhZqNeREFoKUhM8dOcb6MtCjzDeEsnswzm/9a/X2db +D2d10Xu/agFXLbuHP5dOvmAjrsMfAZSjyCSVAI6HGR6JhipiMspT7flubk2P3+rp3jrp6HxIYMIC +DKFIJA6ulUREYGKvM4CR/GXjLZh4rQm2I/XauMOryFXH94xfYa++He5EovCwRoDJnDcRijGnFFW+ +QAyERBuPu8D7N800HJz93sw7yP2yvrvddFP5b0x+En9gawe+JL3ssXTg67HgL9f1K+siHI1u8mTq +360jclZHI/2sTF1pLlgigjT/BVOkTqJyJKAFSPn94+zqjljyeaXINHkr+Z/pNuvzssUIyY3ahxpz +GHcbChP5jP0FQCP4cWabVFiUF/m4To3jBoiwyZNPPKgnLxvvDgH/vmzF932vD4bGLRkMzOZ3ZADz +ISRYu1TyVkeBe7Lwx9C9+nv1oI5egtGimYlUeqBEuYAIxH2QHZYKwnrCjmZ2TSIj5+8fDdj/G19n +8N/8+vTniq6HMki0UqsSAQyTV/ehjBQnP8N9B+CbfG88JR7tfx+Aj1hz/UmE+KawTcEefGyg9ECY +m+S6WlWdtBogGGEdZteNBNmbX8+/vDdJ4vyZmfH37n36/GUfz0iKvT0M5B5z07XFdYStcykUcpXG +A5Y2493hCF9m1m/X5N/Djt6g9UhBR4+40RJEEsMcbQTfqPWZnA+do9BMbC5U1zb1ytygmnip4N2u +ViRWTbebocstlECqDBRThAYpeQFJBVt3NQ5IjGhs5qhFg1QFYDxWuu1A4PtQAdgZNHkTlyK4GwlK +1leMJWCxQUOvEO8Cb+jptb/f6fs7evkUnNju2IyKkbPQ6D4+vhXn0B4aBf5R6vVMGJyc2xzZ2eIg +ERgwUARVGREnlEbsBTbB1k3lSg3d8K9Y2d196HwUJP27cVAymUokUXeMdyOGdEg0favp25cOrKMx +GDITt0uT87Kz4CxDiGL4LxoEkQlebzmp7BqcPoZO4ZQrk0DLoz9WESD0DtIq38Ofq9BI7v75LKiM +EHY5Hp482rCUHdmk7wI95/o6M6VR8+4QBJnIGvjro72c9oBUnH8orAuQKqAEiYiW/G9g3jUPCJJI +7nx4LahSKxIjHmAewErAIXMSq9v1dZ5SXClM2319rr9keTZYdqJQBBPTyj6/GzuWMKysLdVc3rkp +kG0iYvtVd4ICaL2gP/LAJ883pBOHo9GRCFKLEC7da/b6bv0/H7ue56h+MCoFgKkP+8GSpI2G01Ik +0SdaqI1qi20K1fvs/07q7HuvM3B1FxTJAJ9ZHBKJEKU9UKOgnzKh0ofFQHlRSVS6SghPvtyO4A6Q +0D98O5A5kNwiGoGg74YKoUckLsKB5CIEYEIiJK2DBUpWeTFjRiIFhONlds8XGFPUZZusaa1bsrhJ +RcI6aoxYHtmw4CEdGJR1EBw2B3HJ6GrJWHBAgIIQMRvZy5OMSybRCpjoyegHbleuVI2GA/rhrDpx +XJNSOcX8IFuavOVirSA3/G2V9RBEtJlyG+tIJIiAeCQI4ZHP7/3y0PEvd89TkAkKDUoo1XCrTMMb +kQBBuEqBNeEImY6cycZIhUnWPFAnM4dsoA0m3Y21AHUATIIxOVcBevLJupVh07MEoUspAbpZThhp +qQCMILdwbHbJAQjGUp++mK4ZVEzRiWMB8on83VwzxXWv2kuW8IRKYnDjINGq3yuvXlzgk8fqWe6q +410PViDaWIwTALeApzT5SiADn8BOJ5/b/injq49eAVF6Cl29qBvXIKJmQb4uwYapOa3Q1EoRxSKM +OA1s4AIPsgJkwqgFZQDjKJGSUojRuYMSuePOJtwSMx0kaOFrOHAWigCkXtYc5w5sM7pptiTf3PRe +yYLLZE1A4FGsFEcQSRhmc5xKB1Evp3+EKd7r/9Md6O+LYzkYzGRiZDvHGkikNayidP881o/lpXiQ +3/U11P7wlT8d5yThh/z8974nzfXDriV5FSJrIwHLJmcwSUhQeXIJu4+0Y98x6eJpmrQTQKq6ZNJr +UBEXL/Ce6/8/Rz18x83w+f7f8tvzD3282ADh6wpqMS85r3bmv4cQREUZAScmcROAcwIDySpOqqmf +X8rVSoyQvDpWopPksSqxBbaB1uDvmNgIccCTZhT/P6FgDbOXG0H8eZrmhTt03DSaGpQTZkww9J6O +k8yQ/d7bPUD5dDrBEwAh4Eg6jNAdTtuZ47Bm2xYc2uO8MRiXEyd43lZTa7TMypxCQxnX7fH25Pq+ +H6nFjD++MRWOREqCAgaJFzg9Oz94uiKF68RfXACxFAxoQlQUB2xkKChSIkmghKhJvj4xdiGcwaB5 +J5I3owFN5VUxwlj8ZNVV7iMYqubVpzJR0VRaUo1tIxuJcKqyx9mUVc5YcfKcbzTJdBuGnKb9/os6 +9SCEOfx/X6vZ6O4O44GIwYXuhfkdMOwMnhQLN3Qw3FtrmFsaYYIhvz9HEnx+mzhkAnjO5IHPXV64 +G/69P9ffJ+X6U0O3bC1PG1uzuz0hYv6/Tw2JK6HbCw0AWnhyD3952h48QLCAbfI0nQ9u1FYqiI9N +I69U31T05/j8f6dv02Op2BWeYrA6nn1olzu+rznbny5nhHkimdDDyoXRPUR7vXGoHRrSGmqQPCGI +WINKovlCFQGaT6u3c8Sfy9nv7vWhOR/b0Z4AvY/0/kff9Hz+zFPvnOsICz7fad33WjxdnRBxgGfO +dk5fu/LKbF19XFRX2/Zh9R2vgIpDvYd9PDZ1aMczVc0rG2tsvu2C5GF2mDNBrh78QzfLBOY8vPhw +c76FCIRpR8jTDD8pbAeyaHuCVoj4+sHMdsi2pnBOBRhDR02Deahum+G2vRwbwRiFe233d/JE7sfr +YaLChSqhkpPmNDUEK0xUTDURS1SWoik4uQaAHShkhL5zGqoFQFEHQgAIlBh15L2pUFFJyHJCrG7h +sB6b5OhaQGCSYmNxkC0K0Zjxx0uRA2cmx2Sb2n6ocT64flGQOAZRwAPduAUfGDGQyATiz4w+DwEy +HyJnSgHKsWjA9wKRAICiAGEz+TAP7WnO3/G/EGQ45+Pko6B8whjY1RG08pqFnBAE9M5RPafj/x9f +k/sToE9oJ383rC99MJkrMqLVRGe0aMblyVThPIjzgoaKPsf6zmrcLgPyzO/XtvuWGHQLFzczfVh5 +rRGKak53cKz3wYUVGLBE7ildw2yAhyyHn6pqbSUvh5fN7/07zOfHv7tlU6iIWUIrBVWRYCLCjRiA +onWlommICJmZkSiiJKIlpAlgoUpCIaKWihpCikEjznChIppkiCl4IzCKdWQwJf27eEBqULEcIQvc +EmR7hc3r1raTXtPXew3mHfjcYNmGYR4y9IOg8+Rw74Kl44bbo5FDBCxVZ6PszDQilFBDjJMmVhFc +gdgjhPBGdAb5uceUE2H+Mr7ug+UEKtKdKUiRAUUUxSUwDbdOpkDuFSHx+P8XJ/L+tCEOv+7z8DsK +drQtQyaJ3GobGyWa96EzcLAlNG6awUKLPXGawhFWnPgIC3lTDbYQ0NABEyEjB0aSTX/p931fL/87 +881ozPrgP5z/30UGIuNFLPP1EpAREQIBnOfvbG76zyWOh3iMK3cKNeSd7ao8rhdsd7baZnaSI51l +IYEgEPjamLxmQVitVF/+I/EENtlWCJ7eKz1Cthr33ZxB4oMUzfDf3k6ngZgK8lDEkgtibUPpcIi+ +4CJ4bFLkIJuVTWVQn5kkkWUxs8iHp6P+Hx9+DbwlgiEnt4/0w087wNe/y/wB+z0v6d+edvT70OtB +CdTfLBw5xq3ZX/4vKM6W/vT+mbGgEqdDMg5iECNBQAEDFEWBIP6FRCEPK+zy/5+N7/g4ZKXS3Qus +YGtaz90QHzWsixQNkvxaR5b5iTblcdr/1hg7JpdqTd1l0vILOeUVDfM31VMlGRgkkj4OQkG/VuVA +BOgP6grvPvG91J0aBbw6uVnjbJsDx1nP0p6+TvJ6sLrRmnS6LoS3AdWBzgIa3zBYGvTYpjFhu8ZR +SbJVcEMyuFxzfwzGAq/ik4TEUrKu++cc6bCcvmOvUPyr/H7v96OvUeR2MM7zv7oCy4xdE1lupLcG +4bzvksFigqmXfJxbMYpwhxtTSsE3sKizffbNIvdDe6E0xRhihiHkOKEiIhjE5SaY+az0H8emXDmF +tApQg1Vg1Ief5o32iuE2vHkf5u29Xd58/ld2973lm6FYpWGZVXdN02dIoCqbppl2ukfEN9tE0uyG +buppDQmapszSuv4HlGg7Bcjry0d/Zd9DPFCFMMLhnpGf79Ofhg7tcd6jwxAY8fQrEdPdnv1KwuhT +NEbk9Ml2CsXXsN7hyQ2W9uMvrs5GYFMyDQ0oTPfpv62QPLMoGAKMPsKbbsMKAKhnGYDJzTLLblb8 +MggBF1weMDwVt7/cZW1kJiGKgKqFcjMzDIEUKGYkmxAutpUDZDGsH1aM0h5hTagaTZJ6oZY4hHAR +IImSoyQVxGD4oo/gECh96oYcsBSD56dXdEC/4/zpvz7GKv1H/n7azDTP0Sor0vyv3vpdrohmAgkg +zZ4hlcOpvTFoRCNOcF5cwQIGF00+5anrO9iFwqoQhiVPJmLsqxMNEsGQ5Z4h7u/tXu2ByUemzwro +7REFKVTq4dPB8Vd4LELtIHbpi2a4sWgaGYZ14PXKigvChSpS4UsjB9g6MUhDvvFN1vR8t1TgGcoe +pioscL56uvt7enfl9nz615dvRxb5dbGMlGinlcsbkxbK7bZxtsNPPZOWHCvJdtmKXOp2awlGnbMh +rJgzuELDOjpeBqR2waUbKazohXucCjWHie6XO2enttttqeJ6K2UGhMmiLSDRsG7GWqMMjbcqNRsv +QyZM9nKqTKYbFtCvaJi3V2oosYYC0TNBlmQ3FUuwz2CzImVOSgnKtG27OuVnlp52dq7oyCRwocZX +PdxMiOLGKuOMhkDpOtLdnJtcq4Mk2y8oXPEgiqqSycbo2NJPEuQLuXCaYjUZXzHl2OEE5YDiyKOc +wSKMoVGWnjFON8iqebRkeAAAiAdmZfEheHkWZVG/vmVgFIyYCSVxOf7l5goc1kVWJRKQEEUM7SxX +MmYdDHAJDBdIVp+6ocp/roWxllGRZbHAoiQVBcjiHhttvz4NySSEDg1dztlz6Z+7zdP5bQQRFtRf +fkzwDgxVgfJzeKldGGLW0C7GgulQOffygSU13Ksh1YqmQS1K6fRMmE/G3dg57utgJJF8zKy9rtPS ++aJIrguUjuv4r+PZS7syaMwkAomFbsnur6BMKUwzYU+XPsfIkl9avvPN/L6Y65pFqBAfZ4NerXh3 +7W0vi21S899q9uXRZ945smCUInCLNdiv3hyvPkOu7C5BV0rtl6/jsugEfdym8qzhidz6efJHMBpe +fnCI8mWKfQOnZzfj/OzTDi9PjZZ5X5ESXBsuN/fffJr9+/q7OvRXTn3UCPDh7IPRJAXEHPh6sFuQ +jj+RybefFM7TNHHmq3Cmie/Y8j4Kd+D16Zdu/loNmpcDCeoba7bz7e+PXJ5ddAxfOPf3Jbsl4beQ +fy5JCXpwBu9REBlxZML80t/osMkfGOivPz6p8ELLMu3laG6b03K3Br9fwYuvZyHxrskAGc4IW/VS +AYSR6FbMEmZx+vqhRPDbUZpJpQYTaLb8euvOOw8T2Sjs2eFfxz7R8emGtyYLcWOyay+qN4KFsHlf +yd9NUtAwOy4vFkDadGjfFNu6m6FK/dbe9/xnkjumdFm9ote2Tp6YhslxyDxz0813xRNKezw+deO3 +ZaabzP8wHetgQDB4qExBCLPL85nPohkybF1/sYxu5cpMVO5hEAFwtjkozxqO2irdepdBftoHXG/s +dGy+kSuBAQkIhmUb5EGY4or49PzmSieGTRjAf42cNMdW6ZFmz39Fs/jeToIU6zKKm56IxFjjn5qb +PSaqab5saRSITY7HQFgWIJKlVLQAecJzyZopMMbWrSoUklClyCeFzRNTIyIHAwTJSDK+3xG4Vro+ ++34oASmYzJfpc4XZAcKnEwVohJcigPEdb4KoZ35B/2OUQ58Sv41fP+TqIU//aLu42X+zmmEU0yhC +3PT+mA25LviOilFHmDvpeygi4ygYu2QPelAAsWXPKGTp8FiE0oaOSfvcPDu+i0DyPD+rl35y/szw +tRN/39T/Zin7h6DNGp/6/WCpTCLrWP/BC6I5bJAVtVi4gEE2f5/kgvnvlgKYPEKqY32Iq7FfdUx6 +3/TbwNH4LxNmSezJAHp6Akd/o/s6fEtJSa6rJVMRJIKhr7Wb+zo6LZL0UtsFrVoJGHSV2jFPJNJC +CdZKFwAe+XjCuiMwqaSd0M91jhRHXJJSZ654mf0FpvL+T7B/MeyTi+w8W2R15Zb69ONtT6Rzq65e +2gFHNxqDcaaMi6HPlPfzb29tH182NeaEDXtV+JN+ns4g+wCPouOCXIhIj1tbsua3pxFPk6r9Wf9G +9+g9X+Zwfjf1JPAfxSzXpoNGQpL4ZOf6ZweIOXryOYnOBAMgWKZiXB7v4dc8OI6SIJKv0/qzug5R +ICChYjRwZHUFQ/4ie96IXqpCk1nTfRzb4MhZzYGDv26TTke7VIIcp8P03zj1lKmVVUFS2+KUQfkS +r9XqzITcFRzuIovkDBOvEQWzs52lelDQ9+eXPnikOTJ7Di2R+no+9DgPS9gGq/u6cIbQdBowTyps +GBIRaAFwLx8HSceGvXLJLs/hp9a3o+uurC7X5SxyDXuHU6Z4Czchsk4Sy6MenjLvMGtFH28H16dW +avm6qkQAbxhRRNOVKEEgjOqgjfUv9nQRgnkWKLikbsMYI7RpC7zKOudqSOanawyELLK38mgjxOzp +Cp3gzEYuZfvCorOPkoTnp0PDhAuxz93wJYIHVKpAcqhBFsobrrwc7uauGwGU5Zrrzmo3PjH88Is/ +9VWyl5tqlgPH15aHZta5zcX2dCtjNGXlpdZW9g8ocH9mDh2rnYSMOjzr4Bw58BUeQJNhscoGedLz +CN7mIoVFIHDIoihEAReboFw3RZfTDZT5evP/WK/qmoydmLbHZiwczCwi2+vB3ppDpXLj8LPPxfAk +4CBb0SaYP2dy5HXdi3Ztn758kN9k+EuJVUTW3c2VlBIJELmBYKLFCl3OotHfpkdg/svOKBbpdcaL +4VNp/qZey1Fw+9fezkD6h9zsHWZ6mGvYoxEXGGfOwE2ZR9GajY0BO7G5Uh9/dTwMVbYyXJ4FtaiF +6KkO3+fLB25ersvTDkZhH0SZvR3fjlzrSNm3r4Yq7JRYj9eT7yQgNBG8cQuJ61Ks/cFREGM4S44j +PEoILiMJ7yE1qLzQ8IncX8evp7UH2L8I6JRRyamBYtqv+vvS6CADAQEiKWCXsBE5TnBfaFyRUjZh +feeL1pdGZCIF17QyS2LuNJMSqfC7I5xxmOUoUpc5+rgK30ud3h8HzQtPJGv7/TkY5n057+JHfpHv +tDnvEn5a+HPbrgfcHUiv1wHlvHD1kkUDtd9u3+JgUrXAygi9IWOWt7XnwcrsLC9S7O4CwSoY2RDa +FjNZufEyy32YElCbiJ3UvEhD5CFJIlftavhIKg5I5n1JkkkvRWUykiUkU0tU6m/ZhbK3KMhAAfeR +QCyqUSSlRmVmrogIY4RDEAojTEK7KYmiWVZbo7rpZrpZ5pKp4GICLBVDHggK3okhtICmQgMQssBj +MLXiiPJVemhIHBhlat6CMvoz3og8hDlrZjXErKRhmoDxHfxX+W6yzDNka0zqRVoXSrwGE+ZbYVsK +7FJmxSMJVn+kdJUVymGZ+Fy1Ss4y4VtDVFhKoJyRuqkxxESo+UzHDCY1Cm6W6aKSETM5xWxeZzgH +T4par8EtsUT02vmFk8Ipq8NFWJ7TR6xMwH9poUnlhtGOauMJoaSu29VG6F+QJPlJbU5mLmhPZmcu +BUvEZSSEirWkhFNxAscL88aPkQhuYRh2irHgsaFOczCBkNJYhQwIWqUY2GU3EC4HyKPZQlOTIyO3 +lcFIemko6Q20UZmvJFSR1kIikIkdCqCDPQL4aYOIvCF4jwgiyPDcZb7nB/FV1RrKqyPSTHAUSChD +wDq5/RXh7tNLZVqGQ/gtTklOE4Th0Wdk7ohy7ui6CyyJf4TsH65WqHI3pCgJBLe5EkSRws4+qrLd +9IV8FcQiH/UtJ8MAjpOf6x/uZh3bRAFRrTxHcu6d3YtBCKBhOCyJUAlFZxR2PjRn6tTkeICCJ2kB +EjSgzkE5DWwwFtmUv9PAyerWxo0gvmXJzyi5rGW3C30Mzuq01S1Nm+fUs1amO02FMStGf/B0duOO +8RIN7YMoqGK6zYnG/V5t8+9Z6iFzaEo7ECY+6aFfam85GAg8z6nsDdlegdZCdO7ln94ZD4i+7sGt +GxHVOXgUvk7uokJnaEGYB9Jh9RA+qMZx201wfn7mAcQjigf9KKSKoK8oHElMailkHXa4fJjelm/q +pXLfxZJUGSV4ZBnIQXEJQQgeSAQU6CjlVPqp+YOUqAQFBTv1xYnvQEokRGoiwiju2dyan9P3WTpB +Vfb6vXr0bQp475n3+bIeOrBYCjEa+UtgYBEh8QKBgfhIur9MTI89o7PqTvE0JFi7LxcUfh5Zih9C +vr+bWARFB8ZlV6hHlCTsWgMqAkTyqiQj1vwxw9Nl6bS/1c6wA/tz1nSi/2fN+3bIG2WcpxYUVP8I +ZlZKU98K+mP5D14/DeMQUNeiwPnozPx/Lvg+49eJpWJJjYxNFQUNMEND/GDCGk9tlVMzEUv2Hf27 +N/aIOC0lwvStzkDnoEM5A3XQoIkOZJp5cEsACQTrVBro3PlfQylCQMRlKdDPYPAKDChkKRN9M+hy +sCSQBgKgsQpRBKRs2rYUeUTynVEwu7d4ZywNSs27mVg74ZkmKdswomsenjX7MMxAjPcbCEA3523+ +D0n71AEhBKQHftEzu88xvkRWrhKBvwqSQCiVcVFGBQPp+75jx0bs+LKwfptQeqfjhVNfVkxEYIqZ +QbRIoiSGwrD+OevprZk1/Ow+36qGbSIMPUgkYxLVxsv+fStkU86Cbw6l6yHXl2LkYdOxMpc7p5NE +4UFEEpTAURL5DFHyrcUQ51A4wCgfUnH46Q8Cwznvqra9Za3nao+e5U4jUdjYH+W1yPOuTWzhgZrT +fAIzbMve49y3HpMDqpqZMP6TOb6k971KdQ71Eu29pduiUPFayRWMgFBCfr18qPXHFEOn1xWiV4FI +z6cfe9HmsidlsP+0z1JTowrBRR+oD1lnm1+9rPakAqAcQiH1VgU7Y2bJmWjmlxd97wE8UYwlHkVD +DfZiUKAGgDAG7yiuqZFJKe5UG0hNvFUDiEDFT/eT98l53/s5ySBm2+96cPJBBAKDULQ5g51jLnUO +gUyNBt5YjEqmjJ3tRAQYig8ju8lSUzvqZF0TMH+AViLp1tPdhu972T6+1OHl4/1dGg+Zb+uSe5DS +ftXVnMcomSUqhdeXL725vYNp52Ew5dCykpR0TMEFRQIlJS4HxKQG4K2nG20omHXQOlzkxaXu3h0r +nZDXlpZ5QfJQYFMhSUUo+yMvvug98ev/LOelitKPT1+HnNjrt+z8NchD0WrIsUYzzpRNf5/vhorm +cHLralKCnpA+2PXvAfvjKqflzQ20rWfy0IHCUVO+NXtAbyBkOo+pQc9sTBT5nEUiNJA8jv6CCCSh +PSVBQsMojVMebUkurXs6x0984dN8XlKFDFxkh+Veri4dhyMWKghTtp8H+tKxpHGUUBWRliMViR72 +3bDMx+ulHf5qfcvUp+fqPZy1P4MRQe+5U5jW2XK5MJ9iNVszCBV66mkYUgp9HQW9c9jkcPrGza1m +IHXbfbaTt39jspTSYICB3GlCQC/29p/b6/WGNAJuy1hHfUYSAoIlKCLSvm9UA1blcUWr6bBBeoSK +mkVrgHhLP08pD0sWXJv1TAe+HpIHVFjlixHpUKN/wqxdDaTHRG2yHrFQOmFJHJ6jm6bvd17RE293 +dU4D2fFQHRgEP6+7v+X0m77KS1lVH62pNKIFQ/wkPg4OgwYAl67yjECkhQQAv6sYjx1L5Oi1uHLC +oVng9XjbxTfI9/kak2BR/LzXC2H+/szBBUTexlZ5bu3yh4EQPXyWWVbTwmUbiJCmkyxy2iV6HuVP +Vyh71FhF457FsizDuIcB2qp5W+TcSGlhpnfOlA1bs7/Ggec3Bpb57ckEpc9zhtKYHYMbmNBF6ihr +wxBY7/uwstDwcIdseoHpastVeUyl4vLNeUOiVRJbBiR1Y8mjA+o07lGW/CzDdXFVRjxnhgnl5e3T +ljno8aPpx7NFA6sGRtlnB/q7I3l439lYAA2lBmOg0gjc5ZojOyMRVENnVAfBYQ2KIBk7N6gMJniS ++H/npj1B4+o5JQgRIgZZRumiDyEQuyyRbzKc3f6YJIdmDLVwXRfMc8juCT9VV0Tuq+6Ud6aVv9nf +ZM8TCte/F5xOD7NDXd1YeH29KxhZxJuDGppziYNiifgiY76xHdFOajVkDq3dmmHVmfzFBm5sDCkX +sHbPGNEQ4nX5Hra8QJjYffEqRW8OuyOHjx9fnux+cVPcqayE24l3nn3qIwQkR10quPP0RuvmmVI1 +KDRkJdrV9FyBHiKhUck5BCO5FByKJIUTSyoGIQbo1sIqJpWBKff0GHKO4ztLW7cX17YSO5LVUHR1 +EAkZVejhei1bJfB2g2EUjuEoJETs/IfQItMfMm+TMoG6g5tMgBzikT6cH4XIMu/Uluzyh7G8dUgU +JfMRYc88TmLtcysr8DJmwDGOAcHlbygDgCPN99qiOj70dXX6xRDJauHdpbyy9lfGEAJEAFQ9QQ0p +Hf2wixdXaYtU49X7dHh69z6ZrhsWCPJF9CY7en3a0TO8oenjdw9oH6fhbjU/1B2Xzjqsn99U5jtV +CARwtwmNNEQ4A57ygUGIivkKGCVl8BQzKQoJetkxpxGnfZFKgj6WYFMZd4ggDxboq6g1MngmfRhd +D3GCHRzB4AdNKmKd2LsnHy+v3BEQlsf64M8oqk9sYUSI4ZpvJhG94xMA4lCBuChbyqfEolJGe4BU +hXPW6QgBDrVHkwx+jqZ15VSIkmATHfmt0DwFYy0JVPsnDoEcZa+yeEEiJx2sEAFgLzObbVEaqOBf +5LeEBMJR0jUIFESRVcE7S38f9TQ/JCAgkCWGJJamgoYqCSSqfJUNMMqUDQFKUhQBQwSFCBQTJTSz +UDMKxUtFEQ0BMjLTQrVEoRUNTAAMEgyVC0UNCFAJQjMFCQUwkURETQQFUAizKiUDQo0RAjURSFFU +sVQUTSEwQwRSxUzEREREUIgFC0CpSgEyDSsSA0gVQUlCLTEqUNRUFMMRUDE1AUVVUkURFERNJBVD +REUFFFFFFFVQUETUUSiNCUgjQI0xIAlIIUoUoLEI0gUtIiUCBSqxNSU0zRSLQFJBVFQRFM1BMMRS +TDRUkhEFUFTUxVVFTM1FBRUxS0FSURNBFNSVNSQ1RExDSUVSUtFRJTFQRBVRMQqpQgKFIINBQCNI +A0gBSK0JQrSLLUS1BFMVVCUFBRREUVVNLVNDTDBBFExUTVEVLMJExFLBBERUNVJVMhBARDETETDU +rLRVTTVATRRVNSxEUlVBVBRMkxUUzUREFAUTMTREzDVNJQVFNExUzUwQyVUFEzNEBQlU1BFEyUkU +kzTMszRJBIRFM1VUUVEkRMUSUUsSKtIlCKqFCCNIIoUglESE00MRUVU1M0lVFETJURVU0Uy0RCUs +UQhE0U0UDEkRERBNLRRRUE00xSBJLTMkEEVUgVETFSQBMtBU0EsUzSRBFUFRNFBBEiSBEUlMkVUz +RUNBETRJNREkklURTEVBUVNMVTRVEEkU0lDMEREURUVDFTFM0M0RQUUE1BMwU0TRBQRARRSQzRNU +wUVTRRUUFUkVREUMFRU1TRTQpMQRRFFVEEVVVSNLLQUpTEBMQSwSRNARVU1UkNJQSrFFQ1EhNSFC +UKkQszVIhQMwgRFNUURU0ENDAQUSRFVJRQVJFNEFMkRQigFIAizRJREgkQCp04gQFSlQVCBAkBIk +qIoKiKSKJmiKKgoqSmaSGJIqAmYomqSJqokiJgoKYqpIYoCGUopEmVqmmlkWkpaUIkCkkIpFKJVo +aopaQpIiaJCoqYpKiKqmGIKqKqiKEgpogoggpkGSimUolKoSgiImmgZKAAmoKAoqRCRCpiqagoIg +klpkiiqCqaCIqKoapIIIomEoIIqZJJaKSigIkpYpqYICkpKaqBKYKaSmIIiaKaKCZiaoqCmmJoaK +gqiIhIiiomoaKCiKqaqoqiKmIqgoIqiiiqoiqKWIWIEmQiWgKaJliSKJogiZaCiYoCoYpYimoiCW +ZIlAIpIoqqkqiJqoKKiZCIiKYiCgAggiWWCoCSiCoiqCoqSIJRKRaUKipqiiKZiipJiIamaopiSi +qooSlKAoCkqGpiIqmChhGCZaIipaaSgqJKGKikiKWooKQqqQKJmqQpooKKoKooSQkiaJYiKKCYqg +YGKImqGIqgIliKpaaJgFaUUBSYDRpUggFFQpoRBYgUQdIIrQqo6AkgShGVqpqiZZiApKIWqAYYmm +qpoJooqpgggqCZqISZiJiKWkpSqpSiJqqqkImmgkqAYmhCIKo/gSGgaoaQKB6UdCJQUhAlQkIwQJ +rBKlpwE0KkVRESFIBSNUrQBEwDLFQQQJSU01MRSQlKJATUlQRNJSNKjSBEUIxMyCUyUxKtKlUIUU +tUUAlASMVAsBEtBCUKtJQikVBSVQFUhQNCBSBMjVIJRQkkVSUtAUAlNAFCJQESpTSNJQDElKsQoU +FUIlFDSlFLVCBSqFEytLTSNLQJSgUJRQLQFADQNKITAxJSoUxLQ0ilRUjQqlIKUqK0rSBEh+UCAG +Sin5QoUqCfgQ6JFpUDiERklCkGkQKEiF0KI6UQSgAAoABEpQGmgVy9vXE1Iqp8EiGhEDSNCOCQIZ +CCmEPylAyD84eIDXcNj9IfwHQwYRDLDAzIe7fz9OWQA59LFCAYISfESQDTMSQnRza91gf5On9DEW +5m0hucqHrMsh/T1fDJv+4pD6aNCKBgDFOE+pkhiUHf1ODqClTiQH0QqcQoaJ+YtT/OGvXGopbWAv ++2ZT4wB7ZB5lFXmARDmQHn5YKbhTJDUi690ZmWAfTzAHxIPsKL0qgedu2D8vn0uA44VEO1Qio+yU +dSIbkFA3akO7IAaBKEoTqhWkiEX/EUEhAo3E3vhgpEpu2Mh7YPgEnUV5tMuAig4VBVV45/J35D6m +FYez4760+bx6eH7Zd90RBRTnasQQBKOfjMHBxYo8iLBnZOsdL2TMtlwvElCw6t73v2BKh6zAIFYB +MveOJEY+d7yfHt9OtvXxOcpT59Gxht1w6BNZ79oYbBaQpbY/f6fZ7O8zcrTisuZVS0Zs/Jb7R9Dt +uU/w2KdUhfqll6MQd74TyY/nsCh7PsC0ZGEQwQX3Wk8GBphtaVqED3G3qT/ffeoj4PzZO70n78L7 +OwfJv7MkACeIkUhA8mEQSsh/Q/uzdPm1+36rx4m3XvCqLz3MmzjJ5L2ctjrp80d6VEjQokoyU8e1 +6CkzEToM/y6/LTf7V/Nnh8eP9fz+w/l5eJ88+cp3FGIRRo2IikWUQUltUltBYjPmcEdr+tPf6OOO +BRLR6Z/TtQRs5I6oyn8Pb03hUxrmKkgMAqER6gZFvKBWIKk0cG3GXLV6TTemKKYVE0/n/LxD9u/5 +cipyjJTlW2tGRLbanXMSa+PO7m2zxtttsSpswcyZFXKWoolUS/luTNtei5g2tonh95mk5J23umsK +Ml8bIOlK7GbY22hiaaBjZz7YSExqApBRVhUKx/n+Hwf8Hyd1mGrlMy232no7uX9O7Y69hEVI93lL +CmNuXDGmeu7yTSCaLtWOaOkPtf8aPo/4zXkmaNZ9Y3TRfvda13/x7Hgxe9A45cM7jvnhwrwVM8HR +XA18DMPwfAyKIkcFH4OyOpJ4iAjETheSq5XMouQKpRTv5PG3zTZ7Df12feCONT8eLU3MHHvfkb3x +QSR8Xfz/Xuvt8ISPm9cycaT1yTzMgfmhFh+nqo+q6HPZoutAaW9sK7X8cA+RMN3ZyLPkzDD3O+ay +5hXN8ufBBoYttKQQAQRNEKSFnT0bFoW9z/wmHU5tN3NEm22jHWU7st1NZNKK0Y5PmSAZNVwvNkYW +DECigzhDU1KXVQGZMR7LhP5KjlVlYMxYoSFXNlAziOSXoEoEmXqIdL50RRnUD3DAJ5mQni9uMZWl +G458vu/PYkNtrd25lEwJxGGh42ACm2itq0/pM8Nt+v2eo8x+xnysCpL1UOXCdFD4RRs1kQYSKJTm +Kpi/IDxZM5xA06mdHw8Vi3C3ermAAcii/zuSDrhnXyHO+WJXgswVASKR/koeW27BIFxI8MYUen+w +d2AISidlfjXTMF9nng/lY/NOMoRAgpVmCIgJQDOZJO3qHDQ6hD9UUMcLTMtcX5bDHBK82/R4hHx7 +VlrCTZGW2xldhjln8Op0n7Xq2tHPx6IqgvVYUYlRpaRBCiJSmqWkEaRaooAShpFoEIZEFoEEaUCl +AaBECihQppFBCkVGkEaER+tHhW3d2OJ6nuI0nGuOjmwYgAegRKaRAiMN23O1jhjmez3PRcCAaRQq +Kp62zwjEvccGSw4Wag3cEupbCk7DplXRtl2X9x/X/Wvm5eX2+WcX9fk94ehgiSSe9uWQCwry9GgN +AMXsCqvYKrFbAHVoLBY4qHgQSEDJfIHj71a1wAQ1rIc8ycpBYHUh9BSwJICbCqB2QgPIykKygd6d +NGlV1NtoYa+GGCjOiG+609TDNqCR1+ebp6+e8YXtR21S/V2iqA09/pLTMIxbJ3LoaJMk/eOhg7V0 +ZVb8RwX2DSFGDqFBEennsGsGJhzDnDzUO3o53cHilId3pYs2yewMe+/chPuHMsp8WBKazELjq6w0 +61lD5dg35smb8mzhCyT7n6A8O3YgaIiRUVjRCrlAUpROgegc8vRzGHQM+StKs4kz0TiaiKsRSo+W +0kooJKQUKHsusbuDc9UKmzRa3jB4LKcFeP2/6Y6PU+g+/z+Zd7H5n5OGoNy+4gThzhrKXLUxw29R +7AcN02Mm22iGPDx4WUYccqbuW8O0ZwhuM3SKTWqcnh3dkzQXGbM4BDhN+KG3BRDUTz9Ppb/qaOkY +efvnbx/Xnpzk3lTt7vANimuIAuUFZY3zBiQl10AqlnFUrkolpWJ6NpDNZLgEsK6KuvP0N+jLZeVS +HTSdcziizeD1jRPE8w87x6zxz00pLTm1CbWzXDttqGK7IZaYcfXaHeen39PV5bHgeC3FMSWlRtN0 +q1q6x7glqqFW5ArABmZyqXJAh8vMGyG2xszEc4w4HTs1JvcfDNGzpFhWVUzKBvqrwhjivKCch1vs +YNYUlTWVZRM1TSrrofjxt4yf5/bv5oz/LPbIgAf/OLIyCqHScw9j/t9lQjvpm6AoN2OyjG/o9cb6 +9XzVejreebCO0R/bx/+ksf+XihmgkqherW/6rs2//1//GHQgm2fzT4TfvuiyjKNmceI8Iem4BfAd +7I4pwCCeCD0cvasY7nNEviQ1x1EDiwcw3vVv0Y/5cWCtHxZOfy+kSYhAkeFUEBIqOY0MA4hEBPQE +uv5dcbucZg2fE+q2upAfi5DkoEMyqfzml5HBcRPKCG+9em8Drfw/wn+5Nwm7z+AN+OeK7Gsk6IHM +SM0xLZcibpI413PEcjrNsyasYuTXU9cMc3bY2HIYdoXbbg1BmTSOlq3VYQS5lF4h6XNmqdrW3OaH +smV1rHEUbmBzEM0jtCLOyUZ7JgVFCLs0bZtk35+vKI8Cc8QJMTA+vl4Z8Qt7jF5iUNChTWneiE9H +sYXA6uau0j10LaLHNGM2M5gwTFdKVUoI9KI066LFu2MjuFVtpVRiYWXGElK62EgjmalndqYY77Uv +P+/tsv76xYYrbFGBnVf16wl+lnmxESOBCqehEHi8T5VeIEAxGtlmlYaDW9f7nDhxc8uDME/oAj+E +QMBMbJJZRmV6+A9HsaI7EhL25VEzYuf7P+ZFxj6E+2MVtrWtX8z71165kkaiy4Zx7p8su+7uFiKg +FhZYErC1FlVaiWxRbuNyA5ldB3KkQ4MA317P+3ns0PPmHCiGvVQuVRVrVPXcdtZkyGB7ts3GsJSa +sj0W0Z1rtlLKFSrmSuWqstaUry8P09XX50+l7nkfuE/4H8X8WD1ocIOaLY2Fiq1hyDWQYEhLEJE8 +qOelzZUTTBkuMh2eeJ67dpcVdFtMKcLIttYEZhqaCOf8FOWVKEEBQqoB/oSxZppevrVA2DC2DrGL +Cq9V7hHf5YBGiYyEvpaloCtQQPwokT+YPdC+mCj/RGswC3mRQQGBQLNT2UfzvW8v95pPP+vDmzN2 +nq02HSxCohCApDAOhiEBRcuslpRCnsIe3X1TmMdTbSKb0oUYHKCxE+OY5DWrzHY2TatZ/18SezVP ++Pp/yfw2v4IEgxD+q7rVS5GZWAY1eHr79EESKJQgI/elgiSgbGA8IeEYiMbh4jlopm4XueIjATxz +XO3QxeaStUBYcMUeAuqHn/NGe+OzssdGsD1G7+/h2LWNeAKNEbgQxS14afrZu74HtJLHuH95wz5c +QE3/bX7I/939dX6T/o5pBk/yT/9qQB/sD/Oiz+f8hLg/1Wf9qLtnz53CTzLzsKugOqXZnigU8dvd +/n5dEY/U0kAt87brYD+Krbc0TnQBVFhs78H8+zo4zbLNPXzXRy+TxJTTclFn+zUKWF+P2SS6+Gtf +/49/VAPXsyH9suT7cxPfAvw6/89PriZE6jaMJkrBDingR/p1uoxCOF8+Mm2YTytC4EZnYJXgP24B +ks/y4cNOGR8eAz3ZaH1tvV2aMRx/2jsnCyGS8xMmESo6YkK4Xp5XuvyE5BGopvR6TNiGqWuXLMTQ +L62Pc0RcbrR32xGxI0CyOnj5l22SN/AfZ8BTa1XhZg/T4cRz6RyB6SiAhW8rXPE9I65SKQ7582HG +LCLkyhQHhE/+XZcyJWUAClESQjCeLuJpcWzDUHj/Zy/yzwgHa3H61IAJYp9XO4YZlSzd/55vmaIA +bSvMNMw/oD+CIAqq9ju9d4qo6BJInSiQRNHZPxgkSmIdSoHhD/mVBD4BiBa5zlssH9PaFAUcxHYQ +L38yRct5hqH9gKaEC1UuFwe5rGLMqIrXpYk7Empn47o7cstv8sesHzjD6J7LXf2s0Ai4QHiUivg/ +DD/zw1vdFrIxSW4UCgYf5Q1q7/3/D5n28hH28btNlk63E5Dn/WdgG0yNi7v4Epxf7WLGBNtlrd/s +w0FGCI/DKjgUAJcSVJAUp19Yv0/5Dsilyx4f9A60AhT9/582b7du3bznngoIzTB6ZQEz1BnrKCux +to49WL7T7PD17PBfvjt278GohZiayt2RAb/QvZyChFgxVFVHAKgnRmJR5QBXP/iLqw5Woyef+jnd +kH8S2OrptZgEz57YprpEQF0VauDA14R3w7OmfA7BSD3U9O5yY+ZQOzYqB56ZlG6/4suDCaVUIyKS +g+AME++EPqIWim9XbovC4Duf9fEgGWePDy2s2CbD3YTrs+UHX5ijJv2zlLCMb8wbse9vh7rCeztE +EH+908v8S/+tpaUExKqqqUChVRSiTqu5kRUIAYreUKGegKaB47qBytniihIV6H9upTorH9ViUVx8 +s2rn+K/H50lNu2jcDsA8BXwa0IMiCCopUPVgEIRlBLO9q72LSraZEEcgWW5zFHKEvnLqEWg+o43T +G/b/63Wdv4+H3b6/Xn4o9JQ64iwQytoZcUtAS9xGnWCMRQxNEUJBQdCuRnIVAZSQa6roPeY+Hknn +uso8re2Fmj65oXf6yvvZrHULyiX4xYYiQUgMakAHbY69wu/uNLMusjaYiPWvsyjm80llviQzOaBw +zqkLMeDzLx/oUDnzCLVT27RJxPz/l+HxjGCAxaVGH+P89EeCHof9eRd6HgM7EcHoBhKEqAN5gIGm +qZx2zNnlkT9+WSasXRiZFxymcT7PR09EpIyEMgFEW62Kzq2xf3gNeh0ph+rgG8aagiUteXmFkZq3 +b1sPs/HJb6YrOiIICRBYOHMfqRKq0sZ+FWzoIHQGZUCsFDyHBGIeQkGYfoR4eWmIdwquiof0GX+a +86CLBktrn+JZbbgRZRJSgu+zGgaAL/SILIQZAVRvJGc5S4w2sgc9HhjAqYPF/A/sG6rnw9Y1YR1M +AGQMmQD15qxr+PB7tH9HY9/b8fSTbz+nPUL/mk9q5ZUYUSjJazRNho22AI2x/HZouUfhKEJQsSCy +KVb1CuRzhHcAK5xbyQNWIUaqdokEsWF7j2eB4+I6k6azSVukWUbcPMf4ma1TaYZQzAMCQdle5Y28 +NuRh5EEgRGIiKzPiFbs2al3cZEtDkihYLBJFiIxFrXRuGWriHo5Ivf4VK6wFrAgBFUxeAUU4gtEU +Qi2yJWOd986NKbnOCIm+JPaiPh/adOjZzyUrcL5NMsOEE5o0W3n0yR6Qu+/7N90DEYg4RHXyVBy7 +/dk2YBHiciWdqhAx/yIGmsKlB/WhyLnd3D/BhAZByFhTCidBfh9m/dPkdMPOPb/f4aOSdwcwTDQG +nFzoCisFCkncQpBOreJuYkkVVPqAsp8vDhisEkduesXYuOE9k33l/t17eLkFaR3J26lAqdGTADvy +Zs4zqjwHjEQQWtmAQfV4aA4NzUKDSZvKRU/M64RKDgVGZp721ziBC+O7G58SYoofJ1vvkDUUAQEo +3stALY0UliFA7dM0HBKb4Elmx/Z9mG7928rEslxmUt41LiUf/IvgJrQltY2Gi92aF4NYPuZTSqkQ +YMh4DkN9WS+78rvj3ve3NLGO1RT8S91EfgOi6T5087W5fr9Fb7BiihglbBnQKc6bvZh5axLf/gqq +LQwyOV+K7cSOgfL3vKEoqlXmCfJQDOhQUm+LoCzNZUPvwW0jjx8D/z8jMqrSyuhytzLcxmFrLGxV +2lVBZTGcEqPVtcpCNMVMKXKq5blsrn3F6P+9wobBZlD8nWvyCQ41/64H5Aeo/ZvgjDd2YU44XsnM +f+/vps7VsnAHHMlBI6bTblumGVuFDGW4YdR9+572+lUftjaKxs6NjOzibFiuq6/N3YHly+bszaBi +LYTTts6jFjA7GHcIYds22oxjPCohUd9jKd4H1tcGL3HfoY++1udBnXF3gb3OXbacnC7sxMmYVhzn +MCHI5taNQN9t+KTMcjMGCzGN4gG0/VOMqTRE2z9ptKKBvhAGwxiiIUTaJDjgXiCCkQtpyrLFqBla +4bPh31fp8u2XRSU0kD7skHQG+u3GNMRR13ZoyEQrQEQDH3MhlayJKIJTheTt1O1ETGrGMhMjMHAF +3lFF4g+eHyz9XOO/HPfFQSmQOqCOJ5X5DVN11yH1j6kM1fnJRzs0ag6xVHaM5OTNzTDzjOdiMdg/ +nPbsh4KTdPOLu7bWzyGxYXEEYFeOdRTGCjUaT39v1Pu6faeng58+kKCGvz5+f1Hx536fqG6VgP2W +aV38m3vYyDJaUyDWOeIgnjpUnQLAl/b97Mt4du22ey2J2HtOd27tthBxh57I6uVrpBBOKLjDatEm +iai5DVuVVDFVcYSG7Ns9tgtZ1FP0bd/c+9nivd07jbolTOYA7hTXFEee7jamTp2zgSCkAgS9s6vL +nhSpADAsiDkG2ygWds6C39rc8Hm65w+CEa9vJ4QosgdwbEj2xKC8I7Gad3Pc6i2D+Xr2D4Q2hUgu +uRab7Submztz6nEMedyZGJrJhwQaXM5jEG7Pdw7BRc9hJIREYk121wybWhTSnx1pDFPxu2Gmn+/n +fl5/fPV6cIckEQPrYAOEBSQXgPhBif1k2KQUL7eRPmRU3eeHWTrHC7yiqt5EcdFN6NnEH8Pyntz6 +cFjsoh6bFxkFPcdennbb0Q1jvPCJNs2PnhOh8vI8iFV/QXgG7fgffh49ed5fSuSqJMbr8gQPZtwa +N9+NRix4BIpYU2w96fbwcHId4LscgnIJrRsm6AuUy4QimXLki3BbaLblmIplbi35P3Q/id/8O37v +q+n5er27oj3Sk0HTA/kfn4bC8I8DbatNxBQLGe1XYiMbFRVxycnVtMkH4pva59ndpybgxQXDnks5 +1D2R6Xd2o0YdHURcyTIK13L1FsWNQ0ml0HEPQHbJEU11HGxITaJCKxm5mDKKhkua6EUeNDSdrZcj +Yu3bnjCiC5IpVM26JCulnp6pTZQtMD6tGON1Zaebj+n7j2+X07G8rp7PPn0KIVfvVJHsU0qxVyxM +itx2gMGCgvCFmcWKMIwOf+Jt2MYodXuuwpDEvea71NdfGrn09f67/R2+8zp6igWNa9krQYv0SZg5 +FdU4hd/T73hfbdJ+eu3iGrWf5v9O955uZRjSy3YuVsH2xuk1hmQ8e3ZH2/8SnnPs4j0v/ksbxk5+ +uYSDkCCKq7M6gEEFSEAxq4Bb6sFKj9m2IyhY2+LU0vQo9tSHTRpXRFoShUJdog0qJoH87g/vHPvj +77RCIXRCRYcO4hylsQq6uus7bZW6RQ0qqjxlMJKMEZ4drYi2zTsj1jBHCi8ZWbZExtIlznjNoLQt +2F422iB6RJLmy4l7M80KmIUcTirQd+e3oUu87+148rNOhrgNtBl5BODh0b73dssjG2OZc8k8xZz2 +Z4k1GuNQ3ZXJLhsQiKvT0u2p3dOeBst+G5GEUDkIUSxM4m5YjuVeGXGrx1RhzhsyJtjTnQ5M7axc +asa7ZxdnXLmHlDWRowxbGS257tnuUg1trGYGzsqG0YoCQqqpo6KBJeGViN7m6lVl1wX2mnE5KIcl +YAVzJqRBSnh5YXTdDlPw6vcHe/6eXI8/r93r6h2444kO9JZlm2OFpNeA+CVbrimWw4JJvmypUaQ/ +MeRsHJDJuw1vclsaQZUWnHGdT3dhqOnGNomYmKu1LsaJn+rbPeBHu/O/FRRFBRYNiGwJta2/F5kh +pC621bvKmrMttYR7sYZLGEM9+uJePIeHjgU4BTl1eP7/99tI7nKD9x6Nvheu25Q2jHFYqOKWewWn +jNz1BJcwskKCuMu7LEK0KpGyYw6FDddrFC7Gm6/1N59Df0fhfh4L4Q7GuiUbuHngcbrlFSNI8nY1 +tEpqpcnGliXgQLaTK1CHQiQyPDxQiDaxrHcbRPSsWNUZNPWSNZdTGZDpCZFFOXtaxnlwy0cLs21z +RcDzyf4ykfdi4tb9u8ek3lowigirz7bHoHRsAFZVBUlSEoIU8+ZLnVxnk3XGStpZjdiXCMWtFwcc +UBtmJfXyRkPIrb7/o35HHJxRlRF5a1M1VmRpl1IHHEVOCcg1MApb8Qjy5cuaqhzzfWttEo4Xe6YZ +apd3q7DSC43WES6kOppy/d1AOA3b4LWuXhRIkKOrImJMSiwQiHMjdG8Q3giqFV4wpiFqQUoW6mVM +GxhfhZmUJdha/d83HkBk+vcKnd3BQSMXpzRbvGzb755RcAHlCbwIDerN5OCjAPiPwIcThR50vLaq +I1qq5eiBo/y23QtF3C9/VbHU8y9yQ0REbbEo2QbdnHB9WvaSQmbOxnuyOe9X2hD67YvB28bwoC9y +9zIWuM0RI6tJNVwoXVMHDWmSa2/dxtw8fnDX6jKnuYVIT2q3UaMRJ+/g979u68OrqU+F412cvJOA +zR/UxLihBCiMTyX1DzdMTZyetSSzONBUMqKEWbNN/avp6NDy5S3/Nu8f7thsdkzzMkKMgqd1mAjh +aeKbxI1at3bG9D9D4HlPvuLpbPx0Yk/lkq89j0t6d1dx176VUO9vzht9BNF44LyorkuGWZYtxiTL +IT24WHoKESM/zI61/VQY+ZNGTCzMczKRbMtEFmRiU6HHQZXfRHlQ5kLwAzW96KNYFBrjWxApETEJ +JoZVyLuYaQomOJsdRN8s3AXZxsLmyzW2Yb+Z20SxGvG6722RLZmYVFQdeladxTSHfjGkeODsXKdQ +F4Ae4hJYEkllVWQ3rDv5uPHBfjH9H5mCi01AsP9pMf6x/Nlfqhuf5sbtXSQJLej2J5PIu0S91pdH +RQjb9Nbo8miQeEoXh5YpURU6IpKJR6TmRCVJDqlRqEimSSSjRhjSYJ00GWjZcQppKTElEEyYyYmV +JrWDbVYleVOmEUEejlpeVoWRRprVUwTRtWWxqklxLBWtpVC2AMKXbRbskRcgTqGmkh7kMR/V64OD +3TclSjxUivT5lc3UoRmEOxOsjoIeJ7bCGYl4drKoTktCNw8EjCTo2I2OsRWg0xttc5h7RcY4a3aU +Z5zWd/Wf2tu+AU/o/IPFyJ6iwL+lAmu9yThE/LJvKZTAY4XMwtWYJUmUsw9Hl+z7eh8U78/9Tf2/ +1/OYfGMTTVzx5MH8+QA+fkg/crgDOCsfQoARE8r4UJ/8Ie/dMQwTIjLt4HwKmqaSD/X/gMdEf5/s +/J67/6/d/qfn37r5x0enXQ5Dplcg871O03PJRbOFi+jL5zveP1+HkeY1BQ/io8dXr2x7GoFIT1KJ +19XxGGnAturbzVSgusZxLKHFKOkLIr4O70kgjYQr5/b9fiLok8IXaRBYdL5hk700mzVRUZ/5emmn +dL2qY3/r6Nd3/z73zvBHjO+FkEB6k2WMM5hoghz77r91V+IHY04JrbQ35uNv3YmMfbz742uFDSf1 +++8P5+YOk8PHDrjo87V4OSI5Dw1vUsHJtxNREzQWSBx7eNzdEU0Vc3QpN36dUNhNt0SyoO0PpxDo +S4SDlU8AxSA/yiCzEVUgFDK56F7ORQUYUpbQKMmsf2+suN3d0TD+/+xVU75+O1v7E+Nw9x/nmHeg +h7fvm3jTyrT/LI7hwF/cdtdDkdX4YuwuekSJr1OyIbQ+2GEGhChOcRyEldsmSCyJCFJSFCUo0S0S +0DQBQBQAX6SmAlIhJFA0CUNK24RPPWHubki+ovWZ6zrvidnrDnZv8R/h/T7x9u0j1X5bXQiW9e/P +Y+PUUpB97XN9YyfXtk/3vw/PDbxvD4/yWFN4Uz+j7LxvQeUpc97njX6R4MBsJm1mV1ckWJpMtJ9v +iH6vTrh/5fr/5/9tcP/71AM7BbQVwMVJT5lHz4S+u3+cnSM3t3VOpa+u977sH8SxVFEA/+RREjKd +fwOlh2Gcz/wsCo6Vd/NXsR+UWTBRtDARFH/D9fD89cf/R/tx/2nn/vmjz/5efsIdygqIAfKVFVD+ +uhiCqGhKIUAUNCKGIKARVD/JjoFB6EQoBKQoRERIEZWRKVIdCK4ZJaQghglgglEGJRqoKlSmZGIS +CBgiCEIZmZooYoiUHoAEdKKqmZVUD0YXr1yenjnYCslNVfNg4xRfxnFKRNIpEEiSygaHeWYkIKqJ +JR6oAdJBIQEEEkqQkgSQQEAQSQQJCSsJIQgL+sLsgDfxOuhdsX+XJnhnBjqnIT9dVuTOCxZFgLCb +5MDFFaYKCtgTIVtoopIYYmIpZJahpIikM/146pKuvgwFQVxltxUkMMOUfL9NgzTIHjk5vGzDYf0T +9vTA0JISdJMQlJOnfWgkoIKCCghNW/HnqHBufB+fgNCv0OZNBemwC64YExQpB1CMgIhApKBosYdJ +ClBqw6WlKApCkoB+mRM0B8waKQpApagHXgPXqHBoUilGq1R+Jd3QOrlPLBnsL/LW3G828Tfu973v +efz74++2KtqVvh3sHvervt1zc7pPnyvN9scrskqKnmp23sNCo6W7s408CQmWGb7RJhmRkMRp3FAx +IgO5UlyXpKYFRDpNDgGVKI7ua6pqZYtcXN5GhG/wffHj44qjGhLr70Wxh/Pu3keI0U2TWC6FkoG2 +rxzw2ATbVmzF0CXjDjN2uFQbVUgxyIYvPy342J3eH/j/v8+ngILaf8rhvg5wgBVP6Iwa0BVAKVPF +oeHAveAG2DWSRz+jVHaB7bxB4Ta3ZmJ/HLms30TUtyYtGVm/5Xc6bAbk32DIuMLAFogYzh2O2Zov +8f5/j/hmbx2A0wt5j8CyLOiVIIAYhgXdyTWfDrsbu8EUGUdkLEPm/8z04GmQRJATdaEUKiq99OXL +bGZ92Y1KHLEkunLO7It9XUvCZAoN86cSslGDUoT+LtE1KUIF5hRLgp3l4IpoLiDDMCSRAcba1C8J +tuU2wvFD5Wa1ccVjxBNmX5XiPGrPny4GscteLWllEoaMM0GRAPrbo0vquxxJ3PH0jdZguzvExCvH +hE/Ahc29vK9n9PQ+vEbcpxvvdbtXZfFH5pBzZ3GSuLnb+Q93kC/w+Y4Is6sQihsYvctODn4s7Y3G +mB/Swivc9/f+x2WfsKLc9XSTbF2CejuH7e/H3x790ZyL+YyiEQinN7bqLhKGufN4ebz4t8Ge4aYo +XFByQIWnmWAaMUMxpKHJRQTAX15D3J0z3XvWCTwDleNuz7xk1jRo3cXS5GvuzAaTmdQHQgekrxcD +JM2Hs5C78YUoUoIoJxdmmmlFeVuFu0M6bv0u2yTRd4xExlOlmK5f8F2LB9cQW4cT9yaGYgwj8yJi +CdxAuHcmSgtZpU33ZPIHyIlQ8kCgQ7YFOYFdxSCG22bNFzOpNb14aOlqYMXd1uaVTIJ3tMNYYbJX +k4jypvE38g3vfk72/Mk+jebz+whvrio+dr5Mwy6dRcRas1dqYC7bmsMeSXt5t+NLS5d/Lh9bcAEy +gQRC5yBREUyZnwtBiEsMyIzry1o0RDa4JHWiamqx3qre7cXDaJpFyhiYLmQoQBm9D9XIhxbu2MlN +t8gZERnG1cvxzNvPtpCcjW0DWqNpUxsWctGzttsID01mQZrNtGoIwh4aIJKqYlBGqQDlESgi7EEz +UvHGAS1szuZ4crB5oVBhw74Xh8MrnQEcu7q0m+jXTWJrny34XdRm8pKmK0PSmahzTOCT1h5NtH80 ++FT9L203rxV0SNushzwDHWgsEmazjNauGtjdm+9NYMaJ9rnSRX5X3sjr7tDmDW+j38Y+x9gufo4C +T2wb+xt3vQL+u25f133p8bMfLQuPtj/Tkxue/PXdwUDqUOJCkNSIUgvSqHkaIqiHEaYlwo8Y1xb6 +vAj4UIbngJk7w8jqkfINAJiUvMcR17LoDgvLTiDQ6NBQNHluyAsWJA4nzxlRHb3q8/8D7c++2xjF +yL+7x7KpWb4VEUbS9+txxs0YsLyB7bGtipqo4Yik4R1ZkdVYJulIYb/sPA2wuT8a3mGHqkDpCB/o +I5iwJVRN88fq3pkigJE2MOBDcpKE9H2u5dOCtb8InX52jOFC3SV1FLCgE3NPjifH/orgg9cgOGIZ +Y1poVEtgoQBSncQPOyabUiI/rOUojDr1yiIO85IuHlFPmffwEBLRROwlGi9I5thpyBwiMQwETPoT +YaSoIYGGRURipAs6V8t26cSYjZe8Agypm29jDniwV6ZUQWDNVkyCJEHRM5BzsrdkoExREIKTIOPW +/i2K/2cI5EmBAchABSe7RF05g5B1wwZzn5onOT/RzVVYFx5HZHLmKJ7oTci+iRD9Z+iUS/7SzJ9/ +3de/f7unQ6nOG7P5ZHkgiGIRUDLWKqBWSPIrsqK0yWz5bZG8dsUqTUrPdHPd1+bgMALgB4lBEBM5 +M9qxPEC9UTCLioDgSQ6CBKix8/kQ9flZPNy/I5CcgpbRI3GzDLjgIlzPp+Gfsy9ENnnyx1r6NGo7 +/bhCWLS2KRYkJCImMCCAVoAGm0YF7LL8B55VF/nvyBydEjH695S98ZAD8pEONgXT/PCnNSn/V39G +5TzzPhrXmI8/w7zNhB4YVAbQ87MEPYZZFcCLlXaRetdW5TSXS5GHaXQAMYYN1KMJFcng0CgBPjpZ +tiqPAoFM8ik4CihRfbKQln9YQ2HTh4AbxPL+oa/E8eP5fLy/v/0/byiov95HqHkBZlFURh1GdR1f +zo1epwFmF/Ltdu1uywxwP+n9q+NRQRGQT3YKs2z39xFQbaJ/46Q/Ll4sthXY0jnms/jGts2ky7aB +ugqJ/PX8/beL3ApilZgi51Qf3blbPYL37AqEkxcQP1OvZxoA5gRiiYU++49sUCk4v+Vo/tKkMGZU +HEIUGG9Xs7ubPh6ZF+Pzx7CnJCU+eUr7YCXbKskhEBJ8/X1ngHxpnB9+Zosad0cbw+/A4/SjH4ns +YtJzsu00ek7vZ1CVXR8I3rfAk3KO73XvmxCZMU0oHpTpUYHC//lx5/48LIum399HDIE/BcOD3IAi +DfhQFQUQEfxRhqq9XersuK+RjA2TgIEFAKInUh8itIxaTwvcHe/1u2jmnaYRFPN1fOMnsc5TTASy +SD9f23ujExSq6rvPU8nmoxejgP3XQlnAXaKPsfyChPQzEuAiAMaqEiEPRwA1pZp5I+fn+3uBPv33 +eXVWH9LdIVGLH93XbRqERAVPNSFGCICCdx5Xzerp8e2j9b5dqHu9RBP2zAejXyQqKkjngxRudPLl +u31YWviRcO3dL0eYdp/3pVIh/6tCowXSUFRCSpkIokimWGYgoI/y8/7f5/NzyqrF9zWLARxn1ZrP ++39vp2DUgqw3tFj+gH6qfzD2fUf2+75PTfZexJFXx9nxL6C44iYqIhoAiCpqIqokIqnp/w9zFJQQ +STH8+NQ1oxUERTE0EQMfT8npO9kUhQwWfl/09mp/v8cnS3HO8iabiO3yyAJ011czaobdKjx6xv2q +xM49BcpBK/o7HDrogA3+ajd/bCt6TE5tRZgAF4djf2iDxAOEaxuUbj9u+bFm0QziUAxAzjV1o1UI +xmYZOHs1OJqXgeHJtZhhRRw4uGDjXTorpl/JXOSN/lJg8v4ULkHJld2hnSvC0ORTAr7sJpaiJCD8 +0a5YykpUhTSRMW0SUSvYF7PZAwJ4uaBcCEU/DRQDjy8t4HV/650DuZ3Djuilbjf+H+L0WFEEgKUe +uuoH/T+VC4Cr+ekG17v48YoWvRYHzy6eEv7iFALypdCRmQyi2KV1EjSRvFpvNTUXAkMOpmYeJnI8 +OGjpNAJALQr+CRA+X/LbgWZSfZCJHPLIzQCgMVAG9RCNzJ/TpHWh9xE5ZwpZPJzhLeeHlsat5/45 +HUeD6M+vOXU8sP7J2J879haKCKkozDom661rWGOzRtb9dsYnyaw19v1HHzJ7vHj0dlgaT2JDdMYF +Yoo+PltlyfC5xVvN+77Tu80xyDPK2Cd8Yp9qS3Seal0YIOZVVXKvkXIECOGHd1PQPkMjfZjcoKhT +8waV4enomxzP+8fV3/J2kVav6h6OwNO7yYwOo/4l2Dk5Dw97+ODv0z6bsbH8aeNT6YZtQYe2Qiv0 +BxJE4nYjIdx7yojToB4LoloRLQx25tFKGmAc8Mch1/1xN3bkO5U4NjkPTWPN2xXhtMMq9HU0abhb +jTkarEXVru79H899Pwe/IaL1usW9+F99H3dbZFXYMyEyiYKLkyppmjDFHWHP6vr9PM/fJ7Tttp/Y +fN9XGnP0P2WmXbJn6H+1UYqKId06iCiKOi9n6t9zruz8vaUftoVjQq82OxqTJIhDSfUmBMT7MWzp +x4fbsphWw+9H8v3NvW9msMyJesp2kFjpz/Ew7fWuzHUh9ke7w8nXfS7cSz+hWtZkoOIKBn0dvd4B +LCTDoPSj/RCHZgiAT0VwokE2iUFG4GM3yN576F3wuCd6HncyWLJS7D1IGv7pD8pnDskiPUsj4pUd +nxcy9Mu1AqKCIbdEy7amIIoJZROwUEK5BIRUoka6yyTtNsygTNLY6/GLEuAoA3Xw9VsgkdEUcIA9 +CM1txUIlvokNlr90FV2Bx2wop1p3oa67YGNRdqUyrKu3dfBNee2GKauRd3gMUVayKCzxb+rY99Lp +qOz6XyTSeDsJMPZTx0ayeQ0VApBFhogIe/2h4OpU2hF/CI4yr38b8/u/nolfBeHJoYBwIFf8Vzup +qgsQ0TNdEysxpVxF0g+d9BZ6spw83o14J6zfbOmm8WkO27mTHMSyXGj5rcoAYZCqOYuBJFmlUEbl +JySYm8TmKV0yNGQkbSCZ3bV0qvimJng0ENzvz3aqHiM3emuvLWC1oIiOzavPMts1f4sz4WzTVfKm +Yb5PkfLYNsU6WioeVKwOBL42FE5Wk7CG6TvQ42OWSc0xR0wKn9Uzws6s6m5YKnSwjznmPKTc+XW7 +a37/brXXnQakkJIg3pRvnMl+SmkO5qx+RCjs3masIULSqlblJxLVgs29nhqaR0DEpYMigckJ9SSY +zyyzGRTwfOjhTyZRLyzlm03bjLj0YBveF3069vmJa/DuHArOpTn/ek0ET5Pkzt6eXfz7ZTdvIsLv +aDzTm4qpvCu2SxexBfEJqaSHp07MQ4VO5pDYJBIYsQTfSYa39PUcsMyy1a39tOTSYAsgUwRsTpMA +wcW8Zb0PHnxn5/5XfuhhcH0QKTDnjov6/NV2fa4EeZszOYgAy9Ju7HqWb3+zxY95senTzBOiTjNR +8bJCIGZpiKNlqXpvl9Xr17e25EC0PDzAXqVS8lFnAB4EPRx92+/IgDXF6aTex6JNkLQQ9iKMEI6P +7jB8y95I4fcwY7nqh5n9Jf/GM9DzoawR03yKaCwYQk5BdyvrMMPyUyi0Dx7eny661f2kOCCdkxtP +6rxMaFBVsZM+eYnvLJ4h8SBn5fYnbxpbdcPnvsWfQyA/Z8ahOno+7wiAnLOd9BNoyOW4rSuncBSp +PXy9pOTUDOvYPBEbm0imRhkN6sTXitDxTPYy2LSI1EKIw4vpWuZw5ob4vgZj4Go9uvfXJ50h4L2P +aRHcT1c+5mDxB/HJeEDr8Yg4h4ANE+mO1A8PXceehbhAeQWRYE14XXc7JYntIhJWQ76Lue8Qv0+2 +2F7WycQELB4iC/Tqt7oRKxPNBw4knz+QIcSdoUNe2SfK6rJxL9nXxevHu/iuTUVUh2jr0434DuxR +GErHE/8aQeyp90BSpvv01rx16Y+uTrrg0XV2uefnyvPXbz7a9na8Ie9kn/l5biqqIiqpVfNmYqqP +Y+3aiIiJqKJKKGDyH+6PQefHQuWAGESVD9ngdSRhRDpvC+ff/Da+uncMeT5u/Rv312svHbIGMh7G +QPFkmcYvzEHEBzc7NmjRU1QtKanLrL9AluS8BeIvTrj9oDA6aFkrT22MAxe+aov33BIqit6NoAFo +RXHXwqYhfOD37/O79LC17bFQeH74nepscSBZYFpWZIHDQmUKFAKFATvmLQc5V2KRg7uIloqKDpUB +JkFpSpLHQGhGo3xJfirj1pmaDg8yIyimL9hwLEPlBDYTwZTkNr+ObwMv44SmBZ1oZ5w8MhOIMpBu +BAkRwWogXJ2eXrqE96JECeuevfgStpsb+Px/ZsSQ8vN0ilt5J8xtITa7fB2NarNQWKn4y1ykWBLH +9LFMSKycdx+vQZg5F2qdNFB5t1CCIRd3MgABO3xh3V1/zlPALb/b/gcm0DTguhZ+ji0VVVURERER +EfVf9/nX64+78V7f0+V0d22uqfpsfzw/m89Y5tblHMwzJma1XNODWoTQEJBDvKeSAvqKDPRWv+PB +Hx6/00YZHh63Dkr6ul+uboR4QEQlmagI2WMJElD5buCSRZZYkQSbd2eYUQCkyUuE5ESYBMKNqDBS +dPBCwjU09Sm9yjZaSxlHUOzFwgGBEM2ypousrrMwdrxlU1tTq78U36WiQ/KfZ4+EM33EDN7dg/SZ +AiNwSejEmJCZff/s7TaUmzwb+/Ws1cbRMed8czzW4bZt1bKhUimiRehxuhYCUEkEgbo2VpJVEZSI +ok5i+MY9+bvKdeZursxT3/7I8L1W2QvrjWfaP6p78jsIY7CBiIR8f+y18gOBCISwDfAt8VA/l8wX +mCKQfbzEmE6V/n39dzfn+99wh0HvioIod10RRz+cno+MSiMrG1ji6lkqrez4l95lkWzpMSJKi9P8 +6H+rlEdHz6NvWXz69Cdwr4zqMnT3InlDPI5BGCEXdpSOrt9etPCSUFCVQFJYrF+Od6BEbzK1KlzI +gQdUZLv9fosO8d+xfpUIQeUs23Q//Hvd/b9U8Jr9FvpdO7nwDhqICFaaApoWooihkiqqiZqaIJKE +IkKSIipQaiCUeXnnl5/0vLQLFMJM9FJUYqC22LZxYJgamigqqpBZeSZSmSbpkpZpXhG4SJSeRIqA +RVWFqqiKqqWlqqiKqqWlqqiP9+/wf5/2P7tHUcS88B/C47NmYX3tTTg0qCdRAJb85NgeNgGFQQgR +7QDvVuwPPOaOMmiYooooopF/Hv6a03MvR7Y2Z41Mk1FCljd+nXnRhw77BtacbXAwXiNFbUYxJYqg +eSIqBYWnmIWEgh5IGCSHkeJmZWHmZ4kXqEUQVqllLe75JIfzG/T93we+98e8JqXCEyasnPaD62Uv +R9emf6u2h/4T6uZxy1xDOFw0ZUu20x2m2GRa3trE3YbdWE5fnd+PPkTPo+xnuKonnKootIApQqq0 +qiNNAtIlIANIAUiMSUDEC0CUiMBUoQRExIUpQTUS1ESQMFKKFKCtNKFA0IKUhQqUBQlIQTVUVFCi +J/ChI6bouhKBQplblurpSBC6RuYmiJkKVCFFMgDiEKYhGlaQpKBCJWhoKWiImJqS0GjNh2WGsFjN +Z1YNGLJFJmxVC8VMcPM8rFcvDNKrSH9pyNKLTRRQqRNJLFJVBFQU1FBDRSTFMREE0FU1RFBJMsRV +NIUEQ0kwC8oi4Zmp6eimQRaSmlFWpaI0aWCqLBrSyKvL4fJ+X9PMDAO3T3qqP9HiDuf19H9A8z6Q +ielfRz62yBAkP2InY+Hn3O7P47EH+RBALlJAUKQSHq7NHEOl6Q2DQ9PrapZv6/h8ZC4/dhg0SF6O +DSLGOZUse8g9EwWgKRH+Wvkqk9OvjynhthtkpqC9HR65f8bvHB/Ga2VNWICCiKznzdHTkPhv587V +x+EvCoSeKRNf3qLjeKgWgZxEn9eNCw/vJryH30w/NbCg0l/sqOM/bfhaNGtWmUKTL+1QEEo3ayhg +oBUmbz3BqgYkBEB5Mw8yup392GN+Ix/Kp2GFREH64uZBSQrQWOT+yjWQJZFm+FSS/0ROlxR9Tszs +RyNBxHj1Pojo/rtsHDTX5Eu76d9D75jSiGlEQBGHjR0QutyeuB8Z5l6hOEAQTgEoCoglIKfVAo5I +Kn2Hf8Y16/2aev6509vw0faPQlIArIqghlZuHc79wenYt98bKj0BY5fX3IyY3bKGCdp482FgfHza +BSSUW3uimBA7tnDj7wGX+39mS2aNhsCD/cwf3fi6EimMYHRHf4edjFrvlmTr7DNblTs4c+r+phNu +wT5e5Pf82iwSAjfH/VfO/PAFcxHZpfnXFpCiHcs9jmDN/Ttf67GDzT0KJ8Xw0CThHpXU5eyG2WPG +TBsKxaXcYnogUaSVf/MfVYRF/VYql9PH1xjjfx57es7qINy6fjVd6B2cdpT45+zb1CKvKN0jpRzS +UvGIGH1o+f4wcOWqQYb89pj1c8oc/NP+ZR3evTNFxTJ7bvp26EBnGAoBYPh16/45a53jt5D7odJF +tMB8d88cXPi3tjN+oeeCT6zZhFZv6ulh+g/045dy9Xb86k4+KhdQiZzgrdoeGGk+9Ib77fPqaTHT +RBVdeJ+J5AdjtP5UO/7YFn+vdR/5OOv1+Xc/knvLWqeuXu1rKLSipWIeh/vhpcPPJYG7fysKpupq +JKOAmydo3L/xzqOiP72tzetvgY2VQvZK9aBEOqLyrCvd3teHRZmXreP5X+9v4JBDoAuXNmzeBEXN +Wj2R0/t4Qf3fXZgHr/f+H3/+Cf8e0/Q8g8h9gXr/wM5hD3TmjbM9DCVAmvSch1sLmOBmJq6grXYA +IE1CCgqAeyoA0ACOgVANAJQKNADpHQFCoixCiB6+jPruUE8lGohoAAz5eGLFwh7Ige1AUtCtKLpD +09vatXAJ7JMUqeHZeIffPfejjtbBrmG9nuyFNkJ+qU3o70Ax2dGMUttNNqWZlUjbOSn74jy+pRVf +r+2MiQlQhB8KpKEgf3kvqk7pSgKatU9HiuOXhN3jFy0xUDpyeIi5kV/bT3eNb/5yKOw7c6hlXp/j ++OadhFBLM6Lp5mf/Fun6D6MZPsuapS60BpmIGMMYYwrAbYY03m7d3tADRnu6rJ0nKFJm9hfdxk85 +O7J1ruWpeNd3vZLxb247OVAyEmSeXjveXXrPXu0uhNCaegND0+ZOPDeB2yHQNoGJcoLJUmWwMYGW +gZIeb7T/Pd3f0wYdsgvEZZbuDrEI4l1tzg4oQSDcQohcgRHEBBrnBDN7jA0Q61vGyI455BFV5ZFQ +oAZQkAA99Pg25AANgu0GLG3AB8QqHeeFyvkiY8IjI+cmmJiPj5/v+gD75lN+F/kgkPPeTD08Wuz0 +9UZ7pGuPEGfXzvuF72uNRttw0d2ck9xtMZX1sXung8CSC93Z7Y7O8lb0+7snDoBcoBKBiFIA45Jh +F5+dwwJhfcPmqCfybAszE/qtmGpgL/R6b5JWuirQZfwbygaeio+rpgQh+6XpVUsHF3aovYeVGQPr +Mt/mYZES/pB0sw0oiMUDmVAArKEYoBtMQh8u3v0VB2d7vCpAiw1lCFSCt9jKYkPNlBYPLL2z0HSR +3fQbD2fcKefaMhniQ2gz3PGeNm9qU7g3JrMCgyTB1Nb3Ow3re4U1Bkyc23LmOyafN7vZg1BlsuWT +FazMvqoO34HT0ifs5oev9/0Px/79jqcrDvuS0a3pqeJ7mdfS/8mAf84bmQr6/69nY+3Tb97uQClT +yNaDzg5qOCTW6uHNqigbuqHOuQBHyIhkSEaUQ87wMhwAPkoL55ebY4F7F0cIysVQlVKrzYuQnV84 +i1YIUyTiMG8UTUUCNIDSgav0ZdeQ6E82W7AdI/d3cHkGk3dm4Do9ZA09eQnS9C6TbzyjjEeIHkeX +SGh6HQ0htk6QxpTDVbmN0QrIVAKwqSYzEMSfJfbmeLs3VkGZWHgffpSwROqK4a/7X/52pRZ+WjBf +UCkKqzIzRkNoHUhqAmRN56wHkCHiQ2u7aMPIW9Ee7zy8Wuw8GoyQlrAbF7JeO9XHq5TIZOns8t7x +45d1rb3dtBq4U2uDM6jdtD6ZfA+uk89kLXLxnJ2Q/o3u+rvf23x9Xm45M+Z6EOrGlgU7C4dyZEY5 +jXXggibintaOFFcGO1aoxuVQE8AgRHzw8JjW4ATdxwCmzNPg/t/5fz35934hZ2de73tieq7PdYuN +ZwCbF16xvBknuz09leUG2FPeOxe/Xvvs31AbXDpetsBsbD6z7ONw23KNxzdYurcahlsMZDEDMoUj +nn8390n71SIPKFezeQeujnw83uVO8KLfmIa9rhFu9htizborLJE6BslYInKSObpSdlZYz7A8LlQ1 +qxJge0gOIHnlD4poJ5aPCS/mzRk3WrVjXu/lZPGbrLc65r4lIT+1ob5T8rmfkCy2h3s/9dDSaRSV +D5j5zly3C/HDvxX0Ei9N5jUBWvbm3OGYBijNipZG/oE7eEJ9c01/QysQ4orEEFVVVCkKVanwjhSE +d1+eCXw8ptkvKS20iyWBA4rKmQR8cW/+vX5cF9+yNXEVKdDN6x/3+NvoN1EjITO5mMqh75JHfES3 +TrbwVhluAFRQQBFKC8eyIC0VgtsOiL3hty8DQdVXr+fxtAr6pE8ZURYUSgBz9h6Ov1QNb9roes9P +2+6coD9vzvxwyLjdZLHg44Z9Q8ow92Di43TScqp3nOfI8AXxZChoGlTr3osdwNroZmLxnuNpxf7v +r4UWrgk6Eni8qY+Mo5vevG4UyGTIptZOQje23i0ue6tu/4x4g8kg98fPit7vVx5NoBSeMJNGXjv2 +CLBtxOpEuDmg8XwkQktNtIsAsq2HftUU+X7VHeXzQkH2fNftB2OWgYXAYBXvycHlCBEiXsvHCATu +0R717rv3Px0/O3JCTzlF6sN0ZPBBtwccCDQCWDMqsOdnMUUqCpUTnvRuXRJX/g/1Hu+T75aZiuFd +hTZWWu5SbmHNmWrpXhLZVWBQkn8lG72o2jhL9VBDvpp/BS5IkUAVMqKXq5VLWDb2nPofcHackMpg +0aVVLye8QDxxlAtN24B8CEOOvCL3fxvce96RrWImokCrmMOXjRStfm6zUNPVNF/weL4fXrdEhVVQ +VieBpAG0gdQAZJR16hPbfofbNP/jn6xxHd+dGN/y3x7z2MfZ7DBRR/jZVXDS3IqjaGYqMoqISqlk +uUc3G7XOIVRSUEqKKvx+z+Hq8+vvD7R+wff27D2GtLZ+u7wXy2942QnpIeJJ9MrTkJYEpEA4ORiq +khlVGdHvXj3vKPmVqEuIeR6JSwAe7kA172FSpWa/0eCQwl7s5V5eh1ZVbbGclkZVknozYgVJJHXi +InnN2DuVsSPwSIJKcIx7dYiF8omLEwOheKHIoEGBD1gEUgAhiAWXA5nBxRwbo3/x7fh06m5w6wqF +P7B5rIqKrakYoBYDcgKvcYg4I8i1xB8gipE6ESoA69vAYQKBCcEeEUYufIgEkcwOzxizpUQfwvx2 +ut4ULQ00Gt6N1mbWmqFqp6zRSNIRDT5nydvCPDyKBajlXhe294M6r3E8f4KBVBTh0zbbBuaikFgp +KZrq2WNHD3b66eb1er9fvzpCIGu/V0ExdLKg9FSijbegaYXWi1mNMW3R2tmapBDPNmmKL2evaMIg +YxZcxk2szbfnc6qgsKzGGDMZv/Q/l93zw/5++h53cy9E9CcBZgAtActuuMCjdyyyn93sCU8bM/77 +eq7LCr6WuWnraJ/X7yu/iFeX2iaR/uMUo1zZAMJG1mLKy40QggD9lEUoBwVOeK5i4HcrSmb3mW8s +y26qShKCfdYbJrbBubRSKDDJpNZhaHwfq9RthgYMGI5ds/N5gf9nm4j7P69XUt9dSafYMCOnh780 +N6/x0aIV+H6++HvDcHz+/Qz5jXp1VuphcsKrWJY2nKryTzQvIxKKxNPjyWE8F4ONu6fk70oWgKGg +vIvfe8pSkoaCimfO1qvPF0+TQlEQCRnVznlSz0AJJCISgJTv8NQhFe4iSu918wk0cNf7Aisj3XfC +E8AQSCQUJJCKqEofmryogYPKaFur6pv4jX218CHdbZ61/j392fw7tHxW+jT6eIUAZfmavfv1Mykq +J4lw1r9zrCmpcyRlfZhKZouQ9bMMsOc9kqVmnUA+Kr4m5RPSCA0CgqyhVDMrAMCUYBUuUSJ4GUGm +EiZFZ5Lc50bjJo02JdbQGYE/l/w/7FUT/m/o/durCtmbpsp3iKns7ZeqjVgtqdowQms2YEGDN080 +kQ3T6frXv5+ozi+3j835vuvDWO7JKpa+o+HOSXCL8A4qqEh5/blB7I2ZNb0Uwa/y8vZR+Z/XAz31 ++suYDdqv9HJvpBgxgwYPDBzlRxBbodqdtzT2moCSP0wvHRWPz5+ZJuqfAZVkerBS5izcHrQ9S/pK +lzoaHsLcN64xkEphHM9nqgxBfiThTOfRzBvEsBlW9pdA/j8bsMWvXHEC6JirNFrRAdL+7vhFkMRh +vu9iJ38/a7ml9u7m6h1dUJB6ifhWFICA4vNgw7VCIB2XxurGbs2/udJZr8t3WoY9RDyZCPUgZCEg +CzLrI/L954d3AivF55a2E8mHs6K+Htk1S7tdEstXRmvAZ4uew6el2IdA4SSfWYQh7m/N1SJ79YkM +gxAp6G5oV8RJO+CRaNfBePU7bFK+bjRzTvx908Khekk0eHVho/Qk932YdF+mLn3TdgGAokSqE1IP +ogEp/4P5ggyeREnPz9mC63VC2MLZ0Xhk3hc03w2r9zPDlHD9adstvRLryX5hQ35D2Dazz2XMplMr +oomPn2zb+svqnPuVD1tX8UzIUxLj6HO5q+Hfc3sT3IGKIShKAEWqAoyilE0VrXRTKL0kzxZZOVR0 +Yppi9hI0QVVHzJsL32Je06aA+iIm+MzMcxWgqS+K9LfmpGO1FbPysKkn8C5YX9IxalEo8cfVe0dQ +q7ejlF9eZu1/tTDR+vagcPCoCMjwzZrOirmr+dOFPo0lSLAQrKAyqzCUjkm2Vbc66Qq5e7lFJARi +mZSuMDE+f+XT28fs+4T+uqHrn5ELDpOE7fxGnoa6wmmtSqbXH9wPwfY6HhNs8wHSektLbzCDwOD8 +P559xPqh9EH2fhT6f38ZJ+zn18/f3+DUG+a5jhZi4NtMOeE0rTSFKxDRqMMwKTNkcn9f49PLk+9/ +UP2of2jRs3CtIYiJJDlJ4YgcSgUojKqOIClFKIDL4T1azlwSSg499lVEIUtu4O5nPVtdtx/XwPRQ +9CUJT5IpQliiEird40+PF/Vo0QksACu7p3pSoUKRSCwCzs3buxo6oJOJAzxkzxKhzaQMywl/xx/x +7z2ctZ1ny07mH+27VIslIyACUoQlwIHHF41jGMik1F0b04tdQtDfP55NNNk6t3ZY+rtp6a8+TDuq +y6nXhTLnA0AI+VJ5EROUPACIwiLdwfaII5c89AkCaiiAdtM+/98/Z4/tb/RkU6pZP1wxJscOpQpf +X6frWH8dYf2ZbIIaJ1Yhj6sGDEE0dY8Pl8cgUYHjoNebpDZIhRbRHz9Sp9neoT/8en/1e+l4njHY +P9nvHozoDvXi3VQv+YAr4duXCmT/g/wFa7VNCAgjEH/5rXsH/27+3UHoCCmP/cVBxIAyVM1/Zt/L +7w3k1bhR/hZv7qkPF0hZ2q5e98JUc0TOdl7FYiEhNRp1uv9f7+/+w21nY5tD/KZdKUtgx9o/4eNO +PIs71WWRAiMQD5n+YsXep7PJg/I9kADEAEpeTyVGIDumej/scmSwvq9PMOpswhkF+aZpQmtRqnx8 +Z0R5Lsl/L/f+Ja7+fzm1PGSj3qCkiAjRVfyLkcMIys0giWcAh0jK7WnTCvX8LrYSbRrme7/k2z/1 +qe07uPI83dh1qVSkVp2SdIDFEBN4B9v3Q8jYf9OD1+2uf8unfbagR3ZMjHNqq5n1/woieJGYygBy +ADMQAgaqwZMT4tUJI/P71ku94UZ8ltQqSR6oBe4KOd1iACvzIm9r11//OE16O6/V8ttHTkpH84v3 +tyWMQETs9FAr4f4tOX/Pu5seL+414f734qIw/JsFmcKCJB+xxzP2dKjHALGDMzNN/E7BS5Vhug4E +h2wqWJKEpE5orHC/YoAr7MU/VCRESQaqvGIJ0gjlhh5evZonAygClcAVadt93l2wA+B/vwA1UBQI +3hwRGpBDH7ICKnhuiURpKQQIJIozhzABl8Q5w6OqYWXpEQEIR7kUVz46UiuWfT5xnCO+P/MVSJI5 +CQ3cGSXq8PqY+eCbD3ORPDz68Ri+91fT3bXJgybeykJZRexCTpl9KZEFF2SWE0u5anvm8AtWfRuA +7/2PAgYwQSLXgI2LQw2GscP9hqM6iodK3AEJgwcMecf6KHh3S95RASJkbSlMvASJr6PbzmSNRyG7 +ykCOk5g2g+kX+m2LEow4P9XOB4e/ppHtws1DreiLHSEWYz4zp/rUB/t6xqE0J5URJ7jfwcRyckAQ +TAqSEQRAqUBQdQIAUABQdcWQYhx5lxZ/1Fz+stW3zYVwX678XT4Wtu9v58+JWl/vusZMYKIInR8P +Tyzjw8JP1G39YhISfm/p58bU8+j/VWIunjLOON6YZqjor5rvL4/fNnoGgedbsXTuR1Q5vCQInniD +wiElPw5BQkCL5TDNImwB3yRFNENvWnPtQILUqC4VpIQPgqJoK51RFPiCFKAMR29u6WpETTdce3lX +3NdHgEQCjXhSbpybF3/lad2PxnHbzcr+aKotlUR7s4N9w6ZVTbrwjxk7/qofSIdfoPb390yeEPoS +zCcTZh0Qh4K4bhgpgHADA55cqFPju64pghqFRBwLgvyxRPiAYLUyo5uxqteLV7yC1BQxFOz89IJ6 +xyccmU5AxQlETVQ+WUXx/WsNk+6nGZcGKQIgv26kmMZTGyjgQiWRgAC8UGIicMPeNPoCklUYCwUq +Q2fN+vP9vKn9vb4AqKOSerIHBykIFI4ER5QocFGrsvX4QkUAziQyqqPBGiA6kR+0MJ9ejx7gOnRw +yj0kzp3AgJRRgYBIiQQJGbz6cWToh3w116Zdd10hGl8ei5kmImbxDS0kbsA+fQAp7lq/6mKE5wwp +E/jIj2/mfjp6z4BOAQp1IE/n2wEpggpZ1/m6E4nxyybIRQKk/wIXenx/b22T5h/draFPkf/I48r5 +I0fQAL8gRBSnET7Nxfr7UYqMpcCh1Zh58cGlLNDLjxfUjBANBARB/p2diT3+fmxxgJXzbKM/eLSN +vb29YsFU8vCxfEfwBv6s7AU1BOK+831lexACkUeqsOb4krsROF5UXhzhQBNNlYZv7x3wjwB1SYQN +AE0V6ZMYjCIoSQlA9AwlDX6/KQUxf+7/a/xZQAEpKdZQBV3FkQIwIqKJiwxba4ePn5DJ5+8NUZ5w +pA7T+ZBKnflsockSiogKxJCIqKVKhVnVch744M8KBevgOdghw8Os8JIryp60b/7aJw6uSoSTEIe9 +6n/o8s29EaAbsyt4LWJpwGWPJ72MuuotGwobpF3bZmia19Ojs8lbjnWFOWsVBpFoQQoUaBQoAGgV +KRUKAApWkoBEpAApQSkUKaEKEoBaEGhALz47rtrLbDWG63FpgygugaGhAKaFKUDrrdzKlu2w3CR1 +rRBucO2cJ3cE8AMZ7azRjSa2lbJ3BJW3I0WHjnOcZCrbDbFG2VmLMVJaqnGt/HBTNiyuSAntzfLU +SiNRSjK6sFCSdUrIoaQuUuqWcuddXjj4dNz9PV+t+TP9Pw9fTxZ3U+OG1Tr6v7bE4g8+Us5vlktL +apjcuQpwnXSap6TQmtNETtgrSP2oF8P6H/o5de2/0Rh7AJ26Q8rqYN7oWuC2qbupHRFNIf3fDPhk +/9T9prDhOSRQuULlg1LRFVKan1b6fH5ed+eKe8OcQNX2yQbUr6PP24F7dyKYtxa4YTMzLhmJXLW2 +Ath6bsmtGjWD8/yD08fJCsHo/px5dD2deU5obtZURpzaOUx8taMBQrDWWTFYVKqBaVZFm31f2/hh +7uv1h8v9RQp+188T8YNxphp/W239X9gj/XdHJFP/V/thX0V22/mpzFBhJIv0syExAIN/99WygaLt +8mWroF/5us6qefYOV/m3uTmL0AHdypCoboRZFrA/X5P8TGF+71ZPbeNvm2+Y5TY89Gj/rtoPhLkB +SsCAOEHEoh5agkDw93AWtz2USzSBmVOWhS4MxMW7cTZhq4xgwbcnFjsPQoOlUZkDc4UJ0BjCrGsv +WIdzdIkh2QnPIPVBI9s8TKydGRLnOdZ0JShTfQJVO5zazZEYMYH6bbQwozGZhc2jhTJbVGM2pLA5 +gfMj/vXt93m38WeZEXAIE7Fv4luXE52LXWNTx64v9BwlERQgICBOYKNqIAfltFDVf9tRhZZ948a3 +usPiK+8+hT6JTQoRSJyncWgGVI6r170j58dehZU1EE7IgP2QgYoCLEVMHKTavFEs0dnMcUr1OK0H +t2Wtvsk9HJtu4zzhgfONej8m+mx/v+PgjG60ObyxsfuyRbMQ9AcJB9aZswfUM3rg/yu2ZREEGDLc +Kruo35C64FLndnhqiS/Imt9Lw6L+3qwyguvjsFdSoXy+8SfhREQb/VQnh4+xsAVZ0UMEAfOYnNNR +jDDcWsqVClNk9/7f393m907/L3nQRUi3II1AjG4SIA4OkToE+HhLt3aNcQ5PXNgGBAcKgD07+fuQ +/MkGSoT8uTC+fsGRaeH59b4DucfFl04pKSuu/bno48M5nudrevTkMguekhJ4lCl97vPe9hfOaJZ7 +9tRMsuatDVmqbOh+u/j3Hb7P2PIh1OYWPW3uoUOzIVp1Ns3sNDLTC5My7aNLp3LNmZoUobgrB1om +FjvLgmCZL3jEVeHnFceEahj3319Tz1jNEcZw8Js4l3YnBJxxxWjRGsP4eeznZwIiDotV/yN2FR2p +g/x/fvz7o89nQZvB2AIGIGhUHcVlUXurh++bjCz7EEAaSoBBMQgGwdyIogOogL4KBsmcAoEjluAU +hQFIihN8vwQyyijz3O5Yx93nJGJCMfAZrOx6JiKRT9nXI8hCid52lBOEt74/l1Tk3kkHMoCc4IDF +ACREoVOk0vVB2jORFgcNQwlEv+lWAAeOv7DusDY4YvMFQVr0XsJ2vx3DwyZWGIFjgAw8PZWRLAJi +MZG29YweIKG7F0XUYuNsNT2OND/lfvwciwNIAF12IaESq4wMgktv6XSdo87qHddIGQgDFtk1SRPv +DJedz3xmB9zJksvYtmmnGO2WXLtwVO8jfEWttY/lYnqh8TQPGjkmlbGoiAe8wZ0gyhzQeggONScx +xp7he7wDtu+ph4jxn9ZZSPIgDyJAPYB5FQAwBAcQgi51F/4HxhDDOcYv3caQ/RjJjcJG/xFwoFDQ +AEiCgEYsIO/wr7Jp1GpFHaPPZqeY1xImML6j+JbK1pSi2spmKqbfl+PGsEPO+Z8cJstCaa4hrF+9 ++dlh+fDCsIvEXgY/avwaim9xdkYczlQQKMjxlDBMxGQAhGIgYQvsiALxc3VhwVWx12ZcGXAA8hUI +Hb2Ix5wl8gPpI8fygWTDIzGZJAi1Vfga9ejEHDzq5kHUqHn2cuNGnrzh4elGJHB6T6f6c7N99jmC +xAJdgwEVBwhcCUyo/yRK0x4Fc+xESwgSjDIAmbKL0I0h1j3PgdV/JIM9QeLZ9sqBwZnBpQc6GYKi +4hB1A6MYwy5BUHL3oQBvYUoALwfzS3kgBKLRNMHCOLecw86UCJNofGKvCNFAh4U9dYtQRSeywAcI +ACHVEHp5X7yBwTNICI/gamAeByurHTpzpBHTC1HtthfI8JQpPpPl4Gjw2fD3SfGaovlydw/HPOB9 +UBZR0SBvnC4ZlTxH/1M0mTjycXAdMYx/eZMCZgQTels55naEUcRlGFfDm7lE2T4zSHyfFNpHCUTR +gI+VZusqu8LJNFf8nSzAqKAylTVbHlYqh2xT7JEgH5wc9tXBIDN9szRRjQyKjKg1MQQcyChzuaFF +Y1CSv5yvF81SREkgkkkFu73lFgmy9Mt+wXCdbwwMgRA4Akkk9xUYMSsIAKHKyI4hQUREQPJLyB3x +hTJpuvadjVZ0HOvFQD/KqC5AoXA5UnHDG/NJPulCTpYH0IBoKAYQYnYUxJUhuicGKAkAxy0k5amq +BSbqcxbn58LH9+w4oE6iqAIKJ5ueGn07+yxAIgBu3cBv0PFtnjz/2rAgnmL6lv6Q+Y6mEiTp6f+D +7afR+f4/4PIP93mCJRAqSHVAqVEaxBISpUIZBt59PDv+Tu+vp/bp+v6U1x9fLXyngSoEBSFQgWKA +jN54BiI446pqqiIIKBA+UGJv9f69L36P5/tmuvHWMRjOlra2qAoxh7LM++w1loxP3gvKxEUTH3zb +vM0OcATAigt0lmCqxMsnOen8f9/fh/g+bpk4IPB1LPIZ0Otuin/W/TPmJ6rzOZ/D86i9c2gdUhzL +6g6DyN5lVePC5L5WC7lUyFyPYnEQQRgqHA0tIVEUKXUyVixiQXMCYpCOwcvR+We/Nvj3XPnM/2l6 +dk37MaWQGJLXGWRTJAGGdPOef7hP3/H+X4/t/6fd5g+2f+d6GoJ9JgGSkn79AakZZSKh/KcY2Z8O +45No/F5f2xxMXlRg9DtwkXJtwY6OAeRzwmhA/hkOyZ8PMedgOnpUiCiSYgICHyeS8i8MvTpEi4Ng +aaEpwHdVRRi48LkUOyH9Dz17hdC5IQnB/l5LBv52iLgmG97snftdHrjqB0IWhCBCkeNY364PdLgA +HwVggwgSGNKIbmAG5AKReTXrne1XM/E2MiKb9eXPogVUXvshjgjDwsXUl4fpvQlO4MWlHqhFhnQ6 +U1UfPpDglOJRKFiKApFIeSNgwPdwZ9cQvu66fcGyBZKwYhGQMFQlAGIENe7p6M2na07DymRx507j +gNY8LO3x6u6P04d7ff8EyHj8kKuPv+qxJ1YuJLiWhLFqYJcHje+hmhdrKnUKjaHxE6sOoJsv7P19 +OjQfu/ahiUI+djEX8U9yIOpg9TPSgcQFz91fcNdSCr9qvW+0hLbR/ZoeAUCT+e0WAJeJKCxZIehK +rFAWe62ABhSns/L4OHrZU/G+OueZHpDaDsZa0CLhiAIgicTs7DpMQU0hBFKRBLAHQXPAT3NHCmUF +/ysONAgQmrULEy0RUIsTLUgebz+/tp/zfFnKHPGxEBQikBfdrMDMJ0Nr9eX9P+OD8flkobIcjpYB +nLJkMCYxriLMla2yaDbZhdEjXh4qHHKecCfYogQBxREciDl8+Yoxep01T8HRTxZb2/y+R4dnY3hy +py3COL08fl0P1CE4l1MEhXE/pUCaJWga/ZAYQ+uVyGqBo2euLtikQ5fK1DlPLeEOGRSBsN80/h8S +JT1u05Jun9+A7sO56WyCoyIgcU4waspa1isHoXcYPtxgqKKU4+Mnp58cDolYQ+MaikpE5+MJy0FV +EwpJESQFgBFCbFofn/jo+Kp/L6/DPq+S/j+72+P5xl6j0+z8zY8e3Ef6QGEiFFJkGJUAdJc/3sEp +WkPo7ODrPfmnoddicEUTqhvyYgc5AuWiFGgNNhJMz1rE2AjWXJRIfMIf4Oye+gunsb+z7C10PAQa +EOFP8YR7nZCWDEermJuBpQYKZaNCnhUTRMFQ+jwU5Z6EZH3+fo34cPUHQqPcHwGFXi8QT+MBx58M +MysooVKNAUT49jp23AeZsMkOgRNN1MTA52hnIzuQ2ca5Lmp3UxC7Abk3HJwMjQBwaEzubHRbQw2n +fogcC2uDpqg7JAfEfMJQjMNMwHxsBpdSjiDlQ43SGjm6Rp2WCccwoNIUjXy7HAnjoPYKgmkSmahi +CgiSJic3ylA86SSV00FFeg/7IFLquXLqGzw5BvjArLxoi+L9MXMowfqXu/flfQaEIjEcyphIYAoT +PAQDPUKAHoRm+4vOt6SeE1Q/j29HN2xnoUF1MYi25H2AMEjqVAAo3+vgmHQ9IEDtlVgbNQhf8rzx +Uw9zg7J8++8Zh1xzZBPIcdLd6LBCL1Prn5/rX+Ysmu/c6+8XIH+uf6+riz2vyD+/kQ5iIh8EokRg +SH7pQaAHESEPkQUIZASCP5yDuX7/8eB58EzvYpQmycZSokNPPTjfxNaOgAJ+MgoSNKQlKEwJyKoR +XmxBsabGcKURURk05+DEOEEH+UAUK+90xy5UTEXLGYEhhO0wwDumAR3giRoMQ1+GiBhzCU+lzy5n +AGQ2fk6/P8e3QYu8gV+B6+uQX+0ofL0mHQD5/XgGg/oMHqbJTCCnFbMsGjtIbeA2SMrAERDKGlh9 +sFAujWe26bzYXKYI3nCaj3QuxeIHkYFqGXT1znIDIyoDUOskHqwvO+I3vkuGoTkdy4CEFBDMUdqq +UMhpaWV7TgAyGScJfmXMoRwIGB7ANBgAcwomw+d2RNBSkku+7xrgTAD4/Tzop73MxBqKHDwN4JVP +QJEJlANnDKUPVQdIjG9HGDhwSHHIQYDzKyFH4SHCQBC2BSVihksnI6hiFClrvgmAMHJyHExCERS0 +0MkxJCFgY9cjOIznz0SFxsxIegdTqBj2zADdClBREIgJQToCJdx3HuF7GvH5aI8M3h9515TqsViA +cMGNM+tuO2AUeA+9XjsnTR2rfNIoVByvIqBIgQBwKBFIRAjXzen2f4f7/HP1+7qwVFKdxhpPCFEy +H8JFBA4FUKEisgIoIQAWUUVGykP3czsnp4aprw7viNBLFvw1dIpI8Btr24Qvl14gB77Rot7qtsSQ +0aAzFBrKIjQfe36Zv9n+z9X3H2fL7N93IQ8pADyRSQ8kRgOIKaJlGhcg2LEO2NAfxM/zwnHuNHSu +kKvcgH5k8RSAEogAJCEogApIRSAg8NCeeYbMHLv0oB2S9m3qbS6BGcw5rYrSF1eB69+nt1X+jXH5 +lkxf26qqhe8Z0cjhqIJRCimiIgiG9kXr98b21ZnGof3/I8f122D8ng8jUDfD6jANuzDr/D1nq5dO +VCCgksURH2584dhX8oAHwEKYopWElANwP+UkPQJPXQnn+z8dv258dctuDoB3ZZICe1JV1HkRVNFU +RDQuLaSE0QeUe6B98j+kvPV65pOshFQUkQJEfEAH0qPfX04o42ChapHCEwkQtA87LDZpf3/H8X+R +08kwD2H3fn32bf8/x/Pj754e3mB2Tx08rbYWRETzFrtdYL/N1q8/L9P32+fabovR6Qy283HBYIEl +GAZY3BZc/I5+vW+6u5jnGZkWF1rRSl0OYd5x3eBvPFBpE/DGLg3vsjSjo3PA+eJTM9knjZM+u93O +KRHzpetKaOl4ntiigKo6DFzaXpMPFdwcTlbHLjtPG40OpBeFIThVAnu8nvQKL5UCQz0nI8VZC9HE +6u7zGeVC4ld4M9weYxzNW1bXeajnKLoDdP8FR4a3krONHi3pBpnnVm2KzajyXruOu8hPchXXmzm/ +P6n89ttXRahjZx+O+aolYxict6OYgjKjZSYEsGkkyMGaOnBvYbjrwWb55b6NMl3glbSFAT4jbeXb +b5oG5sOSMcTlXWJzRIGCBlk94WnqpHzOKdJ10pk6UMenzPxuCD+93N9/i7HfkuEAdKqBKBKghaZi +3uI4D289GlKKbeHqT7UFcF24ICT0H2RMB6Hg+jWQ2n5mDjD3HcU/4YHXnnPLTHbTYMEOyUJvlNG5 +pniEKUpXjnng0q8vaOhpwg6c7gHw8w7cL24dIaHF4JtY9DiDQKCEJ+APXlKGIL8yPYnoeyDVbjlO +YTiaE0ml1OsEFUgvvF71x3LwNDRJTWFxj4GsCGiCA3rM24Omep2rYoG9HCip9JQ0nIb2KCqPiTwB +nocPuEExgQeTDhyShSFeUUk2mlXTUjryW62oMtpjJUSUkxElVsMN4651u3xpxeDOXCA56uDidJQ4 +4O0KvBB0kmGwTnjFgEMIaCIKEKAcEweblLkt8QFEh5k0UhSh4uj5M/QkODii8Y9GMm2G+t4c2STU +2KU2pcFBKKUwzn6Xx+j0OR+6MexzYUvbRcyMIXE5dufJ50J3lPZXtfU+oePnlXRnExusLwqfHe9v +acCTvdtdVsLhdlcnEcRynFRXJ9oTZqA6cajdgibaTqJ20aqhYiArrAuJSwtyHIpore+23XHGQWZx +p4xk+8EfU8v176ffakL6+08+E+pTau4sx2atZFRxmxF4eCe4hs1zMSURxQKCUQhh2Cwnd6x/z+r9 +yitAPLKFnQjwVOweoKVqTqIb579kjrl0B0I0TRwPUVHQQgAmJCEoSwQknZ8/lZAfVKqGn74X9ukQ +DiQEWIEEheCH6w0hw/u+Xb9scLvp1gD9WHI7SdpEmQxy/vp3UrLJVKTFClD58mFTQh1/ZKCHUsMR +RA4EISiAEogCRfgyv8s4ov3x28zkFo0swD/hA/CA4aDjIEQiCoioUC7VTHzedQ1ODt4P7ilI5yEf +Dj4TdD4rH237pOhzy8/Wnvz6ZFKVNDIQXIFCLoved+i2ju9MqpZ0a5wAxAgMaeZHOCG5Lf9B/x/p +03/h3F56drQ/CpD5paRQPjAOShkiIpQIE6FAJAZgueLnEavDsaLivzxOHz46WHj+EKP+fx9Lp80w +PUSmQ+xCNSFC7UNyuUPzwDyyDBIlCxERTSlLQURLVJElCNLQkkLEAUUrQFLyvk2MwnVCOmzWMMtJ +ex6/2eb8LeOAOUtkqqxFBFQqJmCWJIpmlM/TIhq0tNBgrD+GsQ0TahPDjGJrZKPy982lNeMdUEwY +qydXC+umeT79nxVx3humsrakVTqBDptzOD/RaPGvMQdsYLMGbNlGa9o58iTwUFIDCFJRgjEBNpBA +sApWekme8kONjfiibaC7amkm9NomYIhmIKoQoxFYIDBBF110Sf242D7j/Vz2dSGQJxzLYQGS2SFY +QyYEa0uozMD/QGmKd+CuO/1eR0Q4QpQFjrxxNbxEg4zGfKHknWeexNkhQlI0qUKFCUFJJMJEtVEI +RQwwUK1SBTTEjVAEEpSFFKlCIePlxkMAkIB4jY/No4G4BPvTEXvBSSujkhD8KigpxEDzgLuYOQj1 +SRekggUi9+ecx1BIZlBJxmOk5k8UD4JR9akNHAd5kJn3ivptrj8/N6H3p4hc7xEmrtdHnyZUTxtN +trFg0B54IPqSgfgEjSJgPggT5IAIgDC8YwogfOnSbkTuSZoOogvUnxkaNdMQCE3UilRMRFEAFKRL +DUU1ESTRTIUCzQUUUUMFMg5PovikPAvIaECgvtBrBgwxrCfac0NERNQfO3zg9DpJ2pCMSJIy61hI +EB5er1+j7f3fVn0/V8v+pW2bJA8Hw918/DKG9TJV6BnkxqqzTzPSZ/h3q4V4MyEU+jj4pwmbz4Qb +b6mKKKfh/Y1pAqqaCQNSBCgF3p38p9/8W92F/zXOftysUQXXbGQOIQIBwqFX4R7IpD9rmUh/sen9 +dIHWBDgR98793lUPEBsBykh2A/tPUVExVRM0MxNEnbB0m05nKB1CFJwJoOaIaNg7hdpoKOfTw7R8 +d3WCM5T2k+ZQ70HUPfPvA7nGxqpHpAg7HoECf3fTTBMEyVSFItFRfynog8h+n1fd2TmhOrRNAHQK +DTh3aiUlRNwGgZmh9U6L03m2/f3D57NGb89lAs0jh6W9e+2NP/3/+FHj+6/j7++OjLjxj73a/XBh +L+GWmFH9U4ZbMqe/lCfnx9V933D7Y0CwlQF1+/8c/XF7eH7z9cmCF92rleu/cOyQbP+z9/zo/35Z +R/J2TiDeTSf1J7eDifd2W/s/9r+//vO++m2sOvf8J6vYffmPXrGgZOo5fuZpd1PhH9f4f/foH6lG +4hKzi5Qm8SE9sQGX+4m8/OX4wIIkl/fmKStCXqXHHu/i9KJNH+dK/5VwovLIgwhPbK/iJYvqQf/8 +5ujNOMwrITAiDt6x0c8vdunm/APF9v8+yv/wZGgZfifVri1R7Mt1U6C5EYoMxwkKxHOV/yYohcHF +1i3o/A/sz/2K7h/b325KxYCQIwB06w4OA8w8G0q/2j3Hn+vf33D0n2w5HizJXbjSm48Z/cf9t83e +ePh9Jgb/7Pzenffx8zF8opaFpfOF+aHUh77jX5iSHktA/GMp7D5zWiZDtpPE2KWgTj3xFSUAOZ9l +wxIQMA+wMQ5fINXSJkkldY566+rS9IOYiV5kA9Cx0Ah2EdpEWCggdwmn58f96AsAL0wQkCR2+iv3 +n/mE+6NnU2ixEtBtIAcMMtVXny7rB3i0f0ZpLMrfwLz8/dLInqwf7UiSFzcwgL/8CGdsZz+Qc5P7 +lHlI0QZCAhMhQBingCHhvnbgMe4f3qpBABwZH8P2cvPhChwT28c8Pht0DdFHADww/7e0UE7ZR8Qi +jCSnIjfjLV/SBuOMQ/OePvMDiZLtNSXMQH94x6yRC+SxpaJekeOsgPQFPKeBgTpaCIGqiCKaIqig +ChiChpaQ6k/bAalTUBwjXcwQR3Aaieb5I76uSVRx9s0HEo0qekPhuopCISIIJaADIdPxEw4aZJD/ +zYU0heoyQoZYbsm56POD4UP73r9vD8rmlrDKaiwU0AN9wEN+6m2R8bBQoJ81UhSpQo/D9mwfi62L +xMVMqVjap3i6pDQ6GqPerv5Hj4xfCv7Jb7cWp4wyE2pcwwm7MQxBdK1ir6kbxvg7FNk9wTcMNqbN +ZTLy1hhdZDIlTBcy8aCJNyVYofu+jBO0tITULtniQiHm0OgHHzCwENFkDqivDygX44X9+2zc4eFg +yHLsldEJ0qqJApTlUCNrRD9+wkQxBKC0DSrTKUoU0pSFKMSpJLSELIhQ0UBFQMktUCUUxNK0EStA +FI0RAUJEsyUEMIkQoFKkwpfxZDQFKFAUFII0gRFBVBSJ7KIepdf2SaFKYilfGWkNKeRQgZJK9hxB +SNKlNGUdFuTn4fap74IL9kcOe4eBvkG4fp1n9vLT0n1ITy05CDkv40qiOmH4j+po1xTNU2Q9ef+T +niE8E60BQWPWHjtgoeL4KS9PO3TFFBwUsqlJj2FvZlTGE+SPq/v5HMgouwylsaCSbJKLcxblMllU +C6cmHP4/PyXpuQ8OUs9bWJ/Xpg5ehVacAhWUpUOJBYBqn5OyueTd0x9AtpCImhEaJAk4XAwSL3ng +E8qrmtREnUSA2kX8Bwe1L0r92nfglERJ9pR4lcPvi5vCMXhKFCkEEqoEk+YKZjqeodCp1ovhB9UA +seP2ZtPL+oY6OQx7K4ddEQ1ODpXSGbj7vQdXrPp/vgciAPSRQKEKRPL7j8PJOUF4AzxTMA+kExBQ +6yolVcIcaGVP6SJ6A0xEAQYqMQBAQByQCRKCQiHsH1UBnpHfL8TZNnrnARea/4dWmgB5vGwV8Wky +eqhZ0T6fiKTDtl7RM95ECHi89tzkURqLGdRQCG6FAhJqjZoLfUcDU7vZRoKguOa86NyisLcRQfR4 +x6YucBPb498iBBuAF8EaQFtQOE4UVzNVOidffv185z3T5u4nX5KeIQxz6nzFYkR/KU7cbEgtOOj/ +AJDqTEDBA7JfZ8+B4IyMr1/2gwTki7GAeNp/Pg6ABlBBEyBUExFDU1FR+lZlICkB+ks/wk4AhJ3W +8IXdCCgiBiEMAoSMpb2aABs97PLy49kmbMuXGqQxcMXqH9QuTtKAYixHuqE8QTt9No4YnX/H/H0+ +z4dB62ElpQFhL6MDIpKaT6Q9Zgg6CEp+yBUiVuy2jXZzUYPlwwu5pfnO/ZYecXf6kf0PAxmGEKAa +RhQHoFkP2EoGEj4kolCJQOSriCwklEGItZPmwoEf9/rP6fR9Hb1/ruft/FZgPSaPIffz/pyDHeB+ +pJxzS4abN8mXsyWmPtZ1ASgoFmiQJtDJPEwDkgK41ATh67Iw/Vps8YRJ8Q+A0zY4/IbCAdCpQwUL +s8w69U8RCTB60NgoHd1O3F9gkBCKVPl+33eozCByIHwL886X1XbWt2FiRuYXMyuOOCawmjbm12Wy +Gi3YcQZJwGbHVxO6dz8NswpS2Vhdtj+EFgoMFCHjxrcifZ16seiOwPbnpYL1RuOhhoGDSHtmahQK +FoiUokhaApGAAySIUQQT5vACnR03sLz0wnFuPq5bNtAy0YXzTSyHqNbl/KPjCuxeN6vEANoIbjVQ +gAXhEMYI/O7k+HDLQP7TXwR1ghIwgyIAKrE8L+CSGz8v4l9/o+PL7dg5cvHnS4D8i6QEjKJWnWUQ +dbdtQvZb9q75Rv45M4zUL166evzQJ5TU8djh/PRgtAR/G7+Ov4gkkM+voD9utR0xMREBN6+uXrJ6 ++t/ZFhBI9x2jCLRnFKXXAXbRvvW9PTmwEEhzOYuk6Qo8IfiPP6aVrUYflaqYgVgsOaVmIKQSiIAS +EcQikfl7rjfO8SiaHRfleT3YB6NvMDkP4Icv2UN8PM+dg+WOWg1FN7gwyJNOJw7QQiUCw9OVFgg/ +s/d9O6/Z8/PI5Kg9AhSCTCIkjMPv79tBu4sIDSeF5y7vDme0ePTGJ86pQKgT4W82OwkMf1HRcoEj +KBOjGopaR+jjXKbKFSbeV4yU7JMzbJhRMBcXEBXMwYoVZk2i55zzbsju5I7CAUmCyTMwoOxXVLaN +v5XweevQGSQU7IQsoRSApKxZDH/D/0KLNvDDz+3RurQXAG0kpQtpY60F0jlezbuzx/mLe9wTJmeX +gtEnj/c/v/nj78bltEuGERQJ1cYwWQxDHEy2C/Q9Z3B/CAHd37XLc+5Lzt2/6sw3dkzXxm6UeEMq +Mhcl2ujK11lwtpMmYuFC0zLnGwHL6fupN5A4QnPcGNwBcA62pgkUQUBABcY0d/BEmpUCdlc/L3fm +sXObteUVU2iTVn8YhxIyerchK8FAjYFApcJtXeNrxBBUNhRN/v9b9s4xjIqJuCf586SsBY+9kjf2 +dFsTu29se59fYNrAf8PQFvmIxgUqEiIoIHqR+vDVFfaLJFyOqpUfeBgH9mq0WRpejZm/XQybjODe +KCwoESSVdhH8GIlBVuYeHR2NJN2W/4+L2Hr75BlveP+f7iqiEHVTidtA5uPxLKRxmzD9kJNzw79D +V8IcJRGCARqQAHOOuppZMbd8/v34cQ06/7IQR7AW7gGaP3fgJtJSQkJIIOOcDQcZiQEISAQCCE/T +hvVYZXz9Pur54vfu/vKOk/gBlez6AnsX/Sftgdy1/y6/h6F8aYSIYqRoCBgP+WUEYgttINKDQkFn +gz9fn/7fP95cKdYmgKaoCiiETqAwFLQF/wDOIoSokoaSgIqKSqWmikJqQKClKCYCCKaWgpQoUoWl +aICqBoFkYJapYJFKaRIhBmBCCUZIEpUKBiVSWpaEqKkpiliCiRUSkaFaBWIRiKUhqgipmqAoiIio +qpqGaUFpAIkaCkRKAaRppFIgQpUSkAoaAoWkIgBJpqAaGIomZmipkmiiCiJmEqopIqgmoggqZCZK +aiYplokqGkpFWkEoAoRSkaARmEQoQE/GUANKiRFKDBE1E1FAtAFUUQQwSFBEE0TUQUU00wTTQykM +zESQwQRVFUVQQRUETDEwxTFME0TNERQxFERM0MRSlRVBUUzQzQSRSUEyKtKoNCCUgAU0CUIJEKo0 +IjElLSCUoiRIpSiFIUCtIRUTUUFVEUTVUkoTJFQUlN+OXVNFEEswhBUVUQRFBRRRUVAwylSVUpEE +EjARRM1UxUFFTVQRU1QQURFFBFFJJRFETU1NJE1NLRTJTETTVRE0SkRE0UNBVNREUFQVERUTKUQz +EwUQ0RQw0UUFQxRUSFEE0NVRREQRMiAVS0BEiKlAKNIghSCCBSohEgUrNFRUTJVRS1ERE1UFTUQV +EBUVRTDBRSBEIRMEskJRFBTARUrTMkxRDMSMVNFLFUVKTFQRENVLRBVLVNBQUMkUrClS0RMVMQUk +0VVNUshTVERBMwRTNNJFQVFRFJRNMRVBFJBDVNEEVNRUM0RJBVTBMFAVVRRJUwFEVBDAUFTDSxTM +0tUUkQUVEE0QTRTNIlKDMERIVTEyoUUkwRITDStINFLErSk1QTRTVTEBEwS00FRVRBEyQRRIRJEV +TMBSFUFEVBTUoUVBERBNRETVCVRQQQEsM0VVRVBTREUTKAKlEVARApQkQgJBCfuhVFTSIChCgQlC +BQCVQRJAxRFRFUUSTVRFEUJQRNFNRVVTFREkTUREQkLK0FCRKTUSSBQBTTBEUUSUDAwFNMSQQ0zR +JU0NEkRQFBVBNJTSNI0BJIJFUUwRBLUMyVUlNLSMxVUsS0FNCzEUEBUCREQMEQVQ0tMVFBQRMFCJ +QkEkTCJKJBRUVDEwxQEEzJBBQTEQRMVEQTQURUsRRERFERTUFLEFVSRNUxUlU1QTLTVBVNJUpEJV +SUlFJBMVVM0zJVM0xUQVVVUVBFVBU0TRBU0FQVCUJQTQVUzQVSVNTRFNUUsENBEULVRETRMFI1EB +ExNNFLTSlDRS0zFQRMRVRQMTUTSNFURTUVVUpFUw1BKRRFCFNEElERUyBRSVFVCVUQksVUVQSVEw +jAVNLREkURVDTElFFFNA0xS1EUJJARFITUVVVFUTNSQFUSTTUIlQUkVTRDNRVBEtRFFUEK0qRKhR +FFFFSzAgRTRDERA0VMQwiBQoAKf8T6J+y+eQDhlEFApUEIh7xUyID/0hV6wgjtgUBH/HBlIFIEmJ +iqCGiaiKohmoZSKKRKqJYiqKqoYoIIoClYmqWlKWomigClCqaIkopooD+S0h9o1UQdjMRRR+6VNJ +SFBSnkgGgCgYWBpoilGWROtEA4EBDCSEwMM0lMULCVQ0jAFVUEkhQAyRSUJSUMFUUzFQUUpTSJQj +QKxAFCUFCJRMFJUQUrEolEpQpTRShEg0NAEVIlJQFUkClUrQUlErRRJSoJQDSA0DMVVABSVNVUMS +lBSNERQCUoUNA0qFIhQUJSlFBQtCpRS1QURCtKlIFKjECRLQsSpTQhRSLQgUFUiUlCU0lLSAUglI +U1Q0VNMqFIULVIRBQKKVQqUAzCBQhQFFKUgiP2eGecA8ygDxzg0g0oFINNAjk1Qqd4aEClQ8IUTI +oBKUKAEpRVClBQKFEBoFApQ6PTKoBwFKqQKe6FOhChqhhkJIYCWWCknvd50yDuBD65UOokjJDMhD +If84BPjPhKPMP9X0fkjkGaYRkiNWF2gwAp9Mp+6ByRKEmCJCgPxWxiIT+vqz2wCH+kCKnuhVFH8Z +QfifMbuevM7Sll7hJYYb0At/lVYKgVAFURCjkHvcKZ1LCQR9YcQWJFdGRF8tadR/03idiT0Q/l0O +DLRkh3u+WlqdVwzK9ybuauG1JSBbDMDc/ug/7F6N4CUoStUFPMrkAWWKYpc8bNU0kS3PmcPTpzyF +f4/ZpAeIIKYSUICQJIIJAgkhJEgIQkmhfyyCJmQPeoAeKQD/xyO706nZ0l9/nPon9OPm/LZIoijy +XWqpTWgNfgQdzqiaCqWpSOwf+i+EeZH/Z24/k9XH1j+Wn+vTbOL2+sf3DIq+Xl6ujKf5a+t+yCfX +zg56v41DWOoV9T/Tkigb71I1IPmTSjQtZRxVct9YX43G+X00tU0uhgwvOF1AwLRIyJAFsXL8SILA +sA7UQEHheyvlNsglm5aY4E43sWTMKFiaOJTlqkdZlTHu9Fhixmr6tXRtycxTcarLAKWK2xLQrLOK +WCKOUkFCv5MEKAq487EilHtknekPHsXeb4l7gxUOjPC84a4qbmcFAxUgmaWp6SZpG6JonTzQEgso +gkQz3fOCOKCaylDLIS6tHBry3WOTnkVLxOZYOvK9zsx3OdORBlBpwYHupg2IM0zHw1PfoFficoc6 +2w9u0IBArYTCvZ+iGR6Yp0YKiCkpKY8nAILnOS4UPFgKBvLWMtCiJH1GHhsJ4X0ca5zvb6OxcOmZ +zymOduyWAaQDokZb4F2R18P3U5FUkPg4TephhoDQ+x/jPwyg8+iDgQPG121kiSPgf0PT69xm4qs3 +FMPWuRfQ91wAhmRzkmUJleQzSjzXZ8xPnmDUL4ifDFJkmMhyq0kyAFMW4fhxc0sehV/gt8brDzfw +P4+Lq6fl/Wv42BbvVfKRXqO/kopMhrwbCHju3YliR681EedISXM9IDDSMLUFxUXllQsRU5anZmpo +jylFS8LZXTCUSGJZL7q7pL3Ee1aNtqxZr18zBbiaXl+iOfkpenVeJHHSiQ/w04cGPD8gdkUPn0/x +z4uFbeozfdLXpPfy+E5frpJAuV6Y49YYnsJUeVt4vtDnPFatOW91835cFOIp4iysmtzvE3S+UCdQ +HJdLUeAkIxy7gdOw57X5uKoegLMGx+OGDcglBvimRfB+ntQllD4mbCYAedMGFz6DyOP7uhvi30R6 +fb8HHxBb0wEI/HyRu+hQk9lfD3vXk0w/X9yH154OoteOdwKaF1dH3tMJ9hWvfYkQNDjNv4R8yjOm +HCX3nufa0fBcRMkHuCpUgIlhqCCPyhDGBvrmkSiAU2XuPpij6f3/5/T9e58A+gPwOow74d6T1kkd +ts/AMhU1Ud6K8ZhiLkGExkgsh+9PNDlvu2+vJx5GcGyD76bLGMUgoHv43esg7Ta9sOYzSQUYkHtT +1dQ/DJDmp1tBk/u8O8LyyGxEK1UCtctOp3oHwZ9GpjOIwWKgR7r4Pvs084FDzblIHVnCHj3WGkOF +alRSLHAzJMKhQUWG1rz+7p9W3PgrJlN9E0FFLrDEKaApq7J10G7+W1XOhTEFWpVSYlEBRZEq9Wel +hoYT1bEMztTEUMtiwreLWCvQP4k4fbyPdh6NFJVepaohg6SRQkl247Cp2AuFF892Z37VqTmp4Zvk +6eUPqF49Q+Cz2VEGKLJbRR83pzcgsUmgoV7soqVQ39NkqvokOqGqGik9jRS7YoPoY3A4TDRQa1S1 +dnrIvg/sEHk8JJDMvuLpeHuMBcXI/I7GFJ2hZJXyeQIHA4kWaZrCGsIaz2YQ2hhze7y/cX7L9J+I +fb8k5mffMxQIBJI3Tgw9JGPPiw5pOmykZiQQ7EzBQMgcpJvMaA8JmETsMeONyDBzXnQj0lgIzKgZ +pEUAoQQlRJuaKoria0BrQ18Qn4ePoYjSIBHQdFbHjiV0o3LLXnjQWh8gWRAmXttFTwia84e/ftGb +upT/AAMpF6s2NXVDba6gDi/EZZkBBKeeCd2SiACOHbHJE8UbSEbHnkFb550keAUmhJEgSiAy2wEo +hKNiJTWdwybwOf05qZ7oOSHzjNfnZApN8PPBbLVPGvCEc1Hlg4ENa6H14N2WDIlUU6Nt6eeaXfOs +ByJ/0N7aApCoq7nVuuJ30HHHj/Iz0ibKBa0bTgPQdyCKZwQbqaSwCVhBHNROBnixWXxu/aFnyIXN +dRljvy36s8/LPn7KCTNroQ1vufcHxUjMQTv/zdhAbx4Zfv0GgADAONPXTzKQows1dve6qA/sAm1E +kAmh+BAUCMhInQy6ITcqBvxq5CiJBg+j0U4eqaA9CQOrDswH89vMOh4Z0z44hkJkQ+6TgghNEOZm +0Iav+XyhmGa59Pr58K7EoYKjOg+di5rwlkFsOAiF0nUEaye0PCTPBonF8D/667aEz54hfM86ITM0 +9zlrmF+t7ohGCkTFkEg5yHX1HR8Op2PPT/29jQPRgdxB6lDF7EbhqHGAIh2+n9dVvn9f1nbp0Nuq +FgLMLSpmBg4YZPvdaC6sC0tad9O/xztvTeEGLFStdnjsDnoRa+5D91/D59ZCf53gI+U/DWaj0OE+ +gOqfs+bY2Cno1qAz1UmFD6/j+LCmG2Au/258Q+6Gvh3EMJYt9g2DpvQ2cEOtooMZOiKVJdg4zTvH +5h0wfIG/N156vPGD5evaHn2vwCNYxx/H59+Hq6AHfUzryOEEOOg/NMN90E4nS4Wn7K+rPHgddjnp +46bzrO3htlhrxg/fqfH2CRv18LnnIlaH2b1XozGXH2Zp7FIFm+Dx1q3fznVg+njO8MKQANLoYLIi +/kSWb5lsshetjQ20D307S1jgHjfWt8vy3cnbfA4crxjYWrs8cDJYZ7eu5aHHXeR5HTg9877Bqw99 +23Skcadh9leeNrS7BAjHDAOQCwvp3Fg+pcM6Hh9P47zu/Hrnij25h9v/HvuB5Idp609MZDEk+0OA +4RCSAXEwbPMp8HqzwJN9Y9i2f1UUXQ5oL0QyIpbR/ilQwh9nb4/gyO/7b8VSAK6/iuoEnX8XAdz8 +vF+pywvtzE/lg3Xs6LqchWVL60LB7DY1xi328sRvFHLGQFdf3Sp+on5/ZkcKR18+dxs3EB/ToL21 +dh0sIwD3AIIC18j6c20ifkhYf6+E9Zy2xpKs0eS2GSTWALHtkqmtIcaFhWdL54qQsVVjNCUgguvu +l7IqGcFqRVyZ2IhBMOmnFKSChVwZ6ih1lNJTV20HgwinvQyce3b8dfDkPTb3vtNu/NgenjS1/f6/ +e/XPhYPoeF4Qw7c0S/GakMTujAMSqxF0Kt5mXWUnyYmhD0p+UcMXR5e9/dqVXxfqscknHUc9fW9N +1PQb4+fhei5FdZ7jjn4kx9k+lxbw9xiANVSwk4RQbEeU1FtbT3PFIWmvA4Iz8q2PdWNRkxCv1boh +/hy3HYBQLmQK711e75wadmoDNbedonDWiPWC6EZkDWSX+aEEkVMqBrr5K/vOTBVyLkDw+l1BuXG+ +EnClIWRRgOrEhecDrz3Q2tOa8MYaqaOQPnAhPm4ifY+uUbX1NmhhxRiqqKRWYNRHS6t7nMMtbCjr +7+OMltezagxrwNvX23HXOw0S2fQcf5n166sHd/VnQHcdTrqlPikTsERW/L6IJYW5+wr2sM4cdH7T +okb8ebvKHlo9mC0p8lNJFdlwOReLx6XNz6UOMDoAX9wHgDqZB9IYUHj2+EfDlGk3a6kL0GITUurK +dAbALE+d6ja9y34Fm1rS8+OBDr7PGePhth4SD/E5Hp7LUjwPLgo6JeD6+g6IJEVDiMSDxJeBJRIs +HHF0ETIa/5W51z1ws51H28V3NfL1XW7Xxf4A/IUA668FvXvy63p6C6mB8oAy7ntSPqg/y7f5GyEW +DZ8d+QPgBI8FAh+hgkIYfO4rFqRv4kYPggvmAMzwxIpgFfEFRRMZnudz36oY67dY2jOrGBBjThP4 +s+u6wj08640Hh87AK5BJRHoPQZUDK/ParuT6Q60o+x3f4LfjtH5N109dRtfDnYfVM5ZBJK+uZVdj +2vupisRwnncSLL/lFPVbMJiiyo2SLfIxA3glxqPRDxzv1OiOL9G669HE19/fXc6TnTfV6bHPkNdb +A8/d11Or9J2e3EiHCHheAhTgkivYY/bw9zhTI9ETCzMXIyTz3WVRyQWQvcFDwBWtA8tdd1gofaXD +evde3x55Bwek1Y5TDRHsWwkAAYwDARQZQwjYZPeCh6ix69oMAGEZsa6+p6qKER08jU+5igObpHVS +2BFvKdySWQXlfH+Xz9asjr5+/T9ce3XvDzoDXjW0UNwijvXj7fcYfa868RB4J955RE0HbyrBgGDg +hw8CO3HlZuXvFG9Zql5UzG9PXY9j6V04vrlG/fJ2LXm2LgpQ356FnAeuZ6Yhos3ohj607KNYKqI/ +h5C7H3yNCD7JDNeIfK7zB6c37+4GAcGSB5MBjvGEP60wDIRsddDdTpXq5yYgPhGofSm8etaRr3Dv +7XsWsn0+ORAZpLBw/PD0YnYvZ8hQHnTjnx9vzGL7VPsdHaGg0EMwDB9/wge3yk8i/hpa+JEp29dN +A9nyNcOiAkg2WRhwFIiBUssLwrjiZwV0BLS5oS4tOCRIhgSIJYn9fvz4Fr1Fs48evN36PQhvhW5x +/de59Bj6Vf5eo7A3w3fuX0Rqo+seQBXjxgzJsWw66AXRv4wPYje7A+wvC39iDgdopYgER9NRA61J +8wO+9+BzQNOmAoaPxJVxYsnbgIP4tpH292PXkvHt54NCW47VfW71THBvb+n29sFPDYP2IWW4UV9P +pFXBc5b6CatU0yPPWeLDoyaSe9icWThO/rQ5+NNM5odEfnaYa4g5B9TvWDigesT4o0EKTViHYpU3 +/Y647Y+oGt0DOvW/NJrzi6lPE6rxDQKsPY/Icr4D/Ab497i1b+B8+GepcDDcLIIwhhRA0QCBt/aF +OfTYJH0enXOBw6Suum7froX3xQkMxu9nOCBpsYb0tS+W7P63nOYY6q496u5STdrhsjQuNLUxtWI5 +qakYkalE1501dOeVzWCZxqmAphlE0VSiX1wc1PkeoA5Xf7tzg0Pi8ZshOB61YK69TV3evg+jVFeG +tJ2CAw+kTFroT5LexyVe6whEMKIF7MH2R0kA90D9IQPMeO8QT55Uv8Me0eMHvnqeV6tfwgekIeyQ +Mg8bmaUX0Siw8hAmIx5AElwWF+i5liL986iixdd97cGKmNa+76n26nZz6bdaJH+obQjlGRQHJqV9 +wNdrrVJh7Aw+18PoBb+qB+qVawf2lz5nt4Ps+uhHD6aDJGQ59sZvvGvMLEGCuEU888+J9ovvnmqj +Njw0bZCstEV+PtQmxELvXxDtGxgzlCvlkzd/MInr4+Rv2Infv8ofQr/CZ4QVnHG+h2Zu358h7SgB +CHw7v089/NzJNhxKEjOvAr47qXh4r59H4t/Vr18/hsfbn4byxe6A6wAlNoz04+r2vRNnvo8v1AIm +hwHAXMXLjbZjCJh1JKh+cc76K7sn28fIfq67vSRfj17/ZTRNfxgCM1yVmJsAD2VRIgNWJETHkdQl +/qaK8FEjTsBSy1VX3upmlkEkkoYt4Rq3EhjIuOfFelkjNFNKz1R2uAiZoc3/T3fPN6nwhyfB6hz9 +lgj3dMnRMlkR3KYggEhgSPSXODM7m3mvbQkyxA3+I98qpBQ1aIdHqsjlSidmkxHg8B8UJuYv7YTy +Gei8lk77VeBHNaNLAeaC/dUDSB+93E2m9AKBAYg9767ph4YeH4I40gkkFjw8TMnUY4rxMNInoCwU +M/l1L7ACvXQLcUVx951rYb+R/bpOXcS/Pr7YIj65oH4Dvuh6d+078iAu/b2GkVV90rfivTKUwYAF +sEBjoaNszoohQIxMO3s9ueOPy+s2+2qJBUloVBY1dbw+0CGt9Ih9Pyxz8jVX84z6vXVapzV466l9 +/klxN58v1E5pfp37fbE/pXnTBvte+/U7oFiICEmIlB0H8idxHZbp8Hr59HoMFve10HHTM6oEbIC8 +Y/6sczvQIQH4Ht/jr+49boP9AkPKf0YMZgzv276HjkwFwS5t51kl9qULGesVbTOqWHphFt+PJ/kZ +nQchD5uMI4fHau18u+o4QPX1T4x6hOeudcMuWgz6mXRzVtcDp4Adu/k8ZP5ofGYPM3n2GIEs1Fmb +z0NimFMZtrA/STOwGPpGAi9EOwGTBjZRmVPzV+5+dwy9/DSKJ+F4uc6/k/WvA4625bIl15mE1AR1 ++xvR619gfTMNEpmYEsdbi0ZB+SA8Dve6ebrfxqs8cvVZFB+hKBgga37hpRBBg7LmDGi2tO4Gi7pC +dilDTS+cRAH5VWyk49Ag9+FjH5rxPd/Hk7Cop03eTeubyMpJgwXjp2gFmgszAAggN6lgAB8izdyk +xgUPR7wo+4MH387ywRIh4+7xLfAUKJGvwoUJkihluPpzXp/voaG8156/LfkRZZBH2vShtle59Hw9 +AgZbzGvkO3ydhyiXWF7z49Zz4kUYixY1yw7iiBIje/k/gfXI57xOBL1Yr0PO8lmT+DEzjVWo7NqM +gd27Rm/Lw2gbL9d19Nim9OsAHmXUxxphb8WNj5isyS3nQA24JBnEzaLJ8onubnXfpjZvdddmxeTw +8PPb1DAd7DOvhtNIpqlgiQlShxmPGjFV7p7FG9+nt7k+SDiu9+xb3sdHxNpxuzmMmF2M9mU3SVNP +Bz2NZdTkO+CVg5IeYCXNTUtbJrTqCwYSWFwFFblMGANtoEs6cWScMN03Qom7iayFmBhcbHbHa5AI +IwO4KU3Yau5odVlN3Nc9moCUx6Am+ebslp67+fJGmVgyDFyFnq4HrC59qHkMOAsD5fdnz8uAQKM9 +5fw18vbPfzwXF7/arvHhmL5dkeSOvY2wLeF49295rnJ1Ic9l9jz59ezqjaLeQR4XHgW3MnxddWk/ +dR41YHjjea8TXQ4NgkIIJCWYRiAfOtLNMM+dctGuKkpRQXxVgTNXVne/DI+wfXI6EC+tAF3bkqLW +QLUAEglgSxNodOzssaDFejEwEYeIr0gvwdGA4VU1UUZZ3AbC9DZJ041HtGzTmAVsejq58+F3QHt8 +bHrHr1k9oRt4tBJub673sfo4/UfD5d9F2bQkAYBgj1loDIgTGuwu+eTN8bjXbxrx3y/POX3zvu+l +0rPG6Adp1sd2OyQdltZ3NBWGroJfYpYVOsjWePZO6yqm14AWsjq8zj144N6GbernWt+nfJ7THueo +WaHfJ7w53Fiwq4TOVEhySf3ecEDvzlGiH3EZoHQHKGTuNdcuiK4h8ta7+fHiAfpHjswfQV4fwG9S +PlFV8Z+GAVUF9lWduZCEzLgp9r+00aiVHViIFB1VPDlz8xEv7Fddp+0u9cL8pUdyP7iFDNBfEPrN +zEoOYCChjp3iZHxCK/SuwFgGF+ICZWiygAAZAtAkOYOlqlllvC5Yp5w9IRwRAk1PV/4e2XNNOapW +w5LqIkvCO+I+1SNCHf3z396H3Y/jnxx3v8NuHH0+mfDxvebD7nrg7fyeQc4HCHXP68jwDHpnBEQO +GBnf5yca1dqRUP2vFB4Qbq06FF4ImpgrHx0R7cf0vWKvDaZn5jnoLed6wXLxsOJBbAjijXcDVqR5 +LbOGBgcgtrW3gsSyDjj2IkvfUN1QCGB/Whw5GriB1E2GqQRSA649x+tBNwg+Nln7VmNp5janPcRf +3OIFfv5rv7wuXvpMDfI+ETfPT28VLHnkIzeiyXWjXNeDdEM5bY+E0+SPquHEleEejwQCPh5p96Q4 +PeL5ocpUxcc+rta1I30UvwHLreC+zfcVAFbIbRbadLh/0BjXVQwSKWlz+mvWPTpJkAR2klrZ5XUj +491RvCjQEiXUFmWVQGeT9qQhfAdFflwV3ow81oJZGkRKJCBLww2OpumkDkAmKSC+M7eQz+R/Xt55 +zDoJvfIvd0MLtQLGsO5D6VEnICRi11UpiI1dvfz+Qp34POB/R1HTtX+3QLE03dFnlz2eQ9CPCUu6 +B2lPMp7EBiAGKNNvdhd3a3DlCkB5WRUlGJW6UxBEgf8UAKepQZg7pxUsnABJMkJIFabWYyLIfkGa +mihkQVVT49v67iLFKBLPVB/INzx3tl/O3DnDI3d/Erh61CdnVXxh+vY0RJDpMQyKL8kcQEvX77hB +QoSNofr7GHAOH49zqx+8lOrFhoTZpGjw9vvxkXtQ6xKPbFQl2a9f947M031SI4AYNApHcQP0sBAB +xABKTTYVdPp1nvfHTUojmr76ZoRGkgBP5sWWW7+fBxeCa1UlTFrtz8r8k8+VRL2IbBiZqPFbunPC +COu58UfwNUoCdR/jzu6vfhh+deVUpQDBv98e8jKeWaPN+K5zvdoeBHV4/nxf5ww7AQhDhv8IheWO +/zU3nAxCJIz91iqX7eEFiTegFFFeD6DA8m0rYPLdtrwS/PZ8RXdGbleRqewXdWgEhB6EIMGSPXCZ +gIJ2l1w1yw3/mbBe8/G4W3okX56VTyIKC3tRZdOAQApOybc0pYjvZQglOSJSdiUX2o+7QtMdMOvE +Bt69n0Rn/SjV3u3dWKMMKsvKeBm2PHzVsFiJG8YGQaHDRkqy4rfgeQrvT24kx4IrYGwiSTjEb/KS +LNZR75mhQa6kk031+HDAmw4/7H2sHUvkbeHYQIc6hmChSrngB6uDPeHAMUtULfIfWWEdpN7xE0Q1 +on/i1JrvyZpWY5Iy72J/aMYSKCWdHloNMTH+9hIz6mBYgdb0wAodaFiAosZ9VKJopY2lR/W2CzwQ +OD89YxYRgdbYxopBpKpdwZP6OYUJQdpM7YJiQ2U1EEtBBFLEVTB9/38fNfD0AvH4pnh+z+X7Hb+9 +5DFTTvBekoffKOLIoH7xWW9HbRedTeHZ17Nw+NEBc9bAcLNVG4po6h1vsQAdYHjwdeinlohLLKO+ +kCKOmORxjEYKBxXp0aJb6rgCuM2kswvtMPunw5qh4fXMlSBE0ohQIg1kIECwujcPoQBl9+CGo5CQ +E/H7cTsUMwxKlMICMDLZIqkylWMPdLt4ev1GIPg/WnEU3+F/g46VaAmSqKGka9tP67X/x2nia1Js +kFCd/w8eDCPgBD5PMcaHihIFQh6c95g37VUbqOh0ZHUUA4kkDVjXe9QlFW9gkiORdHnUKggAdxxR +RkXIoVFRXu+MFvrYJJV0aF5UxY8A2xP5fzLLLgHrf6NwmICQlUAlBQQgaCgMUD7pXOrD24ZmlcMc +XW0npj592e9lHmJr42HO2AZh35e2E224R5jirGmhsEWK4UkAqFQJHjaxztvXzJqcEwCaLjRjs1hA +BDT2JjGMAYhAoKMSjeIhQ54vB8j0IshOMS4sbbXPkLQe219G2ph527duZrjym7JRAztsTAw2KKwk +BLDJgJCCzkhZAxETsml+Zw7CRGHbg5pO5AgUghtGs2EoOfseTkWO/Xz/b43BKw8PKB5tHoTp0n/b +0npEB4hzWTAkwJmKCnU+v3e32+WvJ6OgE/MFLtMqKsovvGEIAIlCdmbwEYqGcdgm0eu7OXN+ouLn +I9y4XQiEvcXkOfL7ewamm0DUQmCXLgw9krv1GJAkP1JK9H1SZXOAAFkIsCoOwoUPoab4vdUs1+em +i92w11uHxGelkOqpOeF5aoZIhz2Y+hOT7vfzw8/hwc09PXPHlw8I9B24tB5ISq+dgMb4xAPGcd7T +aEZQToMhXf7vr+ks/NDvh40+3PZ+np33JHgQnBQSRxgQ9KkFPHnQgU4Ak+RNoFHERayQwMiR93zx +irpQVIQhpkhKiRIKB4xjOgCJIqSEEEIEyiCWTzJqdAEiJPHR79fiwv5Py8lFDMgF40qFVQiII9g9 +fTjY9/IvFs5ICltZWNhYj52dcoxgxQ6pYgiG0TphWPGz8/y/T3G/eijxX8KpK44JiZqquHP4b9jb +v/gHxOXI/53nJOnU41xNaIsJiGmS1LmZg5jQy7D+nbJ243yCys8l0ZLBUKIwBAWR41d2l+K9z/Zc +O1wS8/4f+qY+npk5Rfz4x4MAKoHn6xOYFzPNcMICYwaolAQUZyu1e7VPec0jN5Ifp3nd3vhe2VbW +s1aLm3D38isft8fsHe7x+Pvw8e9E8DGpwRbO0VGNbaxrjSlajmUS5L/Tfnzfjj0hSGA7wdo6w7l3 +B4fjrWWwg4uvHb3co8fvPJQPMqRaUClBiVEKUKAEpUE+/7R13YrbjaTrgxXGOdM9gFelKERoQCkR +WnduqDuLGDo5i4caVDoTp6LADxIoUKNLEJQBu7u7o3EcWdmVw2N+vuO7afbZDHo4/h/rf6MfflLY +8WNKOYjlTFuNyjaZmef28vm6fN/LwhBIQgRMMUJ8YlOyV+s3n2yRSLzECTBpeNA8RSGhLGpCWm6e +iFldcqiMMBvLmKnrUBjHjodVSz/DfLHPIbCEGMoBHDI5zXSZWc+QLJeUTWWVRRiSFfZAX7q4RQ0A +W0zMiJLmCgzPopvrHMFI9OoY589CQOulUikEYMClEuySMnNOPZ7eRgfOefXU0dtHvfHpDuPqs7oI +Ex21KCkFIHb6p3YBuyTaBxfmtON8wh+53ScNMKu/wcBoNKFALv8cA52R+d7tYc9SyKqjuFzseFgl +cm+9c+OVFN4GjHt0ugUGng2MD5REwd2i+gBAeLxDEpXtnjXG2i6NwaR0hpNKdLqVrK5aSoUSCkOv ++Jk/I5wn2+zXLlzK47hxpCcvCgpIOgJtAObhge2R6XQ7Y/XvxPqIofWokpl/ugWgJYSJWAgEk8w6 +p6MypbBmZdkeDtlsEpAlMQTSTAUNMs1oNBAyNNDEjoDMiaRMsESmjMuhoyd1+Kf7u2P2lr9qZYot +lYMQ7ihp0HnLCd/sfEN+wfQDpQBPVpOwOYvYNuoi9oxnbApDrmIcymo3tQgX7/QgOzF79o73kDkQ +i6AtKzig0qVJSkgkgtqc5w1vHunheHhFDIT+fp7d3lkaaTnMQycrWanVQ1vjc4n6f9P7/h5J39Pw +uvRbX3dUYj291YjQekGavZ78lynopvnyllBD8IBKIkRVDRSNDFSlKzMBUgiAufHjDSwURCR0Tb9x +azRswztTeGxrJtfmyFNgLFe6VoAhuaww1YlYQtoFZpIHskIG2D42/vefu31/X6qfGn5a02rMFcDM +FUNHuzGL9vwRvorxypYKBwKoSAXVLkIRw+3V9bDyN+M715VfoP1J/NkCHPP5kWAofwZ8/4SQ4fRv +MTcf85Rlo1QotZRFqLx9zYJu50+Y9TaDf9JeOcioqnNsa90ibv5OA/p5o8HhKYiL6vmPX018dYRV +8E/2sDGBuNiJ5WDGcej17TXrZdjjBLD9f2bbLCffBsEWG9vnPtrIW4zmm5nTsamIs8Gm9OaYhxZo +/DWfOa/37vd09nt5dujSoixagdJ9LhrQvxNJyT1Muob91lErwzzfYBKGMZNqS2Hdhue67JtxPTDI +ed2F/x+f0ec7cn542fpZVMLYVO6IsLhUy1UjQ+EcSRtroURETHnoyAoYNy8pxMkSQz69Gg0JXasD +6mQ+kdv1/tn2Hovh4H0nm9P4/r5ff9uxorCgsZ78LgxpmDniUwyRHJFd9jiLfLSwAXjpQbHO8l8S +NWKL1+MHj4TyR6z/NCguIUlSp3m+WdFf6kYlOzV42BMrzSEZ7slDnpKHjIIQj6eECEnICx3tUx9Y +bJf2+Ddl0tF5XIHO65ZABT9+8QF9r19ePtPJ7upuqkbmtkyMov886/tO0BsE8g+m8vfNLGOrFzYv +nTjriy5RNgivICPTMETTeBLgZpbGh69+R+rr/CNBErhP3+LBEhbTZYl6n9xzOkDJ8EbThASGFg9C +G3Bg5GKQRaCmFBQn2nc5Jr1skwcol9at3ZjybpKExygP7c3lzbS+P6dzRThScpcwUMu24ZBMN1+E +yrnTsKEGkYxfDhIhKOlAuUXhoyW2Z5lDYKhkZa8dWHxdvjUIEr3XiGpN4ImJFTlFEQ9Amz62uB67 +XUVTbaFWPo/jdJPIQKQdnEuuB+d+mH3ox7poARflE+Dn3/e7iOjmXncURB6Du5rKCuqeulggw4+k +IgHSBEBerw9PAp1JFHfvzxrPrx5J+m0WAEeHPhDc5i+ObmEPM7OcMHRq8zqoPJWwuUSCVY5IqHlI +h8crp3YvrhLoFY0IEQlAAnxsQfIGRUyhNLsv/mzN2rkCI0RYZp2HFFKIhMt03NFJSnOB8Z5cIS+M +UeleGACwVTvuZHgcL6cyCBQyqgAVsquJzrbshfxZMdYRKh+O0dzbDmSjLgAl9dvKh5QU3WDGFS4E +YkRFyxCLQ6jPGPaCQAE9yDeI/OftVJQgScHR0uDdugLQABe2jGq3Bcov+f9+CbBHEUAXmUkF/zS+ +dk/eZLI5tkK4IWI/TZD/hD9zfpsIEAehCkBLQUQKGeIMApH2ZYLR0Vdd2HwM4V0VjP63dT5EcodI +gusWOdRGeGMoQQCUD/hwQCUebbBsiP1kv9ABJEkOKCZGeFADECkDp73bXgB/OoCeAcOksFfGBeQB +XINMV8ZcgGlh8lAEcCBpuklv2MElzfdbcsHf2xzRAIj44owlIKOZkQMkpe7qOQJYEICIcEKSSigF +ku8mcv7n0ZmoG/d3P9jIfW+uJhr58dgqnPAyGB9NUPEL6Uj48LP5yoLgliEdQ13CQOleqAFCAxQF +lxEGV7vV+EDp2rfkZy8cg+Cn5m0b/79EXvZ0e9qS/6fzjmSMm25SUZwBKJ+zhKoSo8APtzwk4CAC +mIUgCnEib+5oLWdYxFwwAcfpQUa1jM0UEh1kCgKeFkOMD6Q6LgUH5ZOhyV7UpvbBieedcFQM0hVk +TQnPqr5WGeA6JGpTuBs3ibiNHiA/c8goR0EEmqJOV/HNzDWJiiTjXqn6yEhSpo8hfdfhBGqzUD/n +t6fXt6TznV7eanizWE76HqN7IQ1BhA92WQONzUDNGFgaT99M/T3fNz4YjAWSLygdOAV7qQbBbg4X +6Pfa4Lj936/4HIcBHXpmn0qdl7ngUF7UQJ3VFE9MtMKS9QVEKSSiERCL2elFGqYN2pStjGooMQkQ +Tn5yH0aDCSIGg2GErwJ7Y8I0geSPJQUL9ulV7W2Q/fmO8MEgYp7AMYCirGjN8DrZWpXVKBaGuKUN +SSRDDYdZNPBAaTuF9DrsQvJ1kBMvLMAd7qQPpuMmSQW4DDIJ0iGDpy+sxB6dK3eTBZF12LwEAQWH +FmwIiocAxtZujTdSRyNTkQQGuN9ti6RoUo+PjAaV9+uT0TwxbJmOqAXhEPazKkPdPChUCkISpxxJ +PbLqb7jffBjerEIpzIgJtSoKQ5701HY2ZQNDc3LlSylQTM31dJbKPWdxq3uzUJzDE3daMTnw6ugK +U7SiJknKy5pye+FTPHbBGBfVQSUAJQEAUIiKF0/EXV3qu+poEDJffjWe6nYhZD3a8DJCKS557h8k +Jc+H/Xh52bPGeyleznMfwBcv3nORR4fyk0n8Ho96WqUUNST173OOZgbw04bbNOdIWbG47spVzmUo +bRzFTYK7D19DQvBIFduQPV4eXtfT2/tIJ2O4wFTHj5a0kfhKIPo3wgAcjswQJNjEHpxQP5SGyOtC +r3pLP6GfhqdEIp3CHrcrxIoUxsyz1mYfHagD5kh5wcEkvVxTCVDxzBAX7JY8tYvANskrqCBBhKVR +g0IWBYolJNKopcOYF6aPs0nmEnfD4Gilz58HOrrs7X7LZbwBA6Ly5HV4RdKAva9nt2urf49T0aDZ +zhOxZCbQeXv+wSmQnSf49Se9/S0vzt7GsUKXzoI9MYqG6MBRYAl/JNUPnCRtHSf2dlQ1vXSs6/G9 +ZYULQAaAueZYSTxp1dYwXmFg7TOF/plRVJAUFCTD/x8KGAoqv+2zw/jQ8+CaMkYlz1zzPW0VQOE0 +nifp6PPoPZ1tSbs+Y9lhe+bw79z9p88TqFcZMvTVJS/g+HZLNFopvplSlgGSvI8C0PIGe+J3AxuU +5LkPzA+5QzlkANC/eCfYPAGtmISBLvuixfrJ+lOt/9OV7JPRbOKX1dtQzpAkUasmrPJhxxhc/Uyt +JHCLO8fqGfW2hZJKbpG0nTCd1Q/j2pe+FUm+bHFE+7FZLFANqjne87xXdvE2nA6H0/GXyvvWNRku +iFd+6kWt8RT5ropbwprHtpdVbcsZix34H+K3OM/J/bfojHXhEt2vQnfuRqxa/DHba18P0MKjfkjw +OwV5nwnHdFfg5oRupsND5d+SaM2FI7yRhSUnGrDvNuF8s0TSap18jYX4VD2xTXSvhXJQ3e8O4Pi2 +xPOFQtB4RYMNCx8SP7lh1II1KgGGqSlP4yBqA/rIUpskKE/fD6z/hIRTD2E0goaDZch5KfzfcKLy +FKszXQdbxauRw1lBjO070mRdCgUKCGUhL+N7RmGPfiEYPcSYyCQfLYOAq8l4YxBkz69HEcl5dP76 +BccABQEhASnZ5OwSfEeahdTcq8g6N4hfHGWg9r/Q13APuxjR4megQ7jIgIMmUMJK/7YJx5cQ53DU +t4e/1P5dE8OmPMBru2fHxflH6xbvPVKyZjn7XRlwfEwDv5XJ8N3XzypXuoDvPC5RAyucGFUHCBRB +jxfOfLky/F2b1kFwu11UG7WOktCXA1+88a5Fe4PUOGCJngvV1AxPf6BOHSqbCGkVAiQM7RAGPy/j +9IDN3Te+fzYfxodxbrgzo17Whn8pFgEYVZYmzsgQARsglg716e2RAaXcXwI+p9nLyUsDy51A7ehS +oIMgCTI2B9ZbUlDqws/IPnXWnOM0A0VEzR9W2FfgZYtUJxAg3+tNHoh8mF2Oe0jbuUewOI6evKWd +Ak5i8ykOImLOVIgR/MB7Qc39pH1Xtm4IcdolFsLIF+JggYivWBdz8oyUXjZcw0mtWwDVx7d93RXa +mkmzNDRLuXF/2KjRKlw6dOPBYYeiiJxB0gNqL6UuiDpKvWZzfrAvaAQRRJpxojx3WiBQIoG354h/ +V9DzOeU29EIB8InW5ILEh9O5cgIkVXgc774ZKct8yibuEVDje5iU2RtjVOMB8GSKMkP5p61bCD14 +SFAhJJjQfPqHzIYexETEGB4OQl8P84Hye5hagt8MWjKeK+yLM6uoBWvvew4MBiFih/vKMswHy/1c +DXMuHvHRl6kPBspFmBb5S/fWYSJqlYEUkPMU96NYgTEBa4MZoyKe8fHbMOnTRxGGCnn5hz1ONvKP +jQe6ogxOUIMZAS5+RnCUxwBVyrejDo4PWKLL0X0tF/0567/ONtof0c2tOG9RmG3hOKCGcg5wgQ5C +dCh6AXPDMCDiAA09V7SNN+PKJA04EcuFtXfDKIAcskVX5NSEiqD4DSX/PZmdS/PjC5FyXsKxpzYp +ejmeiTHBatzllE7N/kojfjrOCykSwEUwgFXrvXugBXviKhbu2Z+9yhIHTFVQr3tW71n/i6mVcR8E +zN7T/JaBMfD3z+6WZpGqWtmXAtuzbQBF/43ILRaQvtIuYlYzEfYdjYurb84Pi537Pdf/fZhSoJgo +pKRurKVByvDAhWepDBS+noZz1Fpn1lSoAPkXNVr4+z/5g6+HifSRO1DBpTOUX0d9iDM0X6yryqA8 +RLmFgJsQHOuLWTV5QiHFRqhQ1cXtgokPVEpSYzmM/JeyzvVlVRTJOzOLpIxZ3RMLX/aS7jWbbUe/ +RTzHmHZO78+MQTv+7buGrTJx5SYlSED8acbWsoRdFCEMlwMxEZIJEh10b+m7/LBpi+sBLMYfPwIp +1/LtUM3wx9mqCqJ8PlQ+z1qqdwD/zd6HuCLxLvVwbqiivoC7oFu/m5A8d1D4gn3Xt7SA5MUwAT+l +w2n8eavELKowD4UnyWUMCH3AIVnudWruwOS0S6buckTupBjibCnh6kVPKyx5InRQRQcCeLUR+fK2 +q2GaHGLKMYaKEjZy05QvAiFNy1zhLSYCg3F19kemCTS8PLawcN8xzyMGjqmsy5ybWiY0xfHogauH +guKIp6BVeRb6VbAd7DnA4KkhlgCQ6D3qnOupljeiIEMPoa9MTDyzo+LEEtBRn9KF0m/m/ftudOZW +Te0/pQsNmf08M1NZlhBACuPGYbnWm8hZvU7OdbxENi3e3s0XkOJQeG4kCcKBBLaOEO6/WaI/uxav +MmO4yOA29ionT6Ek3ThF7dxynkwIqKubsIEJS6khcMFyWJYEsMI6IcEc3Z5LLOwrfaGP4xy8T0tY +UDwhd2jxND7QfssEIJMGGGFpG1Gg8NM60PodHFqx43uAxRBWDC2mZZpR08mFQ6BNjuG9unHPgCtI +Wc7CnTvuFQA0oY5HT5NibmnTTPeRJ2YeDKze8DLK2baxzGx3rzw41LGHgzjrxmwLbLx33TNLu1Fg +KQO1sN7YaaR5SbwlU6HiZMJEdltguQkyCcFADz0mmUESSWTN50mjzrx0afS0wHRac04REkWeZEOK +kBZrxEFSOGQei3Woct1gYjS2ewCASCb3Y4cTFgrLRmiyuIvOhsizv692CwJpJLQQLMKHCHUJwRDp +q1LyemdNAhV1uyICC8qc+10u29wV0zcyHe5OSU2tZlO/KJicIGKoo3m7ZDv1ttqvVrBxLhSvKhVT +kw54VjFkmk6shxtYHx8axobyN2IkkoaAqpqgri0n37OvbRj7snjHpseGU0tAaKwZbVBEUTe807W7 +uzc5d+idXXFhODh0YVFy7DhyQ7MU79buQUtNsNkxBYD30iyqgjNryypq05vaBevR6k2hBVQ5MksI +Xah1SchLtbrrto1yt6lozBETKaEDsu02JMIQetPkNTqgNJpfocJCYbzpIs4O99PAKncQCMIbj2Xq +HY7qYhOec3BbCqmqM6Ybad8IZHZ3wRkgElKZlHyejI6QTYSiE/Wn1KZGSFEdCZckBnKhON9Gd9zO +ueGtZyWCiybLqo1DBIshq8PJuhNUx0113YYub51dSHQtZjgS4QEWfpWQMMnhw2KhAF3kQ9FHgLkg +uEdO4eeoHBHJh27W3xELg4pJJktZDAPMPUSXdEglUOTMCSARVKIC0kSTvQUeZoiC7oOQDJQO0EKM +7ioxOYMTS3T7txJshaVviswKtUQkjhjtCGIkJMTZCEqNO4h0Jenl0BRGFzSiNRQEmg7wYhNrxmJs +hTvsvMbL1euU0xQVSO7nJ5uDynwzK4QUEQS4LWWRdEImjnnUTqgEToZheCCbTm6QmmMJy7TqJaZ4 +CoR12vGkZ4UH2+wHJjT1M9pkeaVwbW9yxfVcEW0xysjKIKKLULqZI7KeIvseIEkNZFoN5LgBFMwF +szdlgG4a7l0qJBwnsXm1cg5qL3ni8OruB79o7b7viRExERFU44YQREU1EEFMxVFVrUUTFQro12lE +FixAOyocu+w563ws365Mdne9cu1o23qOc7dXhqChOExgHbarJtAUrSUR7qSsfXGJiapXpUSQCHXn +sDtgeNjuczs44QJBQ8melJ7SIOjboRTvGC3JZQOo3KDlOhbz0sdYncScMtXXTjRu8M708Hl35h27 ++zO+siMWOxR8dBKIcdoPMaVM4UU8F5Qj4ugCA+tg+ntjNMYw7mOwYeTrfxmA7UnS9DjRh3Rl8Yzn +m0coalBjoe/Yuo3QJkyXuwwEQzWMKgREQQ8RhhMhH9sg6YY+qjtnfJZgvMiIj7QCMIUSfLhzYJcM +nWI/uTzlEEFAj/vl4TZo6QRj9O32oAAeAUJAiAklmIoSoqJGphoYqCiYioi5r1c/33wVRLSVRCDI +jGKKoMREPTv8vB6aJS7XHWXuffPj3GJKiUIkIomZnV3vvwOO+4ZZkd5VtARUNl32xfkNjiMijEFJ +RVKVEJSFNNJUTRFRjEFZscG94w41NaZrTn/XL6vjuccWVSwHr6f9X+27GeP6+H4Sp4gYCeIcvChQ +oUQ18/4DiKP/7/ydF3SGT9/pLhx3aSeOrvZ4nD7VmIPDpWA6j8+BzTd6+NflE/4dJD17/3GB9yqO +akKY/N8uOlXxNF7739zhEjCFjltW5ZYezSdEuaSEczRw1voYVfXzjqlw2USvqmau26FkgdF+cJbQ +8LEKqh6vECZISqr1EMV6j2nhAq6Vlmr4YiHZFBIjsXKYESTLLIHC2GOfLilFEbX2myq+NcUc1AWE +bt7nu0Ynf6r+npt6NHUQ0PtZXl/mapLZS0J1Anzv3gdy1bCB2H63NnDw36KQ+E/Ea9K5Y053igeS +4lGm29Y/le/0XVVzfPglzy4wWcgLkwPJi4GeFdJsr2o+lQCEkM2fRRGj+FrcWqfr0D/XG6jka+Uf +N/DiKrkd9O4s70/caiQ+sZgMyCtNthPqxLAsWEaUESGhCsrDhe56Hf2znv6dHXjsdDlZHUnThwPY +gOfkaPmt+nehavcbkakcjX2/Hkj0M9hYj59IF8m5V9LxSHyIZhoo1Pj5R1MaRgYXPsp3A9mNDyaw +jRX1nyJhLzw6cgHB4f3ytX34iYolWYxESGV7hZldPS/PU6qAmkwYJsdMk7EOyK4VTENWVGTDLgdF +6/w9f7VJ/G8PFnLY6NngfYeJQOAiCWGu5Bg4bzhyDUjCFO/5xQGLPVvI0LIZHNKJFEAo0HbkeBVF +HRMdEA800PYUTQF1KgrriiCjnTjU9IbQFEUsqm6lxjSznvCIApcVlQ0QiDSzuhKWMZA3URrmyFLe +EokAnnmmC8b7hnizv3PzUNVNZ8xdRkHOqZeVcyVgRnELXT6dWSyD7+e9bodAX1Iridc5RRRQ090G +ahhM15VOgOhkngjiZVCqk1jy33ev361w4POoiS+0Pj5Dvl9DkgyvS8f9vW5Jg9+JX6JKYnPRLWW5 +UgKKn1g3jXLGi3xM1aIgRsDvjaqiPZ/fkorjddD08yYnM7+XXtw+RTwyaLyGz+s6acr2Gg2e4kC6 +a/Kq++OIfAe5sE7i5YOW+0GZ2+HvAUD6pGnlzdvDyCLjC/c/OxBDj9K+sxZumPfdDuVP01N9VRaN +fE6uVqLEWQZhxcBPFceNWTWkj8ak+bg7jgMmoJuEnEO4BOkgx68ptm/qpuohx1+v7XbuF1xDARrS +oEMBFSF+JgsZpOBpO7IFifCHryfvEAfHe+C3MjZH7SNOO3XxKD4SSRKuUQe/WHkV8T57JFVAGtYB +6g5xiUUB8FgL7r3fOlFWSEYHD17M58un1HlG7Czo0++XVHo5gmJtplx24HPBy4XMY1ExROcM5s4g +CjZtcIJx8KLnDH1OSshAOv+1+YRgAlAPw6E1CCGYZBJABMAIN6p+ZXoPrH4BH8DjlWPuNSHIyfTB ++sRYeVVzEmnr18J+tRHS98UVAiA1zRTyqjzFNO8yL1PFvNv/NgsjD677zy+8l+OuM3Na973DNXr7 +Xfu7nv+L8+DKQKLL048c/SRoH9n0jV/jxqvS7cM0g/dS/e7N0y82QAXiAOZZRjy9/ZNeKD2TvGjL +qYeCBe8+BRHJhvjB2b/mkJahRBKBFjBoEw+PwPSm+O2Xx18M1VN788YJhr7Rg+hKKb9bUAARERJZ +pzxccul+lxC31k4wGBsKyifA9x/+Wu3vt055ZBYMvxpFHzAXCbMOqvOiAUEIEP4eajfYpTIdcE6c +iDgyc5nCBDiD5f7B16T9um6Jujs+3POIPZIYWBZ9HtmdjGST52fp4lnv7N8Heqwf47zJvwp4tmnm +z8Boif7frk8GB7k9jAU/2mmFZJ3a688sc8CmJ/BCv6xppjvfPjuj6PP3Xu6QpWeOLTVaP7dlNzoY +vUXVNioxvw+tt+kD98G+/Xrxok3px9mHliFLLlI03ekNp/V/vldLdMGQg4gMI1+dBiEQFCr+Podj +DLjE8QFEt6vjnFMU/nzaberDt8v0KSAiJTZj7mozZhUCqRqPM9hDumImVzsEnl1u6J4pPCUPhD+K +PCgDf09vEL1euZ/wiBOeJEDiASUkUBQSFIZQlRy7jDYzxPRdkzbnPE0lU1El0Zq7+nhOZogqqSio +lH9PbvvXCjUiPSXaLnUE8TMQASEJBKIxTJ0cv2icmdhoUfsjBk7TXk02bI9ufP0YoA1ZeqXo7ou2 +r4EcoLggEYBAYtBgVDzE6EjgVl6+2AgX6Ncd+H8FFeyv6pqYh4wEYbdLvpdT8vjtV0iieJi417+9 +yQSPF3r3TlyHCpgB8CABhDNeIUOLG+yYH2Tr5W+WaBONgm7JFURH1JbRBqohRLWcOKr6czDVIuMx +nqPRtempNiASEJJBKEwMfiNOvMvQQxlvhdXIYIo0N6Ahr/6povOZIgOnF/DbNV/EMWUZUxvwplYO +g/KZI2CxPEavkJURthuoSvLsCnyEFJVyRGsBkI0IFufo01Yibebh31dU+kRgswsgU8K3BE2JtOa3 +YvGtXFQKNWmlZNGRSyMIT6qgnJ0IytDMEAiXKwPD6nVGH1T5Duk+JqpDVm6jUXqGWnTPLtq8Fida +UC5w6CY6u7DtNCqesik15EJTos81d6BESpURCp4VkjU1lRkOz2ImA72hhGreEtQJFXNZdgjAjrQN +Ybcp1VyWyNA6IL0NPp4h1OMVl/MjBsCSiycPt2yCsdPrbiWiIgGmdOId3EtEPCekJypUBTLhAGqL +uAQCg8ATEAOAlDxENuauZIgXpRImTAQLh3BDo48GLslAQ+GKeajMloqpQpCio0IfVavTwE/k+o/g +QPv8JEBv6EfmP4EIf5JtEBgzlgL9U7y++/b2f28+gTmFEJR63PWGnvKyne/Z5eKCnLd4rV4iqIfW +qqHzJGrNPDir1R0DCT6EuFlRTq09KnwU+XEZRiIqonkd/hL+Cd7zTsh4iSV/nefL0a2h9aLxxrDn +40FZii+m32vDITQYXGgEezkEVSukQEGNp3c0is7mgOgiRIRRGh6Oy3sdd9NQoIJ0wFQSAUkZQY8F +54oyvbrLaAqi5lYQhHrhzUWNBxD+6ucW4cUNamaG7t6KCBIFg2CbqMc1ujEjUA6TsNMYwh5WoU6e +RbjBBtVmU5GrLoGawas5jVeoq6dwFLkasIuVMa1OO2nxoqnQwRoaefT7yPkRfX9Zu8uPc2/1kX9P +NBvpHzevunnw6n4ifUy8PIcvfSzg6ZQyrI6oyyCYakcVu9a67jEpQlCYpHsBNPPhjT13TiPPjz3X +w+Ow2CBTDX2nTRSDRXv3Z7+C7P5b6JkmqdKKDHnLC6IgMtkcVz2S4GeYdGOuqUSCSSMa83tB5KXl +wiJab9zRTrejjc6lVkpelTrdRF54nepUFm1lhHh0RTiMQv040vOolqtdPSoJBaWlwseSYYxWrw0j +n9L+GR56KqAfspx6iUP7gtXG+efumJ7+bt6b1MCxUhM/keg66r3LnYvj9RuIodwA/kH4dxNafJe4 +K/Ydx9b/PLk7+9x469/jYgN8DXL80sb4e4zD+Z+nhMOJd6fJ5QI50Wh4AsFcO/vRLDno80GP0plr +58Ah69fXsSALxi9AZX4eG98vXgLmDM2NN1rUaBc7/y+Iv5zIoMOjoPGv9vrTfl9BL0sj2Tz8vmrg +2yFeR/s+d6bPNxj82PuEfDJdj38Q4H1/WgPjHPnAWvq1vyayX9nNAURSZZYrBRBqLIo457dgqGO/ +yacoCI1WlUXgoXj2Xr0MrunXCbJLLorHbdN3IRI7xiXXJrkTDjdoTLNgCayEJCCL+DJg0OmmgpCg +JmiZKU0hpKUKqmJh2y1RKP8EaPLGNWnycRGwdK9LExC0aFDShSFNdYjrQdIAGimqNCaCgEopQdKa +QUKHSI6aEChoSigRppKFNsLrWkrVshpiShNaqkKVNJVBpAoWolQoIihTQGlQoCgAKBaVRpEaWgoU +0gukKVGkDQiGlEjG2yhpACihChqlUsZKM6jImlQpXpXpQSlriApAKKVjYHJmjuHpoo6DRHY6Q3ZC +lpKQOkNMRGjARsIGrYQ0NUEyFsmlKSxk3YcShEAxPTbUKaENAUCUAFJpo0NMExBSpcZoTVRVVBQN +DQRMS0q0gUUjQ0gUt2Qe7FERUQV0UadaiYtGIoFmqrtoY2VdNCAdJSmagcVOI0RsYtNCkWjUQRUG +jpehNAbsYgrrrjVk0tB2LJ0L0lIKAT0gaIBHgdHMTwy0cq8qTCqL20YHkoplRQletsWAl0JPMbQD +oOF4XhReeG5JJM/2kkFNQectcjwJS4ogdCaaNOmIYgoYgIiolKEp0GqiKaIxkx06HJBKnXUSIqKJ +IKTzqEIqaSI6DdtNPWhpmujDOuknOahJ6eXkeemyGthM9NbHU3ENMzVIltroLZsViQeRUVQrWNy8 +utgtdq7pNrnRGu1YlHaEJdPIj3CwiapCNtO2pJhAoEKQmUiVoImKiqp7sBriBqI7ZnGDLljl5Juk +kCIuYFeVInlFe2sajlBTl7TjTxkMzkpuB0InSKPb27x6kE8qXBkVTkQEREFkypkFriVcyoVtaIQQ +VUHs9VKNtkeRxE8zFCwgzK9U8KrMCujGXhXtiMEM1Vt7297nFU8IyFY1o0ZkaTFm2LKHL3Cir2c9 +UGeU5FdVG2NtalKCVDIrmyR7MfN7yqfU+oGoYp5eVR1LDevJltvD3np9Pt7QeWoct2rDnO7bYU3e +u9q9s70amkvaEt5AoxjzzvZ8e1vvb3e+3Z+fsIb74332S9vBvL6B8tuyRbW0v3s+o8fFl8q97Yc6 +1u3nzu9t9t7vYWJzTGCX2LvTvvDc0SV3tCo7Revva9t7DUhdAS5KUy/1Obpo/slyzvxCXfNXhupb +KwPFoQ4EC8DiHBiBpqrL1JmSIESbuRMVM2jL6cgZQt3iQYzTUQ4u2uHsDJ1jipeNAEPWsDvdYqAi +lbVBpwbcYnZ7eQIoXmCxeZKeomWeiNY4xATT4SxitCZ1dnT6GoJvInWqnV1labVRhAL6egXmL1oj +Mpi8S2FVU5sjsbcrN1357E5NZZaALRhKELSrrPfXzvNrt0RUxUtFEw1S00oVTQ9Dp0DqqTSa7sd2 +MwFFKRVRQEVa0BbJoYh6YDNyvO1hHlBBXmhs45RVzFSgqiqdIV9GQ97GkybQRITLXcgqPZNguREV +amq2EtpJ1moqKo0Z0BU5Wwy8ryQ5MC6mYKBSpulLqIStjA9ok323L6KJGSVBPbSVeMXveHeJupeV +9oiN3kmoBivDZYqaIg8jTTUVFFVHWqCZqSIusVEpHIgoKvKOUQkoZeJTqgFVGVBLoSI6EzttbXOj +284LycihxnKInIoIopoKZZtFaWmKJtlqLQY2tnQJBsYkChKonYrEiTDS6CkD26O2WtAml1MESHhs +0c8jxYhEJXCEgKCnRXEiMg7YXD20j3IhhVY2MOjD9pBHyDN3q3L0nKpw0QrqzIrh665s2xSpmleX +Pl24fFCt9oHDSx9saViKZeERh5kdsOkUlq5FkukuFMx1QkJqoxGeaqLarkap5S2Ny6IXhRSSkWWK +kGhuRnl4VnRjsb7vbd5Vw6c49FnzF82GK67xWzMrbGkdG2t56dEv31O9MJR7IYvS/P18fO9NKYkv +DuSS6UpQJe7zUEvguAheG40YsWhWrJNB9Q+ioSlITmaUHRgZozqhF2+pku9u8mxCEvVrJl3nVLFq +FoWrWtGhDvAMQMeZyKGF9RElWZeYsXgmcEGFl4np409ZqmGZSsYRgcXrQF0MDS14xsUBqZx5Ay9C +xZZ4UMtW2aglzGnymYAEgGjYupsAAiBHjME5XnA0EqF168AdO3XnYBpJCwLMGYXdu86kG9F19Y9S +Na2ZNkkxFg7fdOOYKNikXeJT5zLl3F6kI5q6eXm7iRpXOsszqTF6K1WEPeO4VzbKnuFcB6aTosYm +b40FkLaBUKzhITlcFhl1mKlFanHZw51GLaFyxZYIyUu3TiHkaYbaHOsdsLyWNblWp0KvalNPQmbj +PGtWDKGqukvrsqPkSUH2e9i/T6+rzd8bDrxvT6fKeTEYhRmFq6jZslpUMlT3t5Ryh4nVrrleabV2 +VSG5rokimCRoTJI4oXbVnQioI6igsmcbRkRzL09BqdbYsZdRA9dJO5TNOFxNK2ejI9OiwinHa4RQ +gITkI06rZ3vG3sw4wptlMXOoxSbbGiKB0Y9e0D6y9Uupjd53na9k3TSpn3G2p4u7FNJ0aO2q1nW7 +IOmJadFDQjrRoiQNA6dI+eZDo8JXgIQnE1rkee10q5a7MqZkQR55NFkic5j7O7775xP04o2l1Lae +E2nNG1smMjJXyGRHV7SYYlawIL1pRNecqLw+o8bGT5PJ9EKAt677QYeUyexCKJPufU/Wze+vKan5 +7LiQ7si9AKYAMRlNZSYKqpWBAiBVvRCh6iRgDoC9AORmiwlXg1jBLUhpfNCXDRWREM+VLSIGiKoa +0xeQC86jE0TNtJD3NQQr0x01kOqLXetWQSmgaoFjJvQoQ4jBTgGAYgWBTabSuy0GxrU3osS5xqxY +709a4WzATvWG8lDTxRBLwFtauLhaTmraZMqZh4IMIZGUTqJw1qdQnjLhaqrrUGKQhtaOqNTLvoKK +i4WQbTVd5g1WTEYoRxRLundTYzBBWqN0YfIyBkOriRad3uwnnBJguCZJXuyfrxfmzqPDfJ54zV3d +7w5xCY2NnhrPNZ1GjbZm9Oz58Ttxexb2tMbrybnzSl6NLRWxr127sgvjEON6WZ7zzt1jOS9C+88Q +mcJOZLww7dEWxFbNDCEJsbZC0ZXe9rXvWfVejxmj77P1PeE+xGvfe+3tTjI7Wxs/FqN2kX1xDp24 +W+fe8Wu1jahI1uUXhci8oE57PHsEaHop8pjy6ToKfJNL5eHFtHujhFV5T2fPvefO8t7P31vQn1cn +x9j7wXHY+9sixvXvVtnwnk8zph8nspEexY2j1ayq+wecXrhFe+eMeb4c9eavC/ffT3S+t4vJ25rr +jPdt13QnCmeMjaxV7z4bcdCb0CiUSEz3kNhu5121HoGh57z3q50OunlqUU5RrpOXOdqm48zyhOe3 +KgemOJZIgekOhMdgPY0B5aSjS6HQA+w08QtA+2ukNUvQtChSaFopCJQ8gDpVPKCInfbeVJzGc7z2 +Tw+qBHbdk+z7533zznpXV4ZD0vjEadu+xd7n9Zyoy6sHWpmkHusRiVECTbzCzVQlMl3RenMzTxqM +d0VcUHToi0XVVIeVD5CUzcw8zc082ZuYs5KpUnqsWHUaVpyqFRivTuMrU2nyYkvD61elkXmDIQtx +9kvBxyCWAMgK+ds+X0GoUXvPaptZGgnslymh9xDSQTAkGTES7zMtKEyIJlCRBdc7Y+iPRdrboXG9 +fd4+mUtffex9tD9YPfPvfZ+3x9N77d9sB9ze9nx99E3uD5AU4Lnde97vQscXnJlj6x5+0K8/Ce2Z +zGFfrePQ++33x8oKRTolIEFpg4pNVKyzsGcyNm8fV0RkZWst60IUiKEgxEzmXFG9Q+hV3cTpxkKI +t0MvH1pBy4hTF1T/70idiQizJ0C5QfZRx6Pyz8+8JDU/VvvY8+o972XIfMfFR77eNFb0k71RBVy8 +ee4kQ58/A8ZrIDAkCiGYOPvznV6dXvsU7pbt6wVVJ36GXUzV4TMONQhMhXWtKjc5RrIMUqpaijND +FFl9I6UPEqJd1qbsF1U3Sec0bBx4oSYoYqrWsrJfUTQdLJjSyYRahQgIGVpBhEGYeoy8NRiqdWid +OnjWLzlZdZe5mZ1MzN7qq1MzOpmZvVVWpmZ1MzN6qq1MzOpmZvVVWpmZ1MzN6qq1MzOpmZvVVWpm +Z1MzMMjkg5OrcpKcxayjgiDFzOS9ZFC0damFpRq5uZmc1VVqZmdTMzeqqtTMzqZmb1VVqZmdTL3x +SQhCR73vke974pIQhI973yPefD9T8u8WdmezTDq/N3Of46dwowuwdIlp9AUiit735dYl0jF4ZsqF +BpIJUokCjFIzu+qR4hDUUHx4zduwttNLRTjhIWVQSUJQEoQSV5/rBMNk3MfARyasvk6Tu9vjD6ff +7/vG2X+MxI0Eg/xP2zJV+9CzvH+/DKJX5l8OWjp1Qsik9f12dPc/fzmPb1e1zwvNIDV5yIwcFCil +QzM4OFznMqKpoORhEXP/J09+HqEMLI/WuyHzoJVAgA/Da/HXprNW5CIU4ytnqg8QQcPw3PHfZHQS +VQEJRPdGCIBLO+NwAOPXXfWSB7WQNJs0awGbgsOX6BnR8FBztGPkx3hRpnVzpi4CIxgoCZpgPiL4 +GbEc1Ewl0CVFT3+TI0wePVA4hJwwvOc4OGeDotN6TUMojpL7MhwtAVCrXXia/VHIq4FDpTLLR6ud +GuykS2wzURB1yj+UvTUiKlEd2TXkEkYs9Bg2bof5WLCX6q7Jj7gf58rQ8SDmls6ctXU4fAqsEFlF ++fZ0hal5SdHVFQLu7RzimAo7pfkIU6noxg8DBHOzAmEnRKlDWwHPPaQMdKyVkzhVUKQdr+ucKjOH +lgzqw7D5FmHbSRr6zryq68XR2SC0tGpF8YjyL1Z7Z8GgBiZf8GCDQYH6RusaQt3whzE/FACb+V78 +N8KSNuECik0IorBodRLEk9c80IoIwRTMIiUo0b6+qqEgM1EzvvC+pKUaOalz42ESEhwkyQgz2z03 +nKvUmoVxgYp9cgpSiUvYFyhDQmR+Q/j8mEXJfCSaIMcP1ixOzpHl5+ndzAgZ+3T/ibs3fOziO5Rf +B+vE8zwlm7lM4dTLzOVbMlz3OUPoN6KEsJP+DpGTNshDBk2104rqhWOcTQXA9Xv8lC3/Hd0IU0gX +O0dbsNo4L/gh04sx81CdZBp8/vD/FbmKVOP7LL/wETcKwXQ6KLidVp5kYR8MuRHu6ON/F97Cn7IU +JKNUdQWif2T/1Ru/zL6x/SP0899QeL0X8YgCiPc+jjnucLevtx38hH4PdyL3baEujwq9kTB4md5j +kmMabMm0Xc4fpIixQNIH7/lU4FIxzYs+UfPrAJFDVZmyN6ABc2bLDM4VCZ2QJNNCQSxhpHQLz0ca +ZnUwbTwe4zUBVoVYomaJuEI61HyLI6A8lbN9Cz00rPP68PIrHnzJ+M61wvr1Lr0yZCSEgwYMGfw/ +dPo3cbEVWCmwVegFEoyHh/WHrGvPZDGFTI4MgUc4DB7x233cIuN6Ub1g9yK8gKU3ZZrwVU2RYHgO +AGWOSlyOeEcfn8P2h8oewXUJ7dM+37XW7Wzffsb2J9xKdYQIiAoIQBCkxSLxXhAOSmxehy8Yk1tD +oVjv1RoKeKfkEYniZhgRUGZ4CvfjDYM6qQqCYOkba9SAQz3Ic/L5Dmerzct1eQUfthgGjXOTHu0a +3s1DMjQ3pzTNjeYD9nLbGBwnphnp6VZyftsITbu2bihI9fCGMGoa+3oXI/pcfd+aArnH9B+OB2AX +hJYEDr2kJd/XhNGg5befJ5c+eHVnQX8PT1NQfOkKhgPKhNAdiG8kbQhfRV6X8MbRZmDh/LelNdch +DrmJznv+XQ6h5Tz00wHgsOXSd+wZNn88gkRBMQ6UYyJGmtaKZQjw6dcaHMRYKLVLEkoBHLTlFEcE +c1TWOkm20xviE6V0X1ErnisggyFiSLZXkifxgN+N3rhHcefmECOnfsjYjtzgIf/OPlA66UVFLpYg +SLYVQnKCl6WlCQBHmyCRM0YsoUlJigWhGqdmcGFUHsbi9xWIIJSAtjMGAtyRtdZGwtRPw8SHjkiw +qQtsiip293E8O/2dPZ8trPN39CgWJgkYL0wlni/ALmBAntKkh6B35zvkSaOO3jOHkr51x9nv9pk1 +5jxOxm1+RMmim3P5KSAdtWJ3VdHKicmzjtxBYnJ3mB5vC+M4odyKHflGz+f6d66piPKj8M5aa/p7 +awoDVDwWb6FgEW1rwbH2TnM49OEM2eU7kAh1OZQhNb+ZEtfNk4ejxlrnE9MA15QoIF8oSETNXKgZ +EmaspZYIBwjeOlwzonDYMUdu3HzzXmWeQ+x7r6fNrqHPvJRSmS/TaqOr0qgAUsCAEAipDhhipgCJ +hec+5Altj1I+Hv6/Z2Js8ICBZh2Qh5Hh2ot0O4HRbHyg+xRSoiEhDuWVkcAKHo268FdhejmVIGW4 +OcNkMy75Ha/DGL83937NKwaTu+eaD6e/2d42p2t2ejSy6s/WO6JXiSO/Jq4+3VedZ15pxR6qjuud +386Dvm0do9vvDRbyrwesMfrRGKexsjR9fn39GHlfj1VoZ9y0+zqRwiiFgQd5S91Hn5FEcRl/tkwP +iSshCQPLVAPBX9LgGYVjmF4h2JGdJ4y5gKSEcV2euu9VRnrzezXhUKDgGQLnkv9gv6hICXcrHmzC +Ys/L3KeDVN394cLwf+svSW7gEh6dvzV/QiHnVelJHusQxxHPQHyyjjN4/xMI+ce0jnaMWXseIs2A +fPaL5BrRfhWKKCxZSyIrBmKsrZUC2gpiTGKCrFJiF9/Lb5z7/8/u+X/jofu79f37Fia21abP/f+L +0HkJzxh6QLlixtmIYiyduvv5/9tjdB5cirByZhcMoLMT/wITWUizVpjDo6YaeYfz8/f/f+HynCx+ +fsf60FezS07IzBlKYZlZbSFSKVqEfKmMFxp/z/u/R7eNzgT3+ih3BqT9Wn9+lzYlRr+hxhfFzm4q +D9xADMoDlVEZWZAFI+4nSYvzbrxXSdcUwEooRVBSjtbhZ2w6su2nsjrlXjZkeIEIxQKCpQGoqAXK +B5zeskWrbv6um9xXkGRWLMi1stTM9uaMn87trVfboMQvjQMYbcb6069th4QiwTUORIkFSkrKwmF9 +FeBC2SICbJv+pwg8yhBp3SYcFI1ZtXCvmGOH2IDw5Ozdi3tKp7c2LndxEi91kzYqdTvB7pQ8Y9F/ +S5pVZo/1eHVXSWqoMXNA6pubTg85ZKCBrz/torrui6uTswQxzX+geNM35zX4AgKiqqyMFVbh2Pjc +BRCC9eXudhw2xujFv955gIcprcYmxxWe3R1aGoJHyRfTLrZgXkeJ9LdODGO6Tu8XTA+cyj8z/9xy +nPMuZbpeODR7464Sjd66OrviDL7xx34RU45uaMQj2beEbehi7e+OBp5/jJFl4Xn2dV4Ciu26bSeg +TeEbW3825dUMGI5cseCKjQ6QS3Jjo91c8KodlPUj0jSZK0Z7OVYjLV9W7Yga2KAgFNWK9iLhiBv6 +ttrv+CGCPdBurDfCDEKM3n3+ZVj4qBoBTg5f4mHAv8X/sFyCgJtAtCbALgmoCLHcg3gbcaDelCQx +R7rz0QOIEUFrx/Hq50trLKJfS+Dw3sERihKAoSQECOzS8lxYhPRjDsbQaKEkcZlAbhBIAnXDm7fd +Oucxy0ZmVmPKnL5VVM3qZ1kzrJm9hgA0kMKkEeKEwdY/OPMO/l/x8Z48dihwvDgrroqq4Ly8qnEE +SJdwMm6wdGyd851zr15Locdw80VRPKUFJUkFFf7AkImoAwqFKUooiAoFCgQoU0BoRaFClEgKYaGA +oJBCgBpAoEpYlEmiQGJaAFpQKBaQKToFDSoFIrEoIFKC0iBSlINCadLR0CJoCkoWqRaFelXQC0CU +gpSUAhQlBRTSiaAUNKtDQqVXSgJoA1SroKVKEaQaEKBpCISmkAaXjv37aPLtrje/Dw6Xjry6Edt8 +R16nOcl2tdwU8JQKVInxiqFMIWilorJVMJF1x3fHwHu6tHiYb6q0wWR0BOlVUIhLTzms2zMzcIYd +FjI1EgZChQpUQhxz2O/bfXXjxnJ4QnZ0cGQqxCiwEZl/jlRigJBOZ52sNL3rB5fBRfDe4YebkMBT +ZpemH15qZBSVrV5mq+4DP4f0lywbYVHjM0c+h83fURUUV1Yo2iTJfw7HY3Nv41gbDpLVal1Gasn8 +f0tMHvnGEGN4oR5KUORC2uulMZp3NEBzrWRbwFPq0LSnYKEQ7u7xBZah5EKXsp6RrRsj5zmSZh6S +iCvn1pxvXVsNhUb085ARO9U8wniI08l4ch8cOKoyIg6IvWleQqfHnCTiEOXJMRETpTrJS1oafWRG +oytGUNTheCy/SA+hrFLvYC0+oFqr0plyniC7x6zESi76xRhjRtg96mLnJqXV3lL4CLijhOj+naow +iZrVUI7VU6EPEPyL3qH1OgboxVoSlbbecZvOXX9aLdlczqO07QSiInvlt4c26fS6HmPtpdc4B+q+ +qTD0hsQHPt/yvDTQg6REqVFEYEBOCyURS7Oj9rgwXO/fWFyATS6vRyIFIJSYWLK7jR35g4gbZVxR +0UzRz+v3/Jfp/t+cxx/djtrWv3Uu0Et1jT7s1krpcaOarjXaMoiU5LTv996/Ox26A/xfxf6fJFEp +AJAjEn86a+/rmE/V6boklw9tmcOn8fEXKKawYsmXmxgRpjQGpIhqj0BcZG/znCJHqi1xfr6F3evm +Ml+Lb10Z/D36v5eJvbFb8PHoIB12nU/LqlGv478p2g/mTmm8aB8X4teobR+fl+4CnCNfHMnZ+kVR +t7nODIzhQOLub46acxSisKPQ32s4yh1LkuTUIikfYV6eEBkjm6ZgA+leyXwroSoMHKGSpwYoSwAf +6Ud+f8v8pXp+6qiOXtg7TF3RF5NKJfUZcRlx8NRGrwXtgzaIZiWkhjpwjynOlGRm1nKPp3wDrXC6 +QXO1rYPfEZw5HXcDLxOKvZoxEQ6zjN30eHj3lVfuYUKaVApFCkGhKopBoaKUopUiGJFpACkAKQoR +ChUSkSIAWihpShAaBOJBAxAmYEYAkDSIUAFKtIFCUEWZiWYEsAzIsGGtz3opdxC3y5ie4tTc9sez +x008m/DvrXbnqZrv4KnjArSLSrEkUSkVNABSEUSUJTEg97gA8cZjaEHRQBmH2wcKb6AmgXLzJ1lT +eaMu+LbANwgMCQGAG45xK1qeamucuKypSrJirOIxetR9nP4g8xM3g6Iss8S4BI8ESYMFmeW8dT2o +YDrCg5l5cTfZ8Uw6EjZLvLfaMbqZQRZuzmpcV0kXLDVY4wtVXX9Ja4t6IcgKkMpMF/axGmfxxBr8 +VgbxwS9kTPIu151Pes3pYL/wysnRB+QKcdvX6xj3Jdjo80nPx5Y0mdIdG5t2HmC0QBROIRIwKI9B +Hme/THi1rgAJAkiMQCW3HXQFXakXNuBRBeUogBOg0UI4LQ2sillbUUAT1PVOardPnPDETuqhou6g +l6qYghpIRajBHYLSQJLCn66spx1oGcm6brsdaSHADmnotRGFujd6cCBUVWU0GKTDVIURBsgbCniV +hRvWuXDU8OpQV9TW2YUsiABkz1DbIeafogYk5a8QanQ8AsHiXA1rZGJATS76rUsLq+r12NA6zwI1 +02iwel70J21vkqC76uyDvZtqhp2QHawut6gQwscUkEVelstxXHcdyY6c9BgjzUc5IAosCRR7B9iw +GRTsAKIx0w4/W+o70w9/F7qQ1YukJKYUht36czGHnG102y2qQw6LGpcB/CZqrtx/lpASQ0lgHtM2 +EC5h2iVogHyNz3A2R5OQdeaDLqLU11FTHQzEBwAjN8cB3CZoKBBwU/Q/kW/eLo6sQZUqBKLRT15s +8woCEkoCUI9CigkFE5k39d038mjnyOUWRSjAb0X5gqs5hYMYltHXx6NV7s6lpbah6dIHYPUbLHbf +f4fOO7xbBR3DgSgSqc11o8xoia0DwjP8cQ+cmJsF7hl3ECam4oNxQBJt+Hbw878QxzEXy5SMDPxp +kr1ZkTDYpBWCc1u9ZIUhjiZveL4PMnTk0VWfkzimDs6ptbdMYlSSEnS5hJnHx419unuSbo/ty13Z +kVACgFD/afu+m3KY1mZ/G/0Rldf5eXBBn20kp/mhSIAFZIAb8jTuHPlL/FzVy4OVu4pR2NYth1Zi +Xv998PZTzUn78a/i+3u2/6c3uxqzQbLae7tv2oxdgSxTK0E7YW/bqBnjJeZGecDKxYcBdk25aMw1 +0daKNVkfqkB6kHeEBwhwddUR2oMb77rVq7woYgJBigrB4Gl/jxrbbcxTptQ1Il+4SpsWreV4Qmhq +GRtZ7GvOQk7IPB6npLr+XQ77tbbYDdmC76vSaOjng8n6cP8Xr6DjZwVZiNqJN7JRDsNFeLRUURe5 +8/Dj8uitG4wj93J31eQHH5iR+iffj+t43ePtHouPCW/HH1fK4RwLMZiHhnt1rP8b/8867DsUXe2B +oVtrpNJ0Dp+5ve8OSgQ8LN1QagQEFPRTbCqeo4LtqkHfpcA5RN02dIv6+rswZ1DhgRF4qPeu+7X3 +3I3bNiJk2aqXa7OFNOCMi7Ts776nyffXmJ2sq4ztdzKg6CHxQgC3SWLo3w89FnXFyVwbjqmnmYeY +aJlrWgZYVBtT4mSQiBAT7JKuJiOA8nRzztsl9JnkNQGbHnIuMRbQtTNtdecoLZbhZYlpUi7qjRNB +CI6+KG4A4dM2G+QotIHaCAEliwVYrsEkL0MTOgCCrrYAepxbyIJt1sjW5q81msnWpnMmdZM6z7a4 +GZybCFEMSwZxEAU65FaBh61cMRwP4jZjUN09i6qUeXCZ9lI44xCsVjN20I8gS++5b8/X5Iv+7JTR +SjTQFIoUkSoUAtACULTQUrSDVTTBRElAtKBQoHbuefj3y7aszxy9trttmu4y8rDlTRe4OhAJ1GAM +SCkBKoKB85yEFoBckSkgSEKwBSIgSa6Jt2MhevRnPperd9+A1rdq3roMzC6NPGhSmJlbAbhDD0IY +AIFmYdlAypKEImgBoKVpA8vLx7a146Sg85Pb9uMmVdyvfQboDjcKLBEaIfpNvEIMDUuwdICyHIcs +HpffYHptN6WvL8heVy2m1RBz6iWkeDHJwEtAFP2zEn7CigsNBCpI6pggWNTvNAG+Q8NrDzZxkx2y +kxOTwhvxZN2cMBKAJBHoK9MuW2qp/TfpN64N7TdENTMW6IaNWVk5AkP35mBwqVWZtSbs7tVSGtUJ +YN+gBpWq8RDYHxvEiPHd4cQ8Tt9KaA6Gi1FnbvrqGA/2kDt2WJAAMGHj9jM4utbhhvVVAEshmqRZ +WO1mkO9DhjxtgZ04wO2wPPwytnwWbl+b2uhL2+85JDkDfgDsOBVoA9oAQReIN3q3AbKQbC0kIt78 +GeJDdni2NktxN4oVmTi1hitJS78A6LB7VECSBt0wDzTiyJvWBsgQfIIUg+OBrr6+/XfG+/bQ/Zzz +3IbWk5A++/3kV6X6OD0tzyvX1mGj18yrJ8DRAraY7EHbjtoNtr0BiKYEmJCzziIuAhBRAcCHl5IF +0mkqUFKHqHTV13p/H18kg9od32tKgqnxQKO15RerWr732zltzDgTPlz+ioqQHuLhS8wyRl5eXb6U +AeqmuGh4MdVM33AHMoPk6VDK8Wkko6ANINqqcVUOwl6cORimrkCLhwIV+IJcgTQBrC+FiH6gTuUW +rj6uYyZv69b2RqreOkw6NUgNTGQJuC7HKdqLYRVIBSg5cd3D6yOtxPd3NHvlTcOuYQLtQBEXZZGk +qypJ4Y8cvADFdc8wrEEsSZNERVHieP9Yam06aY8nK1rrv05BN3fl1d2IUADkQiJnGFU53OUdYUPD +gpeXBudznhQiklBQ6h0LtN6nhXbHLIJ0kIUgOBKeHyZDEB1vk/j9PozY85d+FqVFKhR0wzBG/d+6 +N6T5CoNbFirbmKttp38cTwmSm1EAf3OHBEr+cIPAK04d2pjD8iJFHWdCZMQFgJnGCQ2uMqGhJvhU +chcwVgSwJChQpBFcfJgnoUlxWUcn4SM3ZfUHP4eXlwGZ5+y/YUT67KQGA1msuVaiszY9vfEbZCRE +nJmntzadapMaljMyXff08jlkx1dGCqa1UcLlLqGgo3ls0bI/QuwC9ZFiVEoQGk4D9f2blUVnDq4o +bGhxgwLsKtCtIqGDrqXtHU3W3W466wKFIp3XdsaurICDQA0tAoUUUUiUClIUKtKhS0gUgUgUiUUg +0JVUoKAvKdzti5VYesMGcgHFrLsOW2GwHSZbmE2k1kzNlKqzMSzBk7/mCCOVB0eXWH+GkKIj0Y5G +Swo9FQqR9uUxmmlKatzVRPhhZ/XJ4zW25vAwrRQeoshkjDsmqMOcWmty2UoYemjqjrSYh6Mpp85t +dRQZod3HNYGJAWBBZBgpj0Ml0qfNNqYD+ssu3b+WRRlCZlX5d3R8NZ43W1VEAkkOZgwII0Aktuzc +K4mWs9H8Ps233YxN2WtrFWFZMZB2hSaZ5/z/H7j3+P6un6fX2Oh1HuFQcoXMDNp6s/x9veBoKg8F +HkOnldAVgqKQSGPRe2NfmoJGRPGXvf4J3qRQ+VKihDIGZal+7WTxNNDZkxrCsFWBUKhg0glA9H7c +NOdQE8gUCBBDDSsua1rFyPudE90IOkADDMj6xvd7ekU6R0t4FK1E3EVkZkRq40hre18CAA7EsCU9 +CiVXNDVcu+NIF3rRAfM1QzM5JzIyKeIENtIODTw4dkLdjSw6nKmRXXp03168b6eCP7mQiBopooSl +SolWiJYlUoRYqWhoBpomQiUoApClpaRKQDv16du/d3UvAiBESOU3NjlymGoxTptCQIgSBsBgPCVq +loCIKaKRGJEKUpBIZiWYNmqYczAxhhJE64wmLEjd2zBma/DN89uvFvqduOnPcU8IBKEpaViRoApU +paShKEKKSJoVaGkGlaYhSZBpVImiiloBpRKCgpEqgaoaKZmIH75HQtBS4CU0BEi0FCUBSRChQjQi +TJkqnTr2zKEUxc0oeodBWdAujT/DoinsOz57GTtpsmjaYh06oH2KO6JnOS1YNGQSmMYD7+r0nivS +AuQAkYSGTBBzIGn5w5+WG2f7vHoAPRAZ11uAEW8+uRFECOQ4eXuGeV99BMLm7eGdBp158y1jzwXD +wluNGJ2rTduHPLkBwKVtkPkpsxMMzJqbIqbcBxCC5/p33fpzvi0Oyt1QrwIvUgUbafFERe467/mf +74b/UjzAFvrzDepEzPUMKnxgj6ffofAD4lfkgQWd3QJZ0kEHYfb7+O9g9duI8GH8y7+Sz0m312kw +7KJDEkEWB18Pl30/yiGq/N1T14JmYp/HgCyBEHy+THl6XF69eNNN+3CkH7ZH540QUV6ODomdESLI +pEYfTbIsE8/Xxztwd+1PAHciHl24IQkgUvXH9tCDb45Wl7PctlG69wYh+BXx2qp3AWndv2unIkgO +4XoaynHLlwLru3NdPCzCYkcGdvouFscIuUNat5FoPMxAUzUCKhOAp7tZIkXlwzE5HUvzTQY30qE6 +qGXWg6vU64rjazgBvp2iLfhYyhMQ4iXipYW+nEkAZb3DAfQfZInRVoZ1qnpukOPRjxUarOgp3MAI +tO6qB3rk1dDRbTaeIGiYpwsQE+OoD6CLUrVdV1U9TXRpUcHANHZwyY/yeam6Q7MljSFIy7CCIWC0 +dPd87NN2KeBMc0OMkznJBpmTZBTJiq+Nu7saa9t9uTIhIhPlwJ5X+7pDbQ/3d75cGOlAtSga16aO +2Luq98LZurLV7hwYlPRgQBfVhqP8PDr1+/DpTrc4OKJiSGCJ21UUEEEkkHTrrHXRwF0YpGSi5EEH +hEFHhFFkhAyQyN7nwOdfyJOI7YNYpj3BmIToHE0xAzBR/Hs+YGKyYgaA5gNAh+fT2q71sBQ9JxoD +NQZKOxmtDbt6wsgbzpYccZxYoqKcIicDjxXcaDo2wkfMY60UUeBPyqhxveA1s7ycWpmb8G8ldzgw +yT67MwdoQT7YKCQDmdCEyobOfMYpoCiZEOMH6ZTiSP8UEwUqV4G95XyWk84+nPgvzk0M0ee2wapg +qiNuSA1VnGAZ34EHg7875eNYjz3HHxSBRQ8p5HyVFTIH0x7HPqeoUCUAAK/4s9brAo9WJrLnSYFH +bWBNaakIjqAY0cSVESSSEZPo9Pq5RTbxclYUHYlr5tQrOieDK7IiIPOJW8mZJz/b1ECog9ghCIEV +JyxkaIDZmJXDez1j46Oat1ubMJfXf8X+ue+gwzAIBOEGcEfI08NNHT2DzgMuSzLbOyMTGVvHtChw +1gI5E33ILQcpBTKiFSUQMSUxzeovbFXgG1CqsP1YPeTNg1c0fluyY60fORyOqjsdAC24bkBaOTmt +1JZOlaiDc4YBXKc9k33wx4KPIHnF2e93ftrcq9XX4xul6grTdPbTeF/yfmSzz9PWNSuFfRzhv62L +PeyuVWcYX9P9/L1IQSEJ3pwcxEa5nX10ce8d2OFN63nAot3sBfKbbNkc/XW4c5Ggp6eMEELaXx2s +3cj4gsgwydaJMRuZ7JCdYyjmE0HPei0FZJz0JNJp6t2T+QeQaMeN4nIbfDYuM/AJYM3TCCEMre+M +1QBzaHSUhmAbbMWQkIia9YwYcQKjU9sDEwe9okQI9xdY1jomifG51+ESKgqhT1V1uZ1kzrJnWWjX +BxoBDUJ3PH0cktXNAtl3vhsZpBPUauxQ3ifFt71EqBbxV4L4wYMNY4lsFxeMoERprDqUodttI2AG +UAFzSbax8fYgjbVFJgDh2XfU4cOwAGbVbytyIN6Gou5mJeczdqZpPURTxr/kxb+f+/f08786lQiV +DxA5pf2FpeHYeKQH5CgBHlWbIhP59By4oetEOj3P7qfrx4C7ImeTDTOu4Zv0fL/T34MvOtN1PXUM +Ku3zEGx6yBLoDC9esAXoQB6vOPqcMVcZpg9PuGEZmXIzHcI2QPV3urvMazdIDT3UUR69Zch9DkNW +rKeQ3R3eV0nvM/Jk/QGtoaHKdq6mbn+GS9G1Ts8W7S6G6i/EgU9zDAi5wuCRW+5mPFPzxQ1SSgid +bYct9QxtBn3Tt+UfrGmb+D1uN+Y3zgykwrMuZlIVmOGv7y35aWDzfr01PKb0u07p79epd3qGzO9x +PbeFMQFasr857b9tvyrGyzzx2bd29l6lwTS0PCGPfW41giC+YHafAnHQK2BiQCrTsHIw8M3eUnx8 +lRM1E+J5UV06zBRGnMMI11qCd2Rp9mH3ureYd4Klf6fqH1n0/dveZo8mH6MghvqLv5sQ3iMaZDwc +vz6XSI1esXIUZ+5puBWY88E6l3DSpdhJUiRvfMaqrNSoFy+iotzu6Sl3MREy8zSqKiLEW+tqpuXK +eSG1ZpTkotGLSp1aqxr+hoz9qp1Ho8iuADZ5y7Ebn2nq3VTT5stLxrIAL1HE6sl8RXh+ZcqJdi3j +d37rfX3qEc/CveMXNx4d2ea3lGl27Tl379KgjnD0mzZB7P9tnrMgw3uW8RidUmSQagFQCrB5Lt4h +L12QbOvLVEgoGoSblipp77Pfbmw7OmRVOXyv75h4SfeXO8nz08wCJjTm2a7Rvm2zKnx/K9+nshu7 +qav56U2RRAcMIOAcUSjXDP3psBRxIEuzZ3/XThx0jPRNXYVlAw4MDT9I3ZdPVfvuTq9VWL2N/vVi +OgKlMT9uznWsjuRPyu92kfzCcMFK2iq6pbDSuE3dDs4vpFdF5y9dw9p1Uw9h5RBkIy4td+jDs873 +dS4ydawSgoAB0+5zH4z9jlZI4cuDoomWRE7CAgJAQgEINL3wWRWlIji3URyik7zdRzURq41rVi0u +MzgDnCz6ywOb4ozZC5Qe4NJ8e50JCvHEPQLqgTvY5sXUA0LjfCbGGgqjDyo2wDGhGEvS0JR3svto +ZbTlUH0suJYHYG9Vg3k4KYVWbVBsKQFYN2mRdDMAFEMAzaSCm4Z+cZg07BiaR0rRwliMUUUFEUBN +NNza4eSJ80bylpFhlrjmnYt04YqrbPWH/1xkw9zC58GuqVdaacuEbw5KJqL/f+5v0e2b3mnfczCh +9GQNLqbnwW+b6YTN7YZfiqCp5Njxk7b9Hd129oR2czcKM2cuU/gfZq6oug9rHjPHk5YYMCWDvA55 +Mbq6OqmN71DiZuzM45dJv5AAszC3h8cJrBCdMYtwAzNq9xvm53L71N1NbipjhTq6h4qYt6eQMqNx ++3302CdqliwgDbqIPb09foN3r4aDD86jd7rf5r8/sQYA6zV9Y76qsE7fKlzgIEOjBDBBMPGRnvUQ +GDM1kb9ibUXPCXt32aia1bOf0f1/wYPGc2jHJnjwaTP2IaSimVo0DPMfqtQu95c1AP7geb5lE2qf +IhwJaNuFtNmpCu9cfKubnM2bjaszX+37P11+P1g/V/d33111AeHKh1qJPqYfzQCJLv59XeBXqhrQ +q9KhOv9tCb4+RR379fT6rwXpYzvCAS34EhubGYAXF0cS0RSOkfI50SRllkDWcZQLyFB19WfxdY+H +RpvBOyJRETtZd7nICL4S1BopqxDRZZzelQ45+LAVivaifxPm4dVXpiHrPDS5wkjbOcpjIEyDr1bW +j64BOyWKWXJsGqNP33c+oRwzCK87C0j/jw9GrxSDu6Xizq5e2AFAkTJjZQnZFBKHPoMqIPbsd5zg +IkSB24WZxi8RCcGj8mlkfYRWHrbfEiVTi5Nhsv+un6yQ/BAkZ+fy0z46P6OaNv58rNaNgKErZmTL +l/Nmwc/LzhJIljCj40OHIQfE1Tb/4wiKjy09t/HVy42O5r2QeQ9YvTQmAYBcE0icKjAEJjQqQULg +FJGBpE/vuzBVcmDyCIgM8de4O3saZcXhzdr/f+bBGM0hydt631/E+TPi5FzGdnOXSql3rBg5RBHh +otXVPUwp5wd33NryhFGpFQEhPlQrFU06iSGdfXn0UHt28emudbQLJx0wTGE5CJu2QnZht5gv0ZYc +Dh/wF0SHoASmeCDhvGSFJVFJFG7CkiIkGCQ7eKyTgCd4CQSkVIYJEAb6uYfg7G9/LpXPo/UN9wd5 +uXF0N6fn+f86X1LPS9vT3efrAAHlmIABt4zQMId6h35N3QiNvWTXJnWTOsmdbCk04YPwDYLRt5Cv +aOYH4eXOXFE56pVYWCsdUJdM8kPIS1rnLva0YFdRl0RyXtDMioKnemTwKRbVU7mGsgYhiQFcVCxQ +vRWNhBwDRLVMNoZepmNRJozDiMoyreY1Bh5grMD5qZmZw5oXsc/rsc/jPe9h13DufJDDsjxtHbqh +rXioPg3EcmxW1+rbXfJMty1p7iXuE5NzExMOXJZbvUm4LW+bjWuO6E06fYT5aW06c48BJVbzg+6F +WzS44ytNwyVkcmlq7j+PW4pcsT0aNU86eU7zEPNoVJEdVJVXIjoXnQ6Wa1k625MJ9eOhE1t7iHFm +Zec/bPT32KtnB0O6I7rMi5oHehzS5f6bFPPVRcB4iYkj9XUdGBq9VDaruk2z2IxTclU/tsTZ1Niz +El5VTzeU/n7QQzXru9iEtEYObnu45XnudvVyNbbZnzEnQ74aGD8QoDbEfR4yBMOISznmXffl/Lxw +CKq5xvqH5Hq2hiQ5DhwoHSBJJILEsSzsgkw9cGaCbft47pV64Enm8uj59E/AYpRTw55AoiNIap+A +6y6yLhydJ9OUZiIl5BhBamXgE1V5bVfJG0VvNGszcrxdLfNG0q/63olgBF5BdpvW9f7A6b1RpxYr +J0FAKEiLCKev5Lh5Ah5W4MFIO21wrl1JkYJPe+vbey3e5em2k3IhIoLb7eXEfb7wpz8SGDK9z97B +le9BcfH31wR5FvU45PCez7V7x7ycLEeAtKFKJk7vBCDgQjg3B63ceFlxA7EGc0Bi9Pocm+MGJpgP +YdedjxgBXXnbj0NRVEHER3OV9CPIkKFADxIR0ADpBHSAESxAIGlATQtCNI/CEv7OB2nTYcBfUcA5 +LSaOJJNTqgS6gmQEQ6Tcc83Nvh52754S6KvNuSZJNpspykKEw2MwE1tZN8Jfu1cpscTnynbTOEDj +OijqtxqGtWpiFWQW8C7iTV3DAyULUFgjKmOCLp0Bx3G9aOJtTZlNprWouguMKMXBJjJ514BOUKSv +5xwuGcCEEQCokUBFFWb5YdeHAIl+O1IdfYOwwPYCKhaK3WWGIYV5alGDMK2rZT9rdrKJqQLbN/t1 +qeZklno5icByz2Z3h7qPEhMu5NTPVZ8/3GjW7os1DYJo0bBdnBbNbwQoyPF4AyhRHeENXEms79+2 ++evI62lk6QNIpycm+soeA85zSzEFWC04bm152mxu3KQnLdOrJF7Tp5O0vXrw66fz+v/fsd3uzz1N +yfYm8pGh4+x7+PTyTgXNK8PRgGJGJCmJGZQoKBoF1CGSh26z4ZyHTp0ukYVSZI5x+ngWaV4OTw0d +B2cGGLNKcGBSHR0IqnvnxT89h/YnwtJrqPzHP3nv7jvQg9WAUBkUYwzVv4ejQnEqnDNcERCkSqEt +CqquEAIpcTnFPTbxtpXOMTX40qYi8MYSCLPfSB3AJk3NXDRGWbWULtfhol9Sw0RCm8FEdXze74F6 +/9rPl8PiGcuidBexfm43af3CQDqkAkqQYHyi5w9b6JqKAnz4qglotRVNBxogo2I6GY87oJOmCHuc +Urzv4zI6hfh3U6vJ1Ces3NWrTXT3hzIl6TcMAptOA4NheE0eCaOuqJt8lllGWcKcK1Ohr6JZrQhp +jvSZzox0GFMsmKax8E8CIta79845TpzyWTDDwMBNueWxdogLZJ1oYwHctawqwbXkbpvbXY2TahRQ +Nw3oBp3BlEo8AzIC4bvYEswGAVILM3fVWECW3vYzChxiKAOIw5RjESLBS29isNdfx8//H1bm6qe/ +lkVMvrhMBe7j6m1D4yb0A0JmIAkkdYvR2V1yYx3bPnBHN4EESyyyI9YPDw3hIHzLLbE6WadiIC90 +zQrq7sYdpx3osYXDXfwgMoI9byBznaMWeUKqCDpgLZgAFQ/jz7G77v098D/H6qyYE2/7wP7SBrPq +2CgePx1O9083CqtwKqoyuXGZEauNajbvPGribenngqAreshTl4srerWiDjvFZSIpO7iavlga48AT +rm31EyYNE9bgcHWYn3m1lVu2rVZFW5UaFRLpkBUMK071DZaQGVAq1Aymhsh6ujkmlqjOaRqEyjVP +cI4NELBL6Mw1aoTnff0GAfd+n6vPkTvxC0ZiFPz83VejiPDuwmdB2L6KHps+ikxvwr9KelGvwDN8 +KiKLuturB8Fudz3gyBi8b1NPUNqIf/ppRXfzIYAEEMtHfTX10onrtZyTczEdVGRAzcKOAN4BHbIG +Jn5M5ZyTkJv56QBiXYPzyiUkIbuvPkqqkxFPvTf3QvQjsgfz9fUhgPUXzVhc0nmUNumoj6gRboBC +fPmXJ9fROKGmPr6vgzvb3XGV7dGyIfcx5YVPiHpNAtBlabkTyKjHAmSZ5kM4UPzoGtDjkVPdU89a +jg6I66KAsukOww8sxLEFiCxeJlnKLcftxOtuwt6dv9Cw0APszx5HnsnjejqvAmK75m1zv7Bye/Ns +N0Ll7iQPsQgYe2NnosnRptbTey5EN9Y107TSABgsP89e120geiZ/Su/aA/e+m+eDPHtOSaml8Rek +vsNva4DpFrw12heYcfEszkUe++8EsHxlojPg4Pi9X42RlYoES/Oi+k5EQcnkgAggFIM7u4CmHYEh +pmXAnmkIUplt8xTcNXWPQ3sPkuzO6b+H/L79f6j9jt4e48/uQbfzARxkjZpezc38fNf59cR3wqj7 +N7S7IOX5Gp0U7oOo+l+cvl+swqkE97Zj9vO+2+9JX1AZIhqHj5Y3ZLc4MgDvrjdNhr6hpmEUJMPH +f65uoYfnn+Ng425/WrHjcFCkm9tydf1iGHn8TzCjUejZXbiWwU9i+MckIRb36Hx/FVR5avuEmBd9 +n78sFkZn3OGtnkkRP8/LVrzVEj/a3c1OZuWbRGxR/67Q+TN1BcS+14drK5VJVzBzMFRXTCyK+LsQ +6d31nQS3918OyXevn80wTjy+bZg/q7xe9/n8yqKY3EQ/nR78uC2/XZs3XRCfu/Z7Ao9vftt/PTRt +Xx496Dr2DTsyQGCH7kJulQOAkQTAPQZwJEGEDLOBYBg3jeAJZXch+rebFGEt0yy4TeuW9JMIiFe4 +yFQ+DRlr0kUR09uVKEibi/9wNRi3vc5W5nWTO8mdZuwbveaGSLnHVHdZ1+PTnOK1wYWeUKW74VV0 +y5MQxjSm9MROY4mnMCg8PGm1pzh1kBROAVMNTsLmrawTOrF3Wg2pFSXnSmAZx3vHFTkjHV6zV3ie +fu/pIGoYfn58N34cdJ3ayHIEGTMIesP5Pi95VICH15y3rrVdqP7+2fiZ344EwnX5Z1LMcxxZUQ7Q +bIiEB6v2q2eyLrtcxVeLx68YSW5b8gLtdmCxIE1JdhMpqpAe/JmSuZLwxB1PjvuvHvt+26LkUWTx +4gBOg0dQ4HUKi8VMAQWrrqb72/VhHdGt48Z28mDxs0ExI6MTLt6q3aDZCdM8VcBv8Ju4A8Q+dOF3 +PfHEAX3m6icx9jZSTWeiIxAf581E7pjmnYaKpMpmoAUY4QnqeeYkveuCLIKU3BtHQNvcNzmnYkCS +LMK3ZRD0R7zLgKkAiN+M6uBQ6qARwR3t7x7idHF3qb4w7PUoDeJnFEmFDg2gNHoc5MET1D3D8hFJ +YOMukGVphP+Wu+zQNrpf7sYH5QBcaLqXdYTHQryJ8ff05J+SCOguAlZcjCZzniEUIOdBXhpi9gvx +nGHlEnjGBgqVMA3A6Lp603+IhTJe5eWju9Yq6OyFp6/Pmswc31ir/YvRaMeNK6+heEaDnv3ZOrms +E2gy7AE/QPd10gcrPjw8lmHjzbvQy9UAL1iASG90qGBCfhAcQHFHEEgEgMbw6DTidFMFSOa1H9Xb +mGa8J8XLVdzopGz8ophZYBdizOAhd+5WiwqPSni6lQcuAYduvDhcsPWGK+mWJKo8FHMLwFiEa9eM +CeaynPvxEInxDPb205Yo0uDx6Cx6dB65KjiFu/m6Y/Uu058nXLzrRsc2/zli9I41hGjQa5/sE5Uq +qFau9ZqnDvq9RWojVxmojV+usO3FQnUYdQ+1GaEOnYbmDdOxESaF7isEvL6FXOW41QIqLfKyxRtQ +Ld9NdZh0IgJ9AVCyNacpBsYPqlkIC6D2G1lJVOq1OnLasaFEPN5FZCbBTxL5qrmEqyNY9jHm7uhi +0t/h+n8KDfoAH1fs8gh+/HgEcNKMhwzx5mB29k348zN6F1iukX/HwFuEMo5L81QveM3rHQO7zW97 +mREXRXfFKKJnHrFx6sVJmLO3fK49RgeUSlA1CcxBKKf/TQ28P1jZnJkY8mrfHT4Yh4hQU+ij/c3O +a6c63viMpnlUOt3rmzEu5IuD+h43E1E8zd4N8QizI3mnwrUE44rJh1SiXLy8Pu71IigNUjAt1mng +v/vEvPhko1zBI6TQjR31s9POTspJ3Y6cOFlipQEZdbrDO9pYBsqJiHvb73t3GSAFjynklsMl0+sD +1QFxt5utiRYPiLgTFGZU4zItlg9NKIgzJvkVAwFkUKhfHD9PloE41eMnhKPztq3X+bMemnPYpw6f +puWP9+n8V4xjzhkDJ7gJzduXJkbmiAxgdPGIfvvm7137nd0TPVoNE9vxPV6/axxGNpHRrI0IYLs+ +HRoePDlPz+vVA8DH6x98fS+A++vdisJVQQQRrBTcQChQgkvdXebw09GP9yes+MgfrLft+xeggRE3 +oiEoUz1wTEQ9h6+NPH+GB7m9thwYnoh637bOuyj9ySiJV0DZKwmMBSejbl+dVM5K5Ofh99Vfvvvj +0+WZEMwxda9w91/na/E5uyxRhkv4opsKu+44nTEfjQI6N8UC5THYzZ5F7OGDRvC4oQ0IANZC9MnX +Fn0ul6y7iovtHCMa28mn5tBxcoOP9CLDgUQ/+qQJIETfpLx35VyXvccqnYFiyAIghFyQZeenmDKM +3/RxJG0tYnfi6P+E+dOX/6FPtsdUIYz5cbMefbpQxgln3GtyHN24Mr989+a9dXWKY3QvMAqx0XMT +6OTG773AeZLhIpiQ7iyC67112/Z7EDrfXut7KHCR24+t/tj0Ej0tVJHQGn6iVDvRp3yIL+J8fKHk +aQG/np9eb+ffjlyT2uQh7lA3SI+fTverj5VQB+PxLiTCB8Po4jILpIIkcz1iZJRBB8+zs/r8kPb0 +frRzQ4ROj3jq6uHNC+igMT4dM31txmjIn7iTvbHhPME095wMa1tuyd533cou1U9Jj7Oc63pIQ9MG +o3AdoX0TqTXK9EMEJukxP46o6t8KAHLjlchvaTW/pwLk3HTq6+IUOdwLvHjCh+PzXHsm5S061mvN +8K+V+bqncC4P5nT1dEG6XhihNJvfkOn1V8QVOf66+i9ASH61SCo8te5SUdm3BHJx3qAdi0mTp5wE +jAKIgMbLsgaQ/ojlWSDonH59kO+w/iYdg2Owtf68fr218jxQXoJxD+x+j9npulsW/EwbQDsid9Yv +voeHlqA9RqW3/Kvi492kfdYjz9p52egUGP2f8r5rz1mFh4HsepgPI07q4HyO8CC92wEBHOuSWOZB +ElM8ltgOxRjzChWTtc3lmFfoctH6NwBrk6PhRn5q++Pl7RdHcvrepxu5SANeoFM/X4Q6YM2Osd/X +CaFJdCgh0AqHu/iIQctE+iTFAdW32Z8iy+q13ORIfnb+x7v2wyMFn69i5qdWb9TxHpxXd9NOWvpn +5h7eYDhVs0Tp6BZRgouwL2kfs8evv8A6KsZ3T+O+/c7d0ahxREjJwhXe/dWpKAAzaGR8MUPjIF1b +auMn8TIiAfBADdyrIe9ZRoVG5Q73zQi+tEWOrfDmfHOL61YlThuhM5ILa/zpf7xaKaPqn979UtUr +WK47VWfsgwdP2A2tedPDNotYTReu2dH0HJcvd5cpXAkWWLj2RS0fxfipGYW9wjyw44CvQc76+oVj +1kzx9tcGGpHLo19FkcnVvQHyTf+AHQiOuBpOMqmPuHd3oB9iv8xYc0NpwEIJSN4Ayd6MifMmM7sy +6Y77NgU9ABfsCuw2vyjpj3288wwESGTaVPdn7wB7Os0LNgsdwq8x1l46XdxvT56B/bm3c+mj0209 +/tb9zD2vQ5d/d7d+zR2+3nMoCj1KrgyIBx6MF36047evb6Df+eHZ7+3XdnzUbffjo6eHKQjVNkXT +VqTVk14Gzi8KpYtvQYP5upc9bxWPp1rGucR0YZZovjTbu3SwH7rZ8+D5//J2TXbik/Wfsviw+BUQ +Xew/ghAxxdaQYg5w+hzFHHYYjAuHcsbKuWHV/VIm+M+Rorw9JNNconshzmcizc/ZuvWkYIxfi5eF +F6EmIxiqyJ6OvEHrYXildeERVYKdf17SXvCU2MkyqTM/7pjux4rcO9+GZZ5KdF+QRBczO5IA+KJn +UKiECT4+pKbgFPhLYBJIR/I5xjD7Ez0RDXw0jV5CUeYCaG5yPDOo+zINI8leZXvcHGdqxqweejU5 +NfZp0zhQHttKdOGPPfc24xnm6vZ1dOn13lpLprBJnsPuvDIH4MAIyyr0uE7DLTSf1kWSKYpH3Bo4 +I4VZ1nl7IAQLkGzPHL1WwxZaKVlejjdbjVvbDuH3H03f0gUvAw8vD9gkoSENWNgFIGwZeyD3UnSO +Qcj40VJFGyMX4OAgnqoRRYEuYMJBAEEFuQaTl2vsI6su6XqAv/0qIn+gDCUBLAKURQ3C7t4f2kyR +Si9g32sQml6ex9fNe8tWQu8/v15hkHEVbrOf0/tVqdvFiD+K8HYMsd/CO3+vpi55oTWyNM9VMlah +kicv2Uq0gkcG3EaBz0c0aKQPf665qZYG4exZJB79qie9HVJnn2kUKnzJjnqMZctOO7thy5vjxCbK +926ph8539k/sRhGghbsa2hov4n94fXFfJU8aRixmeLN3s5OgEkkEibqqqPv50d1/+b33jt6injrA +VZrKe6B6XXy4iMhF+1GAXPZi3yVZgzAfLmLOBEZYb6sMySxu9rncrOTd/ragnu3VVKBWO2JAJw1B +3qIwE6kjQJNq4ZsNtaeY/TdOHzak9f+f+pwROx/Tb/DP35gQSCClqhFKEJVbJPVaKYSoqpCqCq+X +t9/f9vv7QHCR3aHwMonE5KyQbEFYQEScFU+fwAIxIRHOwLdbRjlzuqjECLYp/LTgFAAuKbkMopDh +1CSv5W3y4LCdXWYen2679/x+m5+NGETdxz4W7fDFixyY9V6nM/In9pKuYSyn+t17dX/D2CW4Br+b +aPfVei17sYPf+DihyUDgEy45x+DFXOSPhObJFmO/Vg7UQCpE+Pb4t8M6dFA7MFNGKWzd1/nX74ko +6I4tITgAUCDs/j87x90H33BP9owmHCNyOpDQ5rRgZUS67iZFmtbM8i65ZXh5AiL9BZf5UM/xuoiZ +ts13sCoQqAaAGSVWakXyQHT0ppGh+E8wcVLhpgXyQ60h4Qp0iUqFBEgnRQ6BXiEFoUYKI4FAKEkz +phW4iju/inCJe3TPV5mXEwHEv+oxS362MHCk6+3+tIqCJjQgALkuRERBLt5cd4G2TcO5UxDo2igO +E0xlY4SUQo4fbtMNIjKYhlFs1DWbH5jeIZZZGifDPKKU797bp9bSVL6D5wgakBTJgxpfCuw2zRLi +xTYhVULWXsIddNecpFwsQWjLVCT3s2IWnI41ZuS1IdlQk0PfEuk8oIJwXUeDDfsD2nrEcoVQqWwp +cqcaIr0Y2Ofl0jZK9H3ls2Rfv2cT8W4KXIPjQtG7Lf4+394k8+zFhzviSvqxVg7SKb5U4lKLt7M6 +cR4QHV7heKBCAmCoS2uy+WGZO1+3ysdJLDLm9shB9yREMyZyo57HnKLw4UDOnGuJ3DX9Y/kVx5hE +a/js+r/P88/TIBhx0K2EpUY3YtpdrikkHK+o2e9WfD8tZRN+yMca0g8uLhiQuv+PysfY9ATNB3OX +/Lncwf5dPXYkZBV10OfCaaqSowECr3lpSStD1M3aqOaJj0T4ZR498vjVeHjUQeBApChfhyi+XBWU +JTjRJ/J5YcCJQZuzPetjIGm6/6SeuG1AgxXw9CKyiDk9ERPow01cM+nHwnjk60DQJPbFiny5qua9 +Vn9hv87yYR8/zFgulV0srnjIDJFy4s6ln0eWaeWgYBH14kq4vRQ+/PJV3rDNLC311Dy8Y5B7VN6q +XB0k5g/ulbtd2mMR4oBq73YfCiPB3anSHBR6aHXyM2bJw7HfrHzDMfrlg6NvY+6/wrFZELdBICG/ +LyBvlxHtcj3Z4zqtOl2SWaJTnzvGMRWe1G3l/O6sbyMv3gzm/wFYF+sYOq5144DhBjMkIZMXLt7J +8ksPTb67erJeFl/999Szyr00qOfD6hUIizAN+15olARfMqAnQcBACR0+m/8EFW/yrTqxny/PPqB+ +cVIPfkHgykUX3OT9h7psWbRbDUPn6/Blqf30fVCZ80f6j8OXXoxCj5RPuBqIiPQ/2HlP1EQgSnY2 +KPtm5oxg3p9gjOn6fPuVLD93TtxEBG6y35s5H9nfvrA1VVc171SL7RiHHmPauNkcQdoFv7uCNe35 +apz/HY+5jany4jHDFUtzM+rUNIYlUP7f0DNTupOSD/nven7/Wa1W6g5+w975qng8eEwAz4hIL6JE +iASKL9MIuV9djX5g7ILRWhcioUYP59rJt2K1ty8oZ7+Pj1kPCof5ysSh5DwC5VQFmYQArAuBSSq9 +z0ppKV0pX7Ios+jbzakKe2n1Hr35/2gw7G+05dTsaOqQqVJUPQelft4Du6BudOJ6zl0oZkG5aOc9 +Zq1tqFyqVKsr56plA873z+tHKQ9DpKzpuJKFWE6KiXAAkAAhBQjhiYhapIor/V+kA6xl9nB9FAwI +MOPGrMoUXY6tnFtm2s7SZzMtsoldveldQTz67Zc4gOfp/VUPvv+fEdPPwx3xpWVgMKriLIwZmGRL +DKZc73EzVlUVjlLlL671/ZPd6Po57n6vH7CbraxtCsaiuaZieqHv0o9FAwg7/Ca3YDaZnKmkalUz +kOiUPLOV7M5iHBwYLLemW9mUY/uvPooppoFLrwH2+AvVnVvVbcFoVy789t6S8+Z4jEYVV3dcsjrd +XhE753Gjfu94+e4z0WNtKY44Dppju42LYO+t6gijt2836tkx8VuTWTvIUFU3nGsW4B2FYIIh9+lW +W+O/3KbKFDh+v1Zk4whMKoCcTPf2aI93viDw/UP39oG++vC3Km92ibaEmd1nwnXXFylnlEvCOjRE +758fLy6uX6mouEw/j/bRCSKGGLmX/Ij/GzNhXq7Jf+zy2bZds8oL6AqkaJGo2lxH9bOi79W79nD+ +SmD+/M2+6g8864vpX7SuSPxZOH9ogGQhO9VEfaw58H9NBCAMpPT36I3/8H+p37djxKJbFFR/l2G1 +XD/FfJk6F/uySHv/UjVt15XJqlihhYvH9zpPz7KHyrFYrdN/zd15VNS+EvYw2fnue676FEVREsyo +pn6G5yxcDf/uyVJ01jbLe6crdu+WmZFploYTkIOw4TJQolz5vvg6KGMUjYwlnin39bAwF5t48SHP +35o6EboH+QiEZQIKSiB/8xXf3il5uhJELn11SY78OBVrYoB4Si6IQleA/qOZ0L9Qi2FB161SIEkA +aNKpJgVKbypESDdE57kFRVlAc9zhsSD4JEQWim2Zx6T4siyiMuxyudrbJr8/7/OpW5D+652c7Jh+ +4xCmcSbvbZ47U9NMSYYaOImdJ8tEDAECuUQfpIjohxHcOfbHdPX52O84H+ahJ4d1+9dTFgCdyzUB +9XjhD9uCCJ6FAkhRP6Kgg4FH9wY1v4rIhhUYW0CWnRH4a2R47efy1YH6sYHnNUMEDrhhYSlxGKJU +iKItptIzFyDqZUE0age2nPGL/BeMU46hnZ/EUswIASwoGi4owIBRAKF7eHPt4TDzyOGTI8RTuKLc +qBySrPs621ARYm8DPfc6R4RHXo/6jPbHEIkwxAYx/j54XBr0+nPLOg2ERjQMnkuBwtncATf4eJv8 +84kICWKPTvcgLafqbr2LnwCTlIMn+EXKTaom7b8bhzFERE/wRM5+/HM3O8aL+yWPibK0vflYv0iQ ++3C7gi1q/dywBpXWScnrKYCcdvEsjQljGj6z9T5KR2Okz0UBBovvT8+goAYog9J9HQNiX3TKgApM +x4IpAElEeWcM2aO2lygwHF/pa6UZXYe7yWA1n2KANEuAabzI+02/hvM8AOIBDhnO5Ii4vVQYnui5 +3oSRBP4UzmRYDs7xKUOahESoMxluPqjP9x6cQJcjOSkkggkgkg4yoL+bJU2yyuhXAQaurRBy5oxw +0i0iZ9l/T99WKTDKBGaZ1E5dyiDAQeqKQGBY+ZGDVt98F4DNp8eM9JTIfnm3fn8qTx1f5cvpIwOG +rgD5/i12dvUsfP43d/rwx+Ky2/tL47LOinJz/mFAOvbzYemzHs9et28d0XKcZI4deMDw749SO4rj +0Ke2nRry9n4L/5L4SevRJRUmGQLOsWU+ve5Ko5sQqXmvyok4HYUkUz4NSSxUBgol19Gy65nyaq3T +X5qYklYfp32MGCSgDs3LNHX+ORzp4o8AWEHKROgwRCTHC4gRTqn367mY+r1D9tabe3aCw8+E2764 +7fVnH84L99IkkiSaJKZJhmv6/hvKGsZjzcDCPEALETsjR+cZSknV8q8Umbn7XidKe7q89C7hBms2 +gCr7UNfxdifEef0eIp6PrCM4SIj1DoaZR8xO1udpIPdY3f0BB+aMO4gEoPwXxnazBiAoMBv1gBeG +XX5Rv3WeHGfw43uRb3rq5dfvX37wpQC3Vtu3cVHSAqDKEwdHCMZpkQPjWnT6TdZkHfRCnFLroeuV +JGREpowATdh05jCPDA0xIBQD0mVBe1ZRTwnFn5jsyxweRjjnA6x0uy5d28DKHzPtxI+J4lFDs4A9 +0itnItbXmiqHyEzofO2Lw9MrBkEREfjoUajiy/epyOeLzDm7bekVMg+xRVHzdyALQDEDaBMUJrYf +JZ6CIEQiefuCnE7qXVfNmvde6e7hF0jv2SkDoc3YEQDqUPReCv1Ey3lcDtwQolzxTNmjcoVgVqzb +IcgCki7uY2y+N8RqUcl8CLA6oRi7bfVqcDs33w5be6XdLTlQVXXMVcQVSJ6VSECFyuv+jCex8yIk +AFrt2xOQbpvXlKEAeRkI1Wzc3QPdQOaSE4iGbePPreIs3GnFor79sfvMJ5e6caOnnvDKZGT8kcwe +UIUXKpe4kDAgBdmsi3MMgoY5rbnhhubQ+XW4bFHb9/+bMH9IvnocSqftvIbX6OX3ivo+fvPHtOCE +xX+I/4IQLFTFObd2IprUupUgj5/EfAx8few+Tz/XDmaOxbHt4ZkfRtc2SeL5ByzY2IG1nL/BD/P2 +ZNOxbuxTGDBe0CsqCaBLVTiaUAGj7hu+mh+HvG9tGUX8St49AP59vasE+G+TP0eLqQ+72j5/BBRF +2zA2M3QDB+3fONi+hzfjIzZDJmsbpw0d8N6e++nbzzzTiWlY893eeve0bzl8hAQTu6YGjE5IT1Lw +HBBh3hBCSBRE0ZFhodwVaRNp1AFTIcfrNQ0KniHtRMh2GhmkmngwBEcc31vmybPLULtbvdslU3eB +u8phqoLNt7mQKDvmbtQ1maDRDz22d6U+9fDHx+Pfhmr7Cn357ZPvlEQRQFwhUAIhWFUwXBTxcSol +oeHDiAqVmFsKy6dcim2natcR4pvIm2i7It0CxeGeIjuaipKEMmpTAQLSZYJEm00M6CIQLT8+G8EU +avBCZ4YJySIKow5Z3n67tz7vyncIvPrvOPnwb89tD8+cfKqiSQqNMH5nfbGb66fze/PfV9qfbzjP +yGNHvuve3ZPvQ4PXvSn45o3MhEUQQ+GywR6uzDh9oV4Jw/DV5T6ckJVdOzlmFLnILx74LOoGYU1y ++/lk6BZNDy59/PFbDDKiSCnFYMV+SPOXCNl88sYkml0Tlcs0YMYCxB8UIURCmnGIsILyyLmIlGuk +CeIGRQqgs/oaMXtGGSaLNCYCRACJh7UKkhAQi4DoAW9R4j1v6KeFoTpMlE3k4YUE93pl9IS/bV6b +lf0+0aAJr40yp6ge6Dh9j1o7qhnKkkE1Cdqr4HPUOfuWSMaQJHePRp5UyTCvX2hYZY/KlB8kAWbK +4+t+ZEAlsGHOMm/w+I0GwjViVIs/XliRdLr05cOlmfzsznqqq4plbKItEkzuDVxcHq50tBlFY8Qi +fACchMOgYEHxTl2S8aKKRcUBKL5d6Dt5H8Pq6w/x+d/bSHtOr2U8mFEy+BmRjlgoKM+LxyPNbt01 +mmGm/st/bawOIc+ebsNknBblOhKhul7v93XDolS5akkUlRQWmOkSYP2SEP2TyGeUIpCCKvts9mxv +doJkqGYjoqP9f47a1Snf272/Nzvho21rKNu8MThXhn98F6SkI19gZgR2pMLyBB4eYwS1FKwSixKC +hYO72d0V7AsTBiwwcC4gGD5yKNSLtRDgy1lqMTVRTwRLGgZSQDEAktAiWIl5mU/4IQbgUroTOHDa +8O+ropu5rduxdalTJgQDvDuQ8P20Xz/JyzZEZbTTNmbaLBEguw2aH43c7+DJoKSgrrT7D0dNayfD +0n9SfJ9e43fesimQueFkhBFcYKjkpTkzTIOXh9WuNrNDuUKJxSy/4pszNVX3DrCUtqSoFSaExmku +xT2s6AoDR9bFUxTxPv1xx5g0fFqjop6TpU+kHzjJxRzkSKmg4gTkl+u8uvjGYn4tjau23LbbYNJj +FXe3dqPFmMw1YVlFRMlFL7sDQ0NFiejk2C6yfSke4U8PBBkPxz97tulX8+MB49u2KBHV22dYOnWS +dMoZtYtcZW3lzumrjOhoZ8ukOk8n5g3u88vrdTo7yNtrfe88L71Oe6e/V2gi/NsfidmVm+DtyiJM +2TZBTExn7M1M1EKnLlrAeJAp2oHwZuzZB/Kh0ZJ4KM1bsk36c3k4BEmKJiIRAgEg0hUXGMGGC4hj +cCewwvvYlAahzCgXwbqfv2jan7XwF2A+8PI+UBMHI6PUqFIYFHSN1UxpTA34620GBIDKU+iBjdn8 +mj6/Bq++3BgcNHtZmFRzfEdWUqF785MfwdJRkIQkRMjno9nhzcHxTjbYU24uTGVXG5VExq5krTMQ +unQDpO7heZvEzHP9PaYgcNFxbp0Ag33OhkNDc421i8XZNprWYzFGpWTfbqEIdE/iP4q0YTa7G57b +nkhnPFD1PB2MCBLp7Tgs4CbXanKuYAOyXNDLOmXrQccGid1GtiJXu4Lbc0OtRuiF0bZdlsOvt+jW +AS9Omdnr+ZZPYbAaywsVDv3DgJt4vkJ7TJ0+nvg96HvmedHkElz5BS1VHvZ63aTZBF22zJ4vGWNv +JnbkdMBq0WKvdSQI0RRLkNvFeypZHI0ZJLwCRUNcYY7E5NM4zMN6a41pJs7BoyUunA5XCR7gziSG +ZgQY6WiMr5vz1q9rouZOx6jPen8qL4FtrbpOOjd+WZy5xyAwxjBPiTn7bmv9KhIIJaGlJIVQFh/1 +V0TxtcThM8ahVOyoTkHXm1XTeUOP3iFHPuyUDzw35aL1+ntlw4BgAwKMLBVDBWCrtq42jayGTUEh +DwvTUHOZJm2QrRbz1mwrYIgMuKrCDl8wKRYCTLGZTT1B6WJaXk3AbWFmNw3PNyfDDfhiw4W2Y4wc +VEA9oeoSufTJSCACe7r5voPE6U2VMqKoZSgZkVh1Ya4P3vHk8h4ZBSdrukXPv623TgR+e2jGn1Np +0Wc9+vox88n6EvjRslkrocKeEjXg/46NEY+MFmnMJxhGHVZ5b8PABNIo0fo6ohPmh0m95TE1G6JU +cKOzkAH+oQe1VgA/fDzCIfD0DnAH8f3ncH7Oh8MhAFt6MpeyOa6zLYShCUbgovPKBGS0DgUiIS25 +BTk83Sm/FIBJsik5ijQPGT1Ht6+k1/t6WjKUHAfjIo6JIIiGC0UhZ3T7OF2d1twnCIqA3kDkw3xU +e/Nu0yIizjfkGZ6AVVKA04YkFCqKCpBHotzorMDINzvyq/di8xCLIfTwXGkFoBRAHlGIVEQvMAgR +II8XxdA0aFFWSv4GrL010f1Jq65NVBm7BPELoI/J0jH54LP47+/4+l6f1ff51fvN9PTB4+E3j5oR +WUyCcY9u+P3t09fcO1fSvj9cRq/ef8/Wm/zE4MLe1/wGbH4OgfS51kpJBn9kkJQgkRSUE+IAv9hQ +ffszykFpAoBBoYUDnh37MfO+2PYAJt01BJng9guwp7/KcQfXKqFPcd9ZNBAo16GGfFSiSoNSivIL +n6AaPH3JBQh4rFgDMEc4KA01Az0NWMN2XsH8FJ96Kda7NvXs+em1bq9DjAIntJKiJgUcwfwp3DWk +YffiyjNgXOiJWoAnBGWqKKLklloRLK2v1JjoEWjAkJMgF/Wu0pjLJ4ShktI2h4zqii+M9cSYFLBX +OS0OdhrGlJf0BQqk/QIDJ6DfzsAE/ggW+hCqqk3ImMUTkACdVebZRZYUFk0ZSEQiKIlQQigoLnaZ +Oe3SB0BAQMtnG+AybhcSKcsSTTDovjWk2DQiIzpkGI35IXtYopf0ZvCzZj9LQFFaSrUZ7USyZ3zV +yzrbgEnoUwnXXMKKq0X5yiLwvS7OqWgEO44UTQkaOULaiiwUS9wv7Yc+iGsfvNXUiCaudgOILinl +XjkyoL8e3NrYsqJfiVHEDwYQmwDhVFQID8He6r28WncJGE8Qj7XCcEddhwGlXRdL2m8nueNAJ9Jv +35JFADB0yNujMqZfOKKscvDXrAkuv0U1W+CVAoyxrHmpJQdNfVFhF/dYAHkqQpsUAqqKzaUBGxu3 +hRfsRVmKJk0lu72v4A5EeEzqkaBcoZPIhBqxinWwA0TT+XTs9KLKo2ASc0PwgJjCvTsBAqBcjs4V +EQAaeyUXDDqOJ3aJkmrsmCpEegsMYrSOsPjCpGHbs5cg5uCiEYMiGv1C4hk4NENIbvEqU+JSesWD +LGKNFffWPA+o5Ckpi1cE8nc6ECNUDT7AI9W2VubpTpTaXTT0Apv5/jtg9EECQIlXYqd5CACRFIuJ +YFp35n/06e3SHNWqgAfc0Pl3SOPuHyPNCPPnXjsKCiXgo7eh+rn6QH8QBWgfUEmt5oo893RIQCco +Up7HyQYHEvLY20L2aafDrlLVjCKJpmwUkaiNqoAEEcnLmdvl8PfoeRcxDpdYFEiRj/l5k7X5uPmT +NuKYpqbE6NKh2SIRyoRKjEgdKh2sPUW5Xo9IoDSLQQ4daPWaOnt9sAUPdzllj+40pTv9O5chvAdj +zVig6cyapZmacmlLZoW8WGjTR3DqhQUinVSE8AKQUhRgPDPMgSARBaoHRuBtbd1N0lEhtrCYbGpo +aphqbI7luUDRvdDs76NswGmbCRWmmDvtHaaeQEWQdB3LGGDRB4J9V2zev2OPl0VB6zP8rnphL0QH +xqf4zcsU/dr8AJ3Bv24IwIDiO5vnLhvYL15QH440xxpDEvEvEowMpMEU7PT0AxF7+3SJHKgAoICC +UqEVKoe6VQ1I7lQ6wuy8YAnh85KamUaFPr0/TTIMTT9SKT5knHD58bMF0fT3JgtO7lQ4dAnQ8Hvu +g5fQOgNDYej0mAD75pQQ2ZDh+AXjRNru4ObhCCa29cZ6LHVmMJljto6TpFDpE5RDdVHGgDI4sVJy +8KiPaO/UDwnCaQTY24DwK4+OIKQQmEQm6xmxRpasx56pxEJBjpINb50no7f7WY8y5AOiLJ34IU9D +4iM2Iqt0XiszwFJQPQZ6A/zz3BggQDAHiKQBBOqeFHUtGqcEeAA5g6LQMwRBl9RlgiJzZEEFZAFR +PPCwRBrBCbgB4xu2eXaYNNfet7gBSBnnUnc4MBh5z0ekPUZuJf1SF95z/DxowSECJ+33Z66dNUgt +ANDYsaaCUoq4uBmotdXRxhHRO/dd3hiJDoMTFM4lTR5oh1VJF0GmFkpWiZYNUnRcGIqGWAiQhkgl +liKdOd/ZfWnETjdouDwD48j32RLu883wnSoZpobo+YP8af6l76++PzEnoNeR93ut2xycYBrVM061 +pQyCFtjxYG2tJpdtoVuw5w4S2b40al2b4/p1MihQpGlCgJhqhqqWhKQokqGhpJgCgoIhoH6OxRlC +CC7YYXU8uuuux7URSUlQMkw9wMXYdj1XYQ5+onqkn6eV+Dy8QgthyMnJqlHFec5OwU3JmSjG8bqg +UROlKjytBER1vwcHJ4prYNoC5c1mwWcOuDbGb5waRkwuuCXMTTM3ZWOoRId2MEsIDGpd3oRUw8qI +gkIyWDKgXIZou5gh0wKG6RFVFjdokqXdPntZO+Q+G05M6qJnzYBmgiYJcUizrrRhUFrGgbXP29F7 +xyEWR1ADkc+TOg8OFKaEPMaiJRMvW/X/LZP7vapl/GDL7vM+VN+v4fa/p07+BweGxU/aZDkQy0Sd +vWh9npOr6x2jx8C+ffV8S+7J8cvRy64zfjiVU+e7f0ZYQAmKDYRLZcO0XQheRL1aoqEJfINB9Ahz +C0LQpSbBkE6G2BoDSJQmJVDRSPxb+WywSGz6M0P7QPtxFaRoUJhmEkkqgpe0mQ0CUlIUpEjSlNDQ +pMjQFNUC7Ig5qS7WFwdjvUSCmAzdkXnvrpuvaFEYSKIqKCiZ5uEHkJBwkVDa77mtKDqQHonQfPys +NvoOXd50+lGUvg0YxVJlVVHKp19WOzrc4Z7bTD0jjeasIh3gDcJgJ1YoVogMQzKNdJuwGk6xhwsM +jQljUAUrYwkGANixtNScOHudVMQRRsaYOCGS3Yf5sHdMYzvggJxoijBEm3WwkXoi5pUKcppYlyym +BcIobJkZDIJgnABvsHOlJL/HbUFSiaqfqkh9bpOnS+7BQFnBzmiUk0za0usCxOwYFNErvbZzgZVH +oaDEuqtjfJjQbHRwExoOg6QqIpOl6ofe8YIiD6+h3PWDJ4NmJy4GGRgj2eh3gDAdjv5vl+7+94cP +b6vZ/N8e/29dED5SKdu2glgDT7uDe5cWB+/L6gl8BB0hfIXkHMLaaIJvihj1zBJSAlYVUCzQB6pT +Uq0o0UtGpB+Un7PYeOuPn+0Xsb5DlIMKkAkpgDnnsnpPGA6svimk+WGGPxnE2Rupz6FAQ7AMSB4A +IZv7sUA2hTQsRvEOweLZ0NPlQqdWuc50zbja8aTv4vfN5OEJosskrBkQRYCy8cKU2wxHBSjVO9pO +D5gNAB8qIKi4BIkyplK9Q873NwANIUICgs6ZWQNbfTDAYCQ/b+Pf9HzbfR9XH4c/167mUEWJ7Uoq +KvqQK9qH4+qhoHsJPEZifDRQBOQzaKxr68cbk1z9Ojp8uoLpJ6kBJQxKBMsInTV9nbjacIOdDbsz +DZsP44vQRPqXncPMUUkEQcdPykQ9DmYMWbWeIir8MNzKIlyg1vkiDJCMBkHLwFJaGyXCT0xgGEiQ +Q7iiogQFEspogJm2+Yw2EN3SRN/muzw7Ozo1oVwsU4t0g0TIuLIEkQmkEVNuyZg38hYFFoJYBaDh +LoUIxgIwbqJ/bp+zzfl93sPtVPk5+fJvjHWNktggMPkMwYeRA7N8WtA5J6IgG/CCiMOT7bvKA4SC +z/gtRrQ/6VrzoW0LFT/VzMygiPLzwfb2Db7+VO+lC+jtinY2qqGttsNOIosr87ME+v8/f/PXR/sN +YcMDokp3JZoss2bsOZUy3r28pyhu/K7nM6zpm4B/wFtLDWx1PRqSFN98DI4m4btCVCAZABDYYTbK +WlNzKdkNb0oyW/phLNuDOmFvTuyCCAISlsSYOhhYQCBR1oXgUKT3Pw2msKBxy40bdJu88MEZBLaw +yK7D6PZ/Tb+Y37Is0hTOKPfjYN0esNtKROIdcgyVTjMgBBRAIlKQ5Yel9xwzlMns2lN0u9nljezM +Quvm31vPf7+DRygNPcHGGdrOrqMnNOHhgYKnCa269WU32MnhFBZ7n3XSefkP3qPb5Jx4spnm5jx1 +vWwh5kNwgZIsAy6+m859aKl9XQ0ofHuOfHr3t8QRoAOAUFHyN6x3YuR2a/H2MPcr5xYuK9GDm4CI +Zh2TR2Xew+m31Tl7M173jR5AdWoBEdynz/JuYKGwPebviB9l64gdK0lAlObfXGuLzyzG0tpCpETO +fu6FZse8+GtbTpkqBtJ9fTk35Do5zbOQFIZ6c8XySKIhpQTUbAQndvxt12m4aSejfbW2UoLSgr8S +eS19IMuxo+Y7tVU6K1vcVfgRi688f43pPknvi65EFyB8JtXnrhxTk+GcGc00aNYcwyc855AqQUco +QUESnAd1UoOZwU5P7CJLM+DTuMCEmz01OabaiMK1u867a0QDr+z6TRz6PtDJ7+V7MPvbmHfLkN2k +LzKGC/a5DQNHzAaAiQ8v6xODuXUlAsFAfpPnjc786jX7r48b4tlSc7/598F32uCSiLU+sMwkwQIc +i+FJgs/vonRNCCi1G0dg2mLDWB7vHNCi+q0NxNM4F1QHWzUUUe7O9T6y54ziXGOXeUlBoKC2xkRz +KcYTpyp3aptN4m4YTdyNKxrqlzOMNgQ5J6wQ44bOHlwaMSJOHZMevFk0mk4Q0JWYQR40Gba23TW4 +b7SGB7vc9CJzXd4PdeD2nvzb359ZNfa1MA2zFApdprUNgdOIb6zmO+g22dJtwXVaGhUUd7XLbtcm +5KU3qIHAzKYZmcsMXjbDDbWRZx4ZcKmIbmpThvurvYYryTm2GUg6zK885OMiTGIlZ0SvTjbRrhMZ +iJSy4GZggjJjORtflpZzHcQK8MePeuTW4az3vbbOANGuKdEDfgraTklxLXkcFhlq8iCHbpCw24pg +owpqmMh23Cde7UOaaNuvSBxtHbDKlGMiCrpOXPZ6ZRM9V24JSnmsmxm4OAOyDbryZWZNYXNZyHW9 +pibCZvLMZhWx1S9EwyyaUpbl6zUzw5Tl5cTcyw5KZZxQrE4Ntu7RrbfNmb5q4J5kBjawRYMSzht4 +wcjKy/gKHQtgjSks5JvQAZbcTJ3b5HKMV6vUwndkq5cybGcsYQ20HImxsRNkNlN9XfW2hdtamYwD +MaUlvG9x1Md2a3htrUNstRVNctFmVN7SDEVRb7bUvVEftHvKXPz3cfsgEqpSFIUJqBSssSOemtKc +5hd/KXW50mF6WTgVDLvhvHI20t1mGU8qV69WYx5SuU+btc416r8iuzXk1dMTCURdMfeJ+2kbKR88 +hNJHXxRKNnWnu2NOdzD2TGmoaFFl7jFTwSCSmntMtWjRxD/EDxkgs5feFIFp7CACUjlqx4p/HIuQ +gST2+xAz+J+GWAYpMCKbcFmzqd8stwwSacvUgAGiiMEOwkoQhBD0CTlECKAgA6/IAUCeA1ihNMaT +eaxChOwBmUV1q3i8VEN2d1rrgQILQFYDDGoCOoKoDDd76xkP2OYKN3amIZxj0fauoLyaheRCQUKE +AoA3NjYh6S+GLHNiL59Zv1yiFMSLLoe57yojBW2hygU1+WWyWy/QGFGqT33764qsk0t7y93aR2UL +WY5UFIsIvkYSOchsOT56xhIA8OzKAOy633/LNAAvYZryhgAQY2UquGm0Cbr4WuA125VGHm40N38g +g0FPUrShBImTaip3jdKOKHxIHmyi21YLT45aaU5IZkjkEqfp6IE9iBe97GtVUo05LZkHSOAPMiJu +iGikdQ53ZRtJBD/nN1WpCXGme0uGHpovO8tQyOnJORN6jaRVHiYqL/ekwitowRdo/v/q/z+tjrL+ +LePgprwgBb9aYdCNC98h6zqB5y/1pQnuPtPdo9v91ER88O39KH2QfiUcBa4TAgLsmHjI8BBY+DIn +PHRe5+0EkAlIKFKEkNlcgFJCCfyvhBF5nP0RAPqFtiIE4bbuzLzAc8oB19Ohg/uC+OZQ0aLtHurv +BQR4qrwzBHFkiDBQGAR1ac1GRRBc2yH358nl4853f2ZFgfiIH4zBKgn8n6/w8MLm+Zvd98xPyvJN ++OWXC6cuVF/tUkUx0XdO6+SCAgHJWg62MWyDa/zWs+MUA+MU3CoAh7zAymll83PmT1fs0OD3KQRm +2UYav4xu49Qcs9h2Ov17z3X7/68b7+NnuMaYHvSwvdaxtkDP80rNMxBTZPb7afBDo3lSfB4j73dD +Y+uQ6ToTF9qj268m2WAPM0C9AsESAbRqBiBpNPbw8R9HX/j6/j/bznHB/C/y/U/j7T5vl/no11PW +fhbKKPxwMamn4QYy/CNrTBDTC5QzGomk0MdWce3/Gvpz14b9G82npWlqdcqtt2VojET+93wNt99j +j4VcIkggGiEeyiWJayEyX34g51KtDDkVhd6Smwmk3NRgSuG5QFBWDDPgiUXmXRY2kSRCpzpM9TLR +bvEpnBGGpCReXdwsKIcEmCBTEO5R2WrkSRuQFieVcIoL62KF+oBnpKQwKECaNmSUvggt9vKQckel +RjGyl6p2DnZ9SCYhFFe2v0PPwPj/rIIgRO3fXh6ND1BLCslWUqbIlYxk137GsD5BNHV0vyjAOm2S +MwVVUiu0KgA2xadC9Xigk0fz79UaW5/LozhL1l7mxRWz+UUiJW1A1e837wY8Awb9X6/r+R+eE9OI +EAkklCCCEJIBBIqIqaqkv9Vi050UWJrNja2rOKtjOxGGlaiQpBrbdEtT0yTwiNSM1yUVzF1EtV1K +spEtV1KspEtV3dfR6U8BPxiQn2uOhaw8LmFtsy3NjD46AXA7vVhClOTYLrxP5e38tr088DGCVxGg +gtnROxsarbW/B+qgUP2u4x5EfkpaFEelaqkqr/J+8/OUFGKr3TaBuKJvbYiLW2W2NRUkZESoKZIa +USYG6uHprkKBHlZqXiSh55QqaaUG/bz244RRr94+MSGt4dTJnWmy8rM4rSCpXytM0mLQTEOWUO4R +DPaghrBFbW5oxm/G+BXnGV0nNhunOnPJuu9m6O1hrVlym41O2qfhqgcnhJpzawUUOzv5tfx/3/Hl +OqIIHhAA0KsRMCUhRESQUqiBQooUUpQMVXqniiRZHmoIhS6oEmpGoeqhuh6aGi6WUgmuluRC2sam +K2hsaECkAoBUoEnJhrUEZK2LFSVWzrFrR0rXLy0DFHLcxFd0Lc1BcSISAocdy08vRVVFJKWLEaNt +tlaApUiWhaGCQpCgaoKKhmCrNGrG06EkFkRhoh6phhurpSWIWFoNba0trajx8/5/JxxxZIemH0+7 +2/6P88YJpY4q/T/l/5/v/a9J7JB1Me2z/cZyJwi/XMNuK/17vbYP87enI6CA/nb76/8lo3XYsEG4 +MI9t7z8SD20aRKCbpbXDmKMUBeLI/PlcNnjVDqIQvH9o9FXl/Pv/EYmv4YvV15L6IBzYrsyd4BRk +BeP7P+eQCO1qBEBe8sF/TSE0gCYJ/KIBtoREqICAkA0UtFF7gxAyCi3797+Jozz5fbs2SiwYVGeS +GQgeqc4wAdImBVVQqNOQSAAmqRPge4JvdtYeH4ogHLO95jN6wOZKAmH7n/XHPwLuPhGEg6QXjJ/S +S4XDcgT9qBIUkipCb00ww2rmHVtRMhRkP266LLwUc0MzfX6w8SMv9uoYmM299Y6dsgs9dMzomWo2 +Dfm1em7B1JTgzNCUQOOuN7njqxM4GJXxAOcC6XKNArPV1/69EEGggDpKVCqlGfQGY4T+zUcv1emX +WqLDjb0d56fb/SPjt55A71Mk+SncXkFHISShVVP1NW6cesfeYSBJAGDpy6fv+SfIndS8t+dINUEa +iNqlhShPELTUMRPJSLNNcfs+P6iPhj8pApWG9GlYW1Sq0RaJGc/490/UKtkwf41CcoEKFA/gYAOl +V+zqReg9t+6TVn9qyItv6/v3Bsfu80/Nfd7nvtKCe25hG0F9MQuFyCxYFAVVCFVCqoDlA6OfG/zl +y/yPaeY92wfHpMk4S+MPHt8u/TqH2Z8CdDW3wuwPpalSI8GXKRUpNIJzdPT1K0RcyUUNystux37G +220WVk2PmhmattPvHIa9+TJsIbhgoZWDAcWFgMt8KPmIInUMFg9lsShruoJAdderuDieX9tQTh9I +P6kAqAgg0Ig5AFE7YkP06tVBFAQSkADyBJjqxeHZfA8ukDacfVgLacOYFQSQgJG15CprSUok+YTM +lIiKCVpUm/5aP4/uux7OXiZPkH8OnXJOQlaW2FrcMoYRo1W2k93cV0U9Q9dUiRRcn+U9g3hukxjl +CtYiVg0bD06/dx+/Y67Pr/jow+j4Hp7eE9v0//X/j+HX7zu5+jVa1Pi1caoJa2ngW4NpW0nvTFxG +lEqM29z8j8/o/D9vy/DmSToo33TiDQ52xtGcEZtJtEgrVIpTMXRMlcw9PC9I0jykUerM4lSF/Pyt +08fnYuLR4jCe701en/jwP8Nsc2vFjr7s/bu9Or6T4nu+Wilft/mfsNbbBallZWlsqUo2VFjaxCsr +LKG5/T4+/5t9xeK0az2mdenKbJ60+t/c8ec5cGB0GjGorKDVYxbApZWWViiq1vft8uGjYObVwpbb +yAoDMhVCQJRcn3DrIBEVIeEKTPEoMXJK0fe+JJgSP7zO97R7yDP/DASx14fneB5WTf7PIXDvtF+P +EmUoMMQUHuFEVj2/f3Hjx/Tnyhyanw5G2xp5TPSZJr0yhNunfM2elq1KfPbMt3cyojSyno1cFUIB +HgadlfwlOKsNVdObMLDwAQuoBiADEjeXBOFh131141477HnSFG0stsSgilrGpWOSU0oLqi1GItiz +W1bZ1tj7X8fXjxt4CPPdw+nbxA84Rjk9fh6Dk+zOerdbfvPdKBfTNtzBwEBtnpjR/3BA+pJf9MnQ +MoT59e3jMgeA9B+WMJP5YDZs7ITgREIOdhkrWP7sIQJM4AaP05GE3A7MuuRP6phdIKzk5twGOhSU +7ixBJKFgQVTiXh/eF2N9rasiy2Ui1RtsPreXifMeTy/tvzf/AiP3IKLKpCFNRcXEcslNqCLUw2a0 +6SqdKRrBp1iFKH0M29I5JwTw5gyI4oKeHUSwMaAypb189f+uaMpIgz1OSgAPJ/YwvidYVrqVB5Wc +NzfoPNkWJASihSaRRSKFiK2gKLC2go+L+3x0Gdu2Suf73+nHx+i0KEisS0xFzwyqpUsosVIitD1V +wyLE4S4NCjAqsU+3/XUPR4z02+anLV857uxqqcTYSiwtqkcEzAaqNYiO/tOA22Tc3ycsKnLZct3l +CWQDHAzDm27oDAHu3M0CNv9tM0r9Tg18Lz0okz7HoSIL55XJWZnopvxQx159af57Bvlnt7szSFeT +63OXs9rLGRtRl/19WGtWIk1qz/GizalBW0sTiW4ImhJLuF4fckUaGQKFCoqoUbJ/aaekOGL4z2fk +m2YXWLfweYPNDgPIL/r178a5AejnkLaFK2tWtK0lGZBKriSFRWkSWSHpmIL/w996Q9XfhXcmLqlU +5JuF5SFKU7PqPD/Juae1xXwve3LrK6q6ab3jK+/+UzzN0RjzvdvbM58uycid3g+z2gPCFtqRpSFE +bZ6XEMrVlBG0G1W22wP2k+eTA/h4/612n1prPrP2JoPbfIfjyOX+NwP5snHlfHwfK0bZQUGs8kIV +xDHNKk8l1S1RKVLVXfvLaJJ4nlP52TnFyqSsh1nzqwe/eezkdxk9n9N+87pdsgJWwsoh6QEOysbT +BPH9/mAjMoygGkhFK1amXeFCBE9QAQN2BSUEC/b5ezr7+2Q/fiZh372+0Dx/e/D6uEDfg83cD4wS +iW4Llfs5vGSTsVH73+Z4HcIiAqqpUgkf3XQuiH+89Dxmez2TIkU1cz08rqVUVXaDGpZlR5ba11Ws +a23u/SPlV5VTzKSUMNd/ncjo/Hc9mZkSrp6RriQRba/usLn3ZAwMpX+qTfKJyScORiipUl1jcaCi +lBoKaWgWhSqvkw6CtOkKUSgMzpmC7KBQ3vLzYKLIeEyiUSopClTO6wBuwoxneoETEgVsoaEsjGoL +iiWA5ubfz1ttjlSRL0v/m8fVk9Kwp/lVnKHJXkt+YqI3AVhNXR79ugfq+d3TLIDxKZl6Pn5091ur +nkdn6Fx4L1exUH/c6dPQe3tqlkx6+yJ5P1nUXsAHAXW5Iu70loH8g3TcX8//kyff/4R1/7v+3/6P ++L/4f1y4f/5/9f+r/uf83p/1/8f/V/0f+D/j/+f/N/+X/0//H+f/T/5uz65//p/4+3l28v34sPX/ +e/Pr9ENb8/8bn/wgUfwD+xeKj7IwFf9f/cjcP9v/tj932HZYL17/6Rf6U/36v9H9tfYxH+Q/2Eto +uB/4ETZ/mJx/4IRECJSqMyMEDIpRWV3SX/XDwRBEFOnIHwgQ4+wWmoiV/05+vx/1plf+NDBBP/to +qCu8Hy/6YDn/3s9U2XCswyinpi/8NSUiSoh6UT6LJsVJ6jl2+idPaHMGG3sHRT2ggZRBqgSRrEfW +C7undS6gw+wA6ghfdLRHr3DRC7b3+b/kCAzmIUjOMLPFUaoholUlgTno/7k/+t6BP9NuAX9IxIJc +pwc3+43//tIiSW93Xs3/eC49HPltDpn4Bi/7Y/7RRKP/GumCLYg335D71dunm9H/yH0O1L9GCyxR +mZlKjit/nalAZf+6jMCTL4+eUDb2gPreoRFEsTcOVTI/pXqKSjV/7Lv+xfEgpKdb5J3j/wlP95lF +d8eAKERKMJkRUiVgpClVVMrjhllrKNdBx4gHsJJ6RO85fp29ColnZBND/MSZUAQMRehKZmTEDFRk +uIJAhs/8S2oZYrQdpeMQRgUyMSJYcNL6Ohn3f5fdVkM6USWjzeoB7f/Dg/8XzFgcnr4eV/KgzAa1 +SIpUoEjMWZo5mDbmf49u22ybMxorK41WeqHz9tp+7P/t+1dvZn3izP4TDACidQ72ChACarUJvFQq +3ioKsVVctdqYaW6bbaDmJcaLfNx8OZK84wpRCh3WFPj+k/cJBPl/LJ/CBP4nIPfP8p8jPh/q+N5k +UnCdIpYp7ljGwvTk6A3+n1OomJ6tiYiMEOmgZS6uTI5mTa6y+q7TaLBQptr7P1/+5/6eOp9Xzc+0 +Ga6vYFEdmOwLlrd/8//R/0N/5bO/20ZkENIRL1QzG+8AOYArgZ4KlQrfbhb+5f/P/bh/zv3z/Pjz +/2vXtkNgn/Dj67//k9M+30rynTd4f2rt0dWH//R/lRtqdLd/ol1OXERFOPHputfREr6/+Njf+3zn +Jknlpd8Ya0L4iQoDwTzqzhiGXyfLzk5N/vqXh/yfnjc//lpA1j02+mXdnz4I+ZOhtfKeMgDN/ByD +Tuw1O/03f2/Rr6u6P43bJ5t5dRNFkH9wycfFEHd1CcCQoDi58vooe8hO9mLmDI5Tl07UR6Jj6AB4 +4e39ioSJh3mhJv+HJdbtIFQmF7Lry0aCIHgE7SDKupJlxxP6wbawmJBTeEXOiZihU1N6ae7QlmYQ +pmya9Ox5H7SzqSUcZ0/zYX47s9/p+XyW5b9+XL2f5GNP8x2IHN6vvsTm6AEQdAHsUVwn6M4Y5L39 +2xl6DxR97yD54YTNXW05JrDWFBNfUYpraBg/zCiYIJUTxYMgCXRxuoVEo9bw+vDYk4QlX3J9J7Kf +hgfjlicea8hMv3JgxiBEX0qKogr5ViQqvywCkzPgHDs+OyIWDjfiZweE9kRLk0J8138iY+4hO4KU +wXRGojd8ShlH106VJKqVI9WxoaqTKlwvG0d2zVXf1I1j3r3k148PoSoz73snm8yPo8WXd2pObIbu +psdigoz1R3CUKoKvGpVnuBsST2drs424CMxwGNzDHKv7G4ay4tU/pk+oOPD8r+3u1z6EpskmIGXR +WITDoxidafLz9QWqlxYsf7VEDigmzWDVfqlM/NIFIcCkXwy8Z2Z8nPBg4kshoC8BHvHLcMmbUBLm +7J8GpQikCREXzZAGKBwno/0vZAg1eAUEA8Y449v6O9fubBCmLjG6DseZ1L3sm2lWH0eiFT/nAFrz +KI6hYK8Hbl6aCBSQ+SbXia/r/Xla8U+vM1QfhDWXzk/Soixj0DBclWTbn5f8k7+x1dUwGY7sOjKI +nixBjE+yL0qy2dwu+eA49Qbsnv0iSkUVipVFiQidI6JV/0/qKooiEHmHC0eVVrKBCmmx6kJxQdQB +APuiBa5e5MAa4ID2WyxYRFPjDKOJEaJnK57L6JVlnSlkqDx7FjrlmkSPkMrEBNEZmxoikBWg3P3D +FvJUCiTdcbGpGQV6UNj1QRYZ7EAFZBu+RqAbO4vidz+PGz1e3637/tcPv/dgqB/eKTZExEAShTBH +7aExjGAawm/LFl49g165QoEa6w4KvqfgpBGAp9Ig3r9tv8f9EIHkCJ8+3KckJT9llYQcD5qQ0/ln +u7klEqYiN0kmG+mPcpJUlQm8DcEDiBy3ySZO3FFRDm/TkmsCJE2IuKJ2AooHLjx8gARhnOJul1R0 +6gVnxdxy1qERAWCoFICJcCj4o+Rg3WoRPYyxRlEQAEaOrOlowQHwIgw64XWR6db3h9MlAOWpQECV +EFCCUTZHJaxuXwIWWWKKLLjSr0iQCiulMqzhAgDgLclWCdQy7sfNe5RWP75q4pmwCM197BPX93v1 +LdoEIfPee3+8/zcDLSuP2AaZRJCy5oGxPow+bQH6n1+KTfMPKPoIcgx52uhFwrJruydD27/Mlx55 +FjEIOIUzrCmh0+ahgu0NLYM8RgsPux5P6BnwPi8Eo4ro74zqhhiuqHVDQv9ntxdq+pTILf8X3g8i +vjJkHWxvy2jxKbJXIovWnIIPdYugP4/bEiQJHQRF13QXeSt4LSFZLVGBrcWbnbXfY8UmtxuF9ZPZ +9otcwOajj2uObjdG2dbrpOp0Xrve8YdZCJMiG7jG/PTvWNi1GSEo5dAs9UciZLtpYl+3mb0Kjy9G +uK2jkN2iS6/W77jT4Rn7q4m0n13W+5Mj09LoPji2mEPjQfPng1KnvtwvOvn57PQ1Z4MmvZe2Ha2z +w9+OeMJ57+Hv09L6mA/RBjbxvYk9WQQo7D3UXdRudiGieShKIqoUoUCFSiFlCMywEb846AeHNP1N +k9o3c+sX4JvAIzdg2gD3zwE2DhHhdhAetLBSQCEREKC2jvZA4g3ypRChIk3z7tOEdnP6gEf9jVi+ +U6L0sya+YdlihHqm8b5dxFow0gJ3ei9CVlG27NCBh3+qNANXhkYJQBbMCGACVDkgCHmKVGwTWxCI +aJS84GQQ8r4hGZkhMk3eJnh4l6a/oBPVK0IqtHdeGjDWR3BPiDaAPJwzP/iAUgIjwdNs+hn8A/dd +lOP34vS1/hZ6kh/JvPk8Ka/VQRCdVKDwvR/bgmt3fz+EYB4FFcMjKCCQxAIPkCfLoegeQjwACXTJ +eMT3mVkETlv8OPjp/4t/VivxzCafZhgsjjCkUtULKnhUuXf1bGvjub+u7D9ws/yJvjzuxPnc7ri/ +nSDTSt9943jdxtrPe3hBQOqTbZiLcOo0OMHeu28jz2KS2cLJ+v9nwfD9FU+2/dtU+EFOKxil82qn +v2ZCjiatAr9qeH3+Hq425KMNzYpy22yWNd+csn4fn4mj8/psvvymXlK1VLLFCQRjgZgCkn3Qzvn5 +/1Pkz9efSdOnNTVwzGM+Dh4HhvhfTrucgbFIUnReeFc2LcmU224pUvLOgbcnjkwbgwtClMcMGy4K +0bYuJdJWj8Pw/X9a7J2noSrJso23EquzusJiOF6sbua6hs1YqxBqImR/pf7v5wd+n6s1+0NJ2Iza +wV2rbNiksBqrtL2W5DPId3clujYbMY1jgHjOzbPKONkd3AZwk9mZO7jFs6VU2U2y2dmR1t8KvcH8 +f6n7PbhD9y4jaOknkNAe/uGPeL5nbJ+e8MfXamk2h/R9hsbbXQYYTFG5l8jBppTz7/emvPErQSyF +J1rkQMkd3cJIQq80b+XdjwN3q45UK6WewAM8YciNiif7y25JY5LdGFE9l6ZuiaKW+mG8CVc3ajAP +Dx+X9nGxyZSrytZgqMrk84S6XLbTVrEMrBiQpYhgWSgQGqCdpc/XQ6KZXino7wBJIJp9lnLuhotn +N78Co5MuZi3AxxLdzJmqA5zMAxJDlBVrGkQgtA6kG1Jh3fsAEe1ET7/Yx+j4f4+b/hyefzG8tuOU +whBYXMMLZXFy5bEcYXA22H/ttDUN6UN5lxAFHBsCSY5jJlCuZYLWuMcx9PnvP3fzDjy/f/+P/93/ +bIDImwfPl2fc5E+NmDYW2/fbDwOjlVKOzHCfAUZ4q6YPPaHKLyRqLfek9tW325+wc/uJfDyHoisw +9FZU6YBFLIpCKtLEjKN1Gyn65rS0rccM1+/cd95Ntmkh2/o95+5zUAKd2V0Jtgqa5cTqByOaZEqH +TqP9DNjhsm8TcaoyhvQAxkyXlLQTAaheJANLnPyQwJB1/DYFw2k9d5cHUk45tiKhFCUClBIkThxH +e8wTJHlDrLxDIkc5rrz0UpVokiOhFpOgbeFNawM4jgXr11whW1gjQWFgThyAQiYcuO/mh3eHjlfG +qCQFnV5CzmxIsL4gYwVMZeXjwhOZhcPfMNPt6P4TeE+r741AIEE2SYe1dgtQKVxiLNcggGIFqte/ +Y5QDkh3h+Z4faIfKxT8T4gdv+dSbcmH1uP0shNZjZ2RSEuC7+cTISCg2j6A+3C2CDgQg/BSllCBS +lMfq7n2wAEhDgQCFAHlDfd13Xr50cQoYoDcqAUp8/18bH0hLMFC9n5YaNJTpdaqGbZju78sOK8Ut +RBbT7ekFOlPCXu54QJoDyXSFNshSOpb2eVRRKWNsSsxgiiYJU/1r4+v7LmeaMLnqNMQl+yx4IU/4 +n90AHs6/S+Gdwh49/3mdjoxdXzzNFf5Lgomh0gf0EABCAE+Kqi96BQT4QtQQrVQmB29/sh5Q987t +vm/4/G+H4aK/pwfhhycdvj9l8SHijGQUO/inziGhA8zFDmnTJP4yH+bK0tdeuyaOD9mTyDiaB0R+ +yXD6BUaI84Dg0p3YetCT1huMFiyB0PN5VW7P7fn6HAz588q9PLKqjlNyjl24N/q+Gf2k28a3S3v1 +lwDNH1U+bxC4C+vRRKUbUGBQfeHbpRJkItyCEk3LOgnAcj/iu6ZzDuCpejxz9/XFWM318CmaGHwP +kNFfcJtsNXt8d3h4A3BbwknQolbgQqsOSWOOlANIIVEiApp1VtkXkidWIAc46uoDjb8fNfjmFo++ +dPsZMeBh6PFEbpEt93B8gkRQlGUAERIKErm5wAd1wFNHQE7w2EpoQAdqbVS+CtucIBQ68hv0RgSe +37Y+uAxFMDFKRUqkvWeb73APefTPqP8Enw+okPmZANgPjP8bW22o1c15obpQdgv39fcySjqRBXzC +VA+eURX0wA7gZea9cgxyoE+iiXmPZoBbKBiAY0/Ohwnq8P7d+ghP3RGFMQzSfDy8PX958v7ery4f +Nu2dw2oBYP8Pyfnt7ggl7Kcm4cfJOkEIQQRzoEJAAEB1eAYAPeAUTgAo2SgOVECZo2A60p9uvh1J +2t32SjHMOMNnzdPIEToP2nvdnt8Dy8/YAn1QVFBTSEzR7HMk+SiSJiiIjRUM6Tp0MjqoqBjQPTpm +RN3G6dE49/JEQYA6S2pihppIAGbTo9s1QskFBACSAa/DAgGlP5CgbPlVjAmwTZFnTnIEaDNzetcs +lcQcPRQF7hqCjehiOdBuh20ZJQxLMKA9wYcH9/8BYcwfGQP694nt+V932gfN+T8POpMhopYh4248 +miiwj8wfROf7vE5HtynIOfZKdfcdvQH0KAgkSACkgCTUMouHcNbkAd6AT68Q3D87rpNIjYgEUqo7 +2jIUhBh9QUIKoMcTY+jEIB6MpyCYYQM9FDAY8ioBm6NIS8gS8hCCTGAFTi8Xrw4ZgJEGlNAkR0li +TJ7nwaFP5Q0eCk8fF+aJp19S/Ddevr/DnP9M+p6UDgMwTCNHK83ZkGiNPtPYJ2Y7hcJOdECX7wAd +2qnAZOAG8IMA7Jvm/QISoFnF4ao+D+v5UdXi7bf7wq8ujwHP1qgUEIBwIQkISAAtmUYE2BMYvXvH +BEfThw+ho5tQqGpnon29smpNBSrO/ze/9pA9qAgUGlp+oFXSwGoocS8yeM9CUlACDpvqPORpNGfO +uTfe78uNFRzjSEvj5rzWi4qzAFmZFVEhmV6ZkGqCCIPIJCvCbPDiGvJ0uK0aoDE6N5xzc2VGCVC7 +9IOyGIfbWUMhm6BJYxKkYLQQrJkKVgomWHv8/o/r9mul5cfWYd3oOv9vuvec+J+Sd+1k5dJ1kNRR +iAKEtFFChNInSVy8Pxw9est5hssJv3awBdkDJ4kI9JSRzDShzgej5vnkA6+zzn/Xfz8L0QUkySCA +VgwJ2+85/539e6ert/nd0PUJ4dTGlOieyODrih7KIHhGpj1jEOxYNaDEqmJXAhSidGtnEdDcmeA/ +g+jj9F7xmID9/8X+TffQfqXQvGTaJF4xWbuZ6LWFng2pJAhAYiYeUXNUk6kS1wiOjPSjLSrLy088 +awOxTbCSLGZ5TMka50CxUJzxYo2NNxVk4zHbFf4M5aqJl2GsQ1C0aCfgf4/3eh+dw9bqr9Hbk7k4 +l4J35J4eDc/bg5SGO94QS5+vb+mebXmh0FYQRxJUgpFigmvR8/19N/UcVnDOwWBl1TWUYjYLqWBT +af6PXP2+LWrUnJLMRW3EGygFTgZz21gjA4pYVMLkhyZFIqn3Z56f0+X1ZwD3vHsexVdBL3M0Y1Fb +V0Hi+QGCBEg8IoADkQTXbcEkECIAbBMPDXdblEVc217rPqb3/b8n6TA+hLDYNyEkPGveLFs95TNj +kr/T7uAODqiVivpwNC/GVsyzK/Tw83PLy+Pz+fsr5+ZMkhfz7gO0B7msVtRiVWo7EsAGBECQQqlx +ApCOQM+6GWn6t/q7qvekKEqGnLpwyxppKOJ5tjOzv02asaDYRTWerIiIEYMEQAOBOVArC1bkA12+ +WwV509nerDylPV92VQrUoUUFQhBUKCCz20m2DRFtBBjjAPDz3CQTd+R35x6HB7FRDQlEFRKeIcH5 +nIZFExRZ8PWbVPYpzyuKKi6D4RpASIIA78RcWAUBtBTZKdiMDConI7j5wCUTBCxrBLttrZ/ufsHv +8Xxt6E0RRMUpg6Qx1DRrRq57Epe7EdyaI74B30J4UQtO4hrWKLSapIjYzfhsJHZxERWz0m4opMJY +dq2XQYfV5N5Q0OxYE0Q7g1AJ3G3bqTfOXLisGLXDR2D9tj23s1F5s0QR5872txAgBvDKXC4fLz+r +Ht3vpuNfye8c+gIf6POl0gDHBU6WNEUZUXtz7+mcQwTjcTivoDE8PQ9nRx0ygClJYAekHH98M5gk +bsOkTocw0nXqybPcSGu2xOfieJaH48zSo9IhVp0G2HgVchzwhz0vOFOybzt3QMMY6Axijxz/aMgS +gPHoqaSjfNh1jCLeeS2nfbWRu7JB0CTd0Nzs3vYByQbB6JJsOvOXTE6xrjbmdwIKCes01kiqKGSe +P2bg9azuzyaBts+febyGZH0bXI8Cb2OXVDwkwejHarbLqtOyO2qcFASgw8TMio6mSJB6LIA9kC/m +KThm30AONDcmoQAcGWZZSUYgEhASEeoBjiE8EekzDu3S8/He/Hrwjzs62VTsK2zHt/H9LL0XXZN3 +ZwgKr9/5nS96rSN2EfguzDOABBEUJcAEUY+0fM3Nxi21cfaT5l4hUG8iNqhr4nIB2aV4NmQ8ewOu +9nWT28PQxA3sp6RyGgs3eQJZY7kc4kGRLhD+ps+E2cqvCEo/b/X/D8fn/xe6ifzIUOv0RgoyAhAD +XuDumfTtTJUvlSiLHK/usD+2gMERUSkQXgmPy7BB8c8dCeE3puinrMRFOez5eVIU+zdh68g9xuM4 +OqkKR4xoJY0LrEBj2ycNXXHGTbBRbNOiqXaXQGsQjkzlbEGCCAg/lg6Pc5+/3HHy5PkIeYXBCeQw +SR4q6UuJDwSGoOkzpOL9VR0azRe7dx3BR1KYe9HwoSE8TGCZo1E0bQ1AM2fQbillqyjFRStYztEY +ohNH9xyuQOhpxmtFMUdBQnUwGnQ9rBp0jpTE7ZHFUSUeCkAI8ZE+UyRGAcZM2ODEzLCEDCiHkint +/WlMdwZ+txuf8ux+sexz7Y47jWxkU+TG6hnQNnUVYcGhNwGoTUiGoCUsIiJ0uARknOajARvdaYaz +NXV0mhv+Dv1htvQRqtG4ZysDNv5bzTpW3fOShbwu2YaLWoljtcxKa1rF3OW4Ubl2uA7uiwe2fwRJ +d3cl2GIJLNGGCLG3WVEMQ5MhduWSGi5StXlmOyctignqM7BD+fqaX5Bj7e+hXy/Jcn4Dxo04xBgn +CVIxIxhmxrWmt0OZNZp8aayiDdsqwOLrAuVlTP2Pc5OW++jC2+KbNbcyUXFY7hybQ3m+fHjbHmMN +jv+ju6YeaxVdmhVQq7JTShsSkNEOQkBniGzZgRxrqvLtLxgeXF2aj6AdmrCQd8GAMi+AOfMZNLx9 +e4MJd563V9bfoHhPeXex83LwSbgyKjUm9LtiOdg7HmMOTkaMAhBhEhAQIwjRw8jf9+Il3cdozfEv +dN9TfXyFgjYx68IH9z9v6dA/uZ/rrnJ/gT7qisJuMFIsWKTVDMKMCGbJCcxInr40fgfpP8jnqT+v +j7vYebqbfl39fDxhPh5qBkoU8g7wgoH57TYDzbzA3UNEwtGkHRiaHyNO7UNPQC6KSJ0BoaQHbClC +FUpDLpNIlNUoRDTFzFAmqA6dAnU3TfBJ5qhfKg0VpwEGhDTM6VKzEkBpCkoKTykQwGNQJQU/FvyP +csTH68odOw9P675Dr1VeOmDxDiCn6JGCxsRBJiCgY+BdTLUkWKiIqP8PnGztNxEYJl914hwUQKpe +i2YIkigFMUT7LA+HAftevzWsfqf9Z/rDFeFFaRrKwYoSGCi0vxARocBUkkYirY3932TjCcIAg1Sk +cTxPH9tnJoa3u6YlgO1NUFaIqdCaHjuEpOZP8W8ul8hqgKNKhaBfrG4kyGBDCVcInImSeKpZJImP +N4RUXEu1BVaYJ7LqCGiikghKmpZi2ME0QQVZUAYU/C6SicbMPv24pl/TXAw3QrJyYFZB4shpkbpM +IY2zKccGYITy00aTyO884XpSICkWqVUpWgbzHnHnbiQ6nzxQx/ljh/mCYkPVk94PeAO8zhlINg6d +NBSc5Pg2E/tHveh4IexUpomQ3NZxuSdkJnBwGey3TF+041AIYBKAH0j3Bc4LZxrBPidjkT6IcKnc +CFCIaTx2JzBruGjDXRLMLRgpSKQoIWfukpWqpIg98E9aXgPYXt0UNjW7g0oYAnSUhoDoiaB47KyH +rBk8Nla8sdC4lNsIaBNCLjLbcdwUgZnw8O30il71PPccxyYkMvSQJZISL4geGfKwYfOIiWICRMXI +2w47hR0OxuRYXlH6olFBlBPZGgpCgFPYRSqRYYaaEN4sDichCco+Px3qGhMcENhOIiTRwj8CxDee +pk8gpRaBmCPUsKE+bAY0NWBgFOcPhto1DaEzhpwk2wU2cqpvYDHhKT5wqeSKUssoUARHsQHiMgCk +F2jxDvbrf+RtZki+3rvPAymWKKOT9USxeHZ6xu9l+B9vu+H7qPzvmDrv8JDS+Qjh/KAlniXY/BDQ +of0gJPcfu2bTYhJTTWP0zMJcqDqQBoUByFR3sP4HD3qiBxG74o6j7eH4yAeSAFCTzk807hkPIHpT +gPlAeizmbdgyDSMKYofjl5r+vHzOEiSilQ6SrmBvyT9tMQD+37/MSbkk5mTL60tLsybBRuz3CyeW +kIIhh2b+xNlT9hGYiQoAk+kw++1BAF56hpKD9JPq2l1qb+MnkHLWD+IF6U5TOnY/j0cRYkV/4Pb4 +r4McPSHkWdInn1W3nd2O7VsOOiCPcSfh7777i5499/R4ivXseqQ5R7dHR8pQYhioizQjtG8+eR98 +BKSnMpKQBsy5BaKubgo7c88vDRk7HqT1hYfSoLiASiBgBPRCUCSX4eAe35nYno3Dg8p6+gwN6IjX +KG/loeXjdoKGjlqEMIpHJBJTGFMRsq588Ph48B59AMHqbeAW/OG+EGkohKAlAgFFI1xKnvzvA1iK +X8Xfw8ac17a1++p8bH3Yh/G0dCMImiYqKNyYxMhmDRQMpJsGiBqagI/dUmpGYUelEkRloKHI9vyv +O7MCgoiKB45B0YjYOwfmu/o50hef89PyejiPyJewQkD3Op3yLIw3eSaFDjl+4ufAGRPOcHJ0mjSW +tg7VMUKNny0hoKiJUqlEoOLDeajT13ysdd5JFTQ6RkyMEMhV4BChIAEBAr1CffmO2/+VU6LfXfSF +sTVSA4pA838r/Tv83Lg2gpiHJ8/5+8eWiHCnayc2Ry3sP1hjzGkdCvwQo66dK0l5jE9IFsmhdIFW +ynneAd0cFURT5OifLFb38OcEewaxJASrSB7NAvEkTVwYg6lr9PJ0j+wg3Kv8+/7n9wa1/t1eSCLo +GASVE9wwyWkH9M9JNbJD2kUJrlPqNAuFpxlBBgMU8UwyFotS1r8RokQmnYkbYoiJCmCQppaSBMEr +8ck5mFhAeGbcoSxheeF2pPu5ukDUNXFwGflJux+mnfMBzOERvRbcufZkECSQOJRQQSdiztJhNKUp +80ZNBxBhBTBFLXGFhz8v3cHEDScEAGI1ORRloJhx84OKKgjm1JEUhmNIIlKX22qyArPdGkiIadkK +goPTMRFEyhVRRUujFQxBVIxrJDAMfVyOmiliCgSIDKligq60VWwoGTzccmodEGZgex+z7Pw31lpm +aRoV9XQEQZQJHS47HTkCgJY1NMGjDs8+H8vKiQFAyrBEqqQqFRRSOPGSoiQmoIFE8NtenkZgL7DR +fGOGoeoxg01MaAMYPi8SvNrwH/gSXDByBk3ziD/sSBJFO44BHTSwBkYlhPHs86AOdnEAYjwimooI +gZgFMGAdrvQ6dBO/ugfeaHniBmnVY2o2TU4aYs6g0YzMcz2e0wyNob6mLgTQDIAfq/b7AOrlSOYd +K/Ngige6765PYpIsUmFo+sP4G98DB0OBViODgrgzmx2TUERqjHxTeNaAgDJTHGdMYYMVJVVNQTPC +Yv54nIGkiBmSiZIqIoZi2owxVBNFRTIGkcAhUzS1RQzIQQEY2IStaKoapXTmCqQCgKBGgCQkGkmA +poSaIZophImJagnCDCklImsMDfu5+u1f2/dhv0pnTtMhxuqX+HG55/049RIeEKQSEfLzr9v5XzTl +5Be/UdOl2qRiL24p0rBI1Ijhym9n1DCqqXc7DJQrpHdIzTOl/dQFbh46KsRQEgjrYZ8MOgnQhiHZ +Cc8/NrhUnsQJPM/4DhMeYwxeGCCJlV/ZHKPYAiIFfP3ucB4iaENgxT4IQr/GAQWEAA2iEC9QApRC +UGqjOaDURheu1+qVY/H3tc6Pjkjmc8+1Yr7cmmHHpm11V+WTrz46sFEGxWdLwN9UIrwq42RpRwYl +VRRzZe6AyXCPG7a5RIbbN0bptOm8PYZBfHZwHHKOFZFHYp3wEYfbP0OP0Po5ZA5cgS/vIcaOxv+d +0F6CLKEkPH2HJpDAW0pjBRsmHIRMupdURcE0mQ+XibYnAQCKDkAQKk4KEgPppQx6orVw1dfzbt4P +77Hc2jPokpb7ovWC1DT8abzSvkKAZzldjaUkEggICQid+Wb2fruiFY/AtBTt3fz9HY236JVq+3pq +akNFsqUIUuEgqSqL+SeO12NFfjJguz5PDUKCM1g4hDCm+XFNUYVH5vFlx0BslizhLqlRjocGKGgk +Ck+ylPcsgBkm8TK++cnJQUSEzJNdhxT4P6A3vRnVtrBSxjJk1qtEUxCBiYg8h5eIwQShpelaE19Y +ExAlCPS5qR2sQhDAv7ZiAh50ghzy4onN8DSIKueHux/Nv6Y4QJFS+B1gvoDvx792J1cCDyFXm0Sq +al6AosZDpKUCYOTOEIhCpy5RZmfDCvHZdBSRLM/b6nF9TzQ/IjJ8/GTvnWqDRQmDKsklIsKJAf5+ +34Z6NZpPpPEDf4kgv8uhoIZJGTXmhpP+f9ccv3HHOcKcUtbot6ukeRVChqjx9gft3WCpj/hpf7zn +ceN/l/stcVN432BZodhVWjH2JJRFLKWDp+nb0ZJTN24Ltie+Y19QH/J/y9u7g8d97BL+mel75+GH +gsbm0vT+fhU/nl0tF+lQCaqP4GJgHhBF5GTK5Be7PLD39Snu7wOikEfSjOYgUSbzyjfR0BPjnxa4 +hfPkdo3qDTPdvvdt69zu7hQk82LrGgWyeajSL8wkXAhvi84fpfqMaP4879oEvf2ATxapDCXbL4Yb +Q14CF5hSL7x7BJktD6YxNEFLh/NmUEHFcwQDsPn9Yjmc1kjw8w6PVLicq5yl9LzwrlAWlXsimH2V +RkH6GgQ+We9FjeK79wGxrQEgUBJNo/Xf+xPD5h/UwDBiqT/JzHLKf6WIJBBD9oj24AdOUSuA6h0r +rAmA1+I4StOJEp6wNhXHlKXBEndiKBw/Y0Ep+LGHFxJGs9zhLrDvydfeMY2/i6IL9DeQJdBFH9YL +DPIgeiquDDnfB/yYJxW4egkYEHpWgopJaqLvKO8lqFKgKexBKHdqBzYRUtMSM4wf3G1vWgz/2bDt +4DMQfigHunBXxj4n3mPCJusMFFxSJiJRUN3df5rHOfYBbIKkFCiN4DqBEgLfGh7a9Q4F19ZPyv4w +anBei3v0WgV7zpW9e/1a8ruN2c4eweWmVqM93F1dFcxx8+q83GYWp9VLmPoZL2KLhY9PcwRVIR5C +RMJCkp16kfAaGH7j8DngnfRdOCPAPQ+FAzT9VxsCFCAPgweIr8L4jA7FCSSpEK5aZyb1M1cBHI97 +6iKqAGFVNu+eoTEdWRV8dOPA6je0MHglrYDsenqJ6n9w+5hpEyUohNNvY7GGeyTVkAgTrfjhWQ6F +jR31Ub+ecBus5+njwTn59iUeOvq+ShXEzYpo0VQ2YC9VD4nwJn1Y04SjJ35z1d7XXZx2d1fHWupW +Qrw6pKqHz3qBLrmnqqpBE8qVCuhSWCEGdyAokwdu0EYil6AefX7f4FvBDD/S+Z1RZE+Zbg5XnIYO +zDrs+y5p/cerfEiidSXW4EPjxuXC58EuT55wca4FSA9DRBI88ZYwQWh8G++8kIm338hMjXk8b5DB +IoWyrTCwGnTj3ZCmZ0h5cXSPPTlihEIEpIVmmJJcA+A19Na2bNFD6ALUCiWV5QkETDT+wkEN6ihr +1ocmvV1Q/rTHGdvXQXTRWo67AkfEUSUIEs5+hayga2R5YHGaEXGCA8AdyOUJA4RDYEACsgcIgL6C +TiIxMW5Qa9OrJlxi/U5DUglLz5+S8xzu9xs3oeqlost0B93xHuNJpFCgEw8v8xD8dsinZCspQoIs +hJBvVrwEAQRrdaEoDwpkZEijpGiQjFEB1u/dwHl1X5ev2sTjKHIQR/RAWUK80uZGB5E9NP8eb/1L +Fbx0/A8r/SwVne/Q5/68BMLF5jWAhH2At+wIQvgEMGAdgVMpCylQUq6gGhzz5/NYtSi30v9+N1PD +nweaJnhiw0fqxcUwq9tLB84GAgEKpBEnSH+xMSq5dGRUSeDOfhPeO7r6niCJZSplaYWwS5mWxgyn +YsBSUBmY0+zbGGInP1M+Y2mzsHKDDllydHQeHeLwBwRINHjtCJB5CYkWagahMnqT+if19PxPE6cn +8OnUNBWayytdlone9bbWwieh278HTZK5KRwynn9Xs47e+pA6dQofCWIAGkMlMgIgZThQxBDawO+w +aXjbRIS6BM53qBB7PqVCGzgiE+DP6PQDmHgVSkKSC6Ko8Q9EgixEKIgneAgewuyC/w+tvOO38tYP +ZR6ZvMyIPH8jrrAy9+LJ/qfV/f4AP1nRxFPZCfQyKBuJ+nD8pRPYIXz6lk8FunEBEiLkeFQqLkfj +8/5S90fYkiTOPcatDnLr2RQ7aURBRqz3haG3CJwGcglCdAgFYggj9kKDEBIftdJ07f4z52GwrxFC +2VS2giwV4USIg4/yIdPSdYgCGZAiq2LGHnHcLX2hSoLR/qbgik6GpBkmDzZMRRTQFO1hgeVERVOE +Qc3Kp2iV4f9NKXSGiqqAm2DRTUEFWgImCA1StsA6VGihNDEFFNMBVChMjDKNDhyLhqikCZSiCCkK +Fg/fZJ+dg8IcLIxEkBIuLa/LWodBzAH4zgQImAIVICkKT0zbHhg5RKE5FNIdshRS8wGjhbYMjxoq +kqeK5YUarlYwYNfvMvMSkX3HoJ3piow7+g4TYmBANEzN7pdwTTsXVYgQShYOTvhPWOV9zed6F0bC +asog6HEAmdQcyUBygDwSQcHS1EkqZuwwm98Xw3Bx4eRw4MgBvy2yBSxRChTVCKz5UkR5UoRAFBmS +hqgoTDLpwiFO4FwAQwdAnuNv2H8/r9frvD6X1f4+Serr7O7QTE9r4M9QmmgpUppk4y+c/jeBDh4S +jclDb3ZL5rxyk3GDJww5JFWl2A6VPL3vO1+s7DxfTPclprU6xAVA8Jz34z1xjan3vZGI6HyGR1s7 +KB7fo4efkjhTXfjvNPcsk+7tflncjwCiji44+0J9B7Uw8LxkjQfaw9KPBQQJ7qB/LLB4SQMO5qGl +ph5CikiTUOGckgAC1LtKwam1srWIiicHGYhHKoBwnAxhwJGFChm7iybtTYUdIZooaMk/r1Nm4KHi +Ok8kAbg8eudp4YxJ6Wbb2sNIOzC2g7lGCRUVBDew5YFMPoy61BXEYIrVrKyLC0lWCIqSIK6cMFSJ +pcaZKrsBylTJFJfqDGPPkQ9DyKCiPCmGBChB9waGChSDgkU4R46Gbi/nycv8kPD+29oCdITqeXl8 +3gPJ0IbpR2YjxCGURBDtrJo/hxO/XAGyh/aB7lPpvSTm+DJZTTj4xtz12jN2rB8z11J4TEGhehOI +DmhoyCJDgPoEjbcAb1uceYA2acOZygOB4U/QcGhDzBlIZNL89yfHcr9D0De/HHHyV/V8mPPJynSr +2wDhOA0xUAxVRESFNBIEwEoQErK2LTZyEQl8HP+ygeSBYoe5GGTvYSeKpx7un24uGlP3Vr3Zx27M +r/y+MMhzkBgQTpdBpKuPpOad4kjmUrJ9pGKVXx5gFt+Z3YK+GyqkkD+H9+Byn82g3kCr3vBqRnr2 +0blta2eC4Xiie2VpDI5xud0VC4V7bZCIRyvnz2u9jj/ePw310CMpPJSyQkEsqkkgOAQoARf6enq4 +1KviVMHLAt6a1rlDn2oB1cFjFy5c5dP4+nOXMVyhXxvv3jyhd/5aVA5S/yHo/pHx6fXHWi3urccd +vn+/txBbZuVaAHuRtF12s4CGQqoRCSVQMUlxHIg49MYSATJA520gG2ZkIYSUiGaAqSXmDgDAhAXA +2Qa0nADmGfl/XsG36OerpejB/z9+A9GOJOKUJHJXDMRCnWvsgjSIUAAUiJEtuH5kxsGIa/sPEH30 +Qy+8Hd0HAf3vjupZgNOqFSD/KyC9AHp2Yl10KhtPdvM+EPPY0RPLjUWqugJJRZBuAW0TTg3j2Oyi +h6g8MHouB8euNxiN6ecAh1G0lwmEY/l0NCGzbnOOBDIchfQy7199wxbG2DGoKsH7PTlOVozFASex +0JxFJsG7dcFUaUOIF4CgqhA4mgNsohoQMYDgo9oiQZXA+KNCFSpM1L4VARIVsZgqKWYi1Quk0mhz +tMDAQVSTUMRAHBgusIzGGCYwBpp+fB80gYQwyFGzw4TYmsOMwyegRyxv0A5bjYwXnITA4HD0fx+g +9n8qHz5T5/j8DVwCuAcqoBYx+8+1zt0RcZGZ7poK7cYspAmn1zTPRRKw3xBz1BJKFFIIcLwDuIGH +WNgy+2+W9QorhMKFOF97AvTBcN00OL3Eo7FhWaaAn5Uraw/JzEwnhEgRKpJAXOeUCIHoCJrxfA0R +HtTIQ8QRu8vikR8cU+ZKufy26dmSXoUMn6WkEt+Nzcldd09TSzgdpT5/CvSNHX3AoCQhSHj35Pmj ++AWJL/B38vh+3YZ4c6dAISonooioUC7SiHx8VD/knUM+Ib/KA+s44CA+Qf49kfBCO23+T0E6DeR8 +Pl+h6u3vj9h78GKbdO7sd8FNkct3PYLSqEWjGO61hb8qEGz7WdCNKPUB3W/r846MCgiKJYpgohKQ +iQ4tM4RxMtLL/bCCp87kz1DF1SMYVppsPn82/Pm9rBUEvh9f26Pr770Tx2Jda72qwdPe1pcpZec8 +HrgKn8V5YwSWOhQg+MtzIJgDIUFhQDKRlQo4Ai8GKWO6+r401fAjDey+ILIZlQMw68bgL9oHN2jj +C4DCPL7bZl5YdAq75pjaOPy3IT38OPlngiiifN7Nttoes3lg7pdqQhP55VI/1SAUQUJCZb4xnoEM +JGJmBmGqCkIqASl8v8fH5yMj/D9P14PIoO73544z1fHrRxlG/ujGWvHJ1nFJwx62ChSWiAJOUYx1 +wG+Csv79skkY5DIZnBY2fKUAJCAoCg3oUUgEgEiDgu+cIOseHNf0JEebnXwcOqXQAe3TQGgnRbXP +teAA4A5ygXQagkRQWSxPiQAgpUGwa8uSLDjWLShVFvbNoanRn3S6WjKKJNjRYE58faG1czdIQJUE +KVz37vSrB9e/lj7GxZsa4cYx5l5iNlORVLDvjjXbkUU0uX0t2B9vVAS2g5YwQSmOiIeddMdTZYWj +C4d3l+2EhAIuUCxlSZwkB7fDX8/+b/H7fb1PhEx/P68P41RbUSBD57O+CKg3VdE9aDN8jvkbyFHH +SvGA78lwxjq1pnKJiHPRRX0fAd1vP3r/vFYu1TSpNFtbs5q2bav05ydDxmTbKiBuom2X0q1VQjGZ +kKjMB9tCabZa8YuqnJ25vRevN88fY1spjKrUccvGazVKPfrfNYbYxa+WPjNXolpFqAqAt5UtRAQn +sjklDRQCortbfIVDDSYKxg7W01rMIizRmjmmh/uQdo7axBZDAGWyk6nDtodSnFlDV+GQrJsDDm8u +ZQQdhGIuX/WEv+dr0EDTvpa6GdXX4Z5bcKJ2uYPM6ml6zm3Bzxk88ftfG++5rLcOl5i4BHvvXo2z +Rkhq/PfYQ6vEcZXkhZ9u/mLz9P1pR9Q+TnhopQoGCQmVOAOOT7FHm1nj5azSC32mcg4BCmx9hYTQ +aKAGknmQ7BJ4Uh5n0nz+6ebikrfktpmGOYUsFJRhMzM5vAO6Gd6p4FEI1x/uEZicBOL9KitJVdkt +r8+DunVsYpIT7+56UthLWxqqyAxlxQjcYEfAfT2d7Noa7aOUDjjtz+SLSj0O3PXOpaEkdK9h6dsw +EIiTu5E4HHj0np+iTcetlAWzqjTvwaeeQ096mlpbz7yqoqCryp60DjkB8daGGpqE7n1w6lEGx0BM +4cniaxfFgxgqMTFf8biDdykHzVynHiq7ThLw8Q37D2ghybCgh3yHejrm9nXkhCN7J9AejSQHYc2H +T1VEzwERDc4IUOMTKhpPUYm9GRezoh7Tr9aPlnrpSh88fUJs9nmb1QXZqSLjAPbo8noFQgWqNcix +j1GEA4HZq1nwBaqgoSkH2VGkAgASKT1HbBy2C304Uq5cLfwiY3po1q6S0AmJnA6pGaAbYefkUfVW +Q+2GEfPHRB+iPOsCQ0QatlaQnuT8HHr+0EPeX6fhodKzAj1kGkCBAok64WFoXFoeI68YJBZO5l9O +B1UzB6kSBiBWUlCIgLRqEqqeBMCABrzUxIm7A3H2vAZAj/YP7JKIjRKfzGDNi20upZrWMXw++vL7 +bz58uUFHmjaNvJuGIjcy5mCkhlRmYhz6fJqZlHnlXaqqD3KERwbXV+enTHeWawauqjq8O76hnl8B +F2z9yjGL5b5kzXDSNDr4P7HvvFHp4pyP7kXl5gBbNDSTK//LMyJFP8eDOUgJ/lb45fjRpDbZR3Wd ++l1HBb3u4RY9TRL0Rd4/nd2+fp2m94NAR2Y4B29NoBBJQ+szW9sOirHf2V6Pj6UatFqg9eSvaOnL +tGHKcAgjyO3FEwdjUGQKG0OAeiuYSawbLOMBn1ghwjSY0wDTfrzju38sw9crx++CYIrBTV37kn+H +SblD2r/jsZPqXc0VrSMOD/w7fIIPCITSqkbLqPZo169tIxsMr8iqw7GcwVYShKCAlArW+qGr7+8E +YckP5/JB4BBorYASxBU+hi8Bm1x8CF2BcY8RTIMete2ed3L+PP+3ieap9X+dnRrN9GP53a4W5Y4Z +LlcwphRcbt3WY4uutu+n9n4td6OXmqT+v97ov2YRys9f4yZJZrdtF1mjEc8VVNPTevc2r61jwTV6 +3OwWr0uXs2V8tF6Hp4fe4DcxGQVZHLocrSacev/mw9WuUTmfmUZQKO9q81XfEHuvwf4C/OhKdcVf +eGMz/qDYHLIH9O3m26JOAgZAppRLN68/gbpBGRZn3ecw9aE/gPvPATlkN0hAqH2tPBAolYDOJfmY +U22332gBjA9eAhgOqshssEoTzgxBDvInzeWDGRSQAgJCDXFd8qBFKg8rAEyh4EuKDATAXusynx/e +vs/XqdouyD2/r2OHYB464/PHagehPghSjKp8n3/2gqkGgaUPbdM3pDcUqRAiULQtFKBSr0jpQkSU +wkTIORgyUHG6H18B+nAPl2oeFGxoFBtgLBiVSfNQ7f5davr+q9syyzjS/ZPd/PjuL7Y/oX36wOvM +HmBPR7x16de6p4x4J4BP0YPMKNRATKBJLB9rwd+fi+SUQwnJ83byj4v6j+47PUOj1DrraAyhLxpD +w9BLQNCJ2ImjWmPq+fmKgTcv/wurxbFIKGvO+ORa/ydXx8ft+fb4CMnt2N+cGC2O3TXS4Fgaw+wQ +OPp9J0DJPl+rc6/FJCfbx+cD+S4fgH2HfSejsMh2saFJlSPq9Sal+ucTgdtvFO9sixsKWwEP1YIk +hhB5v2/ds8g7fn8/lQTU0kwRSXohMQ5k8UHfuEKImfHWfLy8CPXB1XuqnQ10NR34ND12hu6gdzI+ +Z/Mu4Pcn1wnyS0QQxyCwTgNHf/3XhbG/U52pV9KGx8A7zbU0bDhiiCMe/ob+FzbZiJDrIHYA7xO2 ++cejX4H9X2cg96fhJT4PYNScnAE8faWNooVh594QP+wcew4vtIWcCCvkdTCTToaKxiuQkIMSIk4Q +UqDiDmfYwoMyGaGudzEOIkMZXQwDteJeBnh/N6ABTCwPzdzory9iePE6uJwBwcHd6vur5HQF8JOP +NfET5j4eD5+OHZD2JKY1S+GAYs8ysA3QkO8TQ1Km5Er8zADBgRMh1J5FERHzoebDxN4hQaWHQ2u1 +Iegnlx6QgeKAGwejoLKUx9geHhHisThyBNWtBKHPxwegLnslcEktRzQTaMTd/c9as67fYCjwa0PV +63PTleSP8ZDf3esPum3I9/IDcy/p4GbXUkEQTVFJ6dk+cYCigEalkBBjPb7/I81PAwwBULEnWa1f +Rji73gvYPZAj/1sCB2KAJ4T1+iHjWT052Y5JQ1rgmVtKgtfl7jKvzcWlxgkD4z4SUxoIDgk1B88e +PsHffpQPTzTWnzOEvf5kd/ZeofYnh6w7oHc3jm3qQ9BT0ChItMWAU+72vJr06ddTS2DGBlMMhUOe +gOgeU/Qj1yE4nNPE4pWQfHoYgztFnD6FrT0YXEUvL5LuTS8UG8B3U/XOCZfN5x0fKdFpzD/hYT7J +8Xp6087t+02Hue8BCNCv7On1bfdaR4MJhNWg9Pf8PI2SneXI5WDCmQPuhxhO7gyYyYe+CqIrMzvp +7MgbkKWdRw3GD9J1DST7REQ9rQklIMQh9/ucA7q/CT0SrQfVL1O0KBz7floUS99S9O4kxaQpQGlE +osWABBhyh9QEYKajtBNRzfbWahrVuNLvjubBN12dsMwLIdIBIerq2E69SkAxA5qc+fMZvmvP9e2I +uttjAqdz2zfkZrBh88ywNQZrOQZjkJZiUhn1V1PLpx9T4h8BcOOpke7kfCVoYp5TXqDhHc9a5KOO +CekId+68oHn3vsV+jlzQ8xvJPcnn8jGYYbT1+QdQ3PKh6C+Hq+ZFOK5wDsDksj5EUmXlkt25skfQ +PCWjPSNmaj189/A4r3mbxzllrwae6Fc6DBvbw5UZepJlUEEgkgYiFIGy8ydaOOTnZtP13FB9fy0M +IE/BhKj+VALGKRExFyUF8oTJaEhplCkCj/M7+edt08J5GIBNCUrQNIr+EomS+UCZIUqHUueuO5gW +lohiIJIQAlASB9ThBuOK0DKBrHMNQsk5pX5FGUEDWUj534+FhKFLQO8fUebeDZUQUo+3gLJPFWC5 +POiIMmPjvFKh4HEgXSWdNsaT3pTkpQIsZHEhkG7g2QEoUEVZ6hg6Xy4oetWNIsA607VJBIHamRQx +GYq5QHTudz8vn3nCO6mWCO3Lbw9kPlfn6j9+c+CDFObedjS9apmZBOSfs46HINHgb+Q6YfJMizYJ +kj88qbJHUud1xHsaoIKOhZR21Dswzsfoce7ungzp0Oc8TV+zoUMN4AfYh4ZHYHQ/lDiTSGkTFWJc +BI8jQJtqUKG6lIfV+zv14h38eWuYQyQ5zvp6enD4qeQcAHgBtFH9rp6ikfL9cfMj3x6EPBUylriy +jRiCMVbT+30+jc1uKiqhAFAKAkICAQ6ZI+zrwcc+LtfJubtCuxkS9kMvOzwIi4ogQlECDbb+X3C2 +0VbKeCbT79kUDEEJQEkDzSmm6Lcynm2PP5ianHAPFuFLhgVplzAqNxuUzBLMwRzLi+7iQ9KSSc0O ++D8/L9DOE1G3LN1L1uxs9oGduHq1w8Qlqdk/f23i5vMm0jppc622naIzqxZ27uO2kVC11pqzVZpB +s2ho2y33+1/r/2P7X+v/A/4XiwF7LEzlSUKf7MvzBQiPi1CvQEQE4IV0f1el7ne0vjIgAP1SAB6v +kYA9pfnCQKEAffK7IREG4gIgAwijtikF8XnV43ganaCMKIERzV9KCGmvZ1uPVsyi+b5ewXIUBKFJ +YIVVlLEKWakOczlKkISrMyMysAyqqE88WLnXvM5rQQt9ts+i/QlLqmImKtFbVtiXC4t9PUO/8yeJ +4GvEpsnp2tpTYxNGjM5c5fNZO0TS9sS3MpmYNsquYuTG3HC+vn+15vjew+jvDu0aEP6e/9/1C+zz +k6+rz+aeYPO0cpUS5mW4mXM0mhHWghlqiE9+YWx4v+n/w0G+/9DnF//j//aL/aL4l2nVh15ovGOj +J9IOfhPgP/jlX/hu/1OzkD/Ts+tKAfYHQQjfYU3+UOq3GpfueGRAnbCayHw50/FGD8Z+gn/kIExs +nfM5bibPgaDhqS1z38KHycqH5wh02h6dD9JfAEP9g/4PgU/qD+f562ZnUOsm+zUaD8p1Jk0mlfV+ +OVDXzbf38cN04YNtX5Yz8kZMmWUGUGlsdFn1ZqKmD6HrA6E2gd28Tg3sxD6M3wdsHUAsakpfQgf6 +5gHyb2BDvhIGznvPfgTqbm05fqTlJF9l40+aOOJ5eXVezpOZIvUZ+fTN8x4J9C93uHuH0PzIePzW +PpaIOq2qCsMSBhWnfkwwPhLN9BDXn7Bu8nwS+Jzk8O4O2fznjFRsW2v+s4EtUWjQUAQ5bT95M1IX +tvXg0U3u3VVuwwQVN15AvZrlKh0wftdSQuvIzzF82ld1PM2vJEZtQeUUhXqozZpT/Xh/28+j7jfw ++k/Gc/LbtARiQUDuj2pJTULMBSv7Ypqky3Boq8L/oG80M9GFDR6fpCipd2yGhjZgvJsuWJEPDy8O +Bq+We5NnblO5MibtUYCi8r0VybuyivLVInn2z546jYMmvZrqahop5BbGntyYaWL6D8x0uiT3CeQv +BHbExEUIUmxqTr33eS2wFmzPlTbYpFHa5S5Kld03x4wx2btTdM3m2ta2u+UNlTSZdjJMU/HWBik2 +tuOBvlDVbJotlZeGVh2SV5Xtk5h9vqzrFHqnIeT8OxfF1CeG5Xm1imilGQAp33Hi61rcREEEuxkN +LrSGxhdguwlQwAP/ceyhuY+1GvRHXeYT8c23rFa0MDxss+0bBKzAiAKIFIJjaFTiGJ+stLTiU30F +o59zhxI3dnQgEYGD/CgWCc3zfKMUCkEqoUSnjtw8XDGExdl7s5EYtoxgJecktaqBAzFANucUL+ah +NKKr9sq1VVKup5HhwUREk4l2tNBWRVkWHQ2zLRrzmaybCm0uzYaT1aMNBs7brZhpSzU9V07kDwCk +AUSOwsiBkz/Z6UpDhTlAwljU8yvcE2slE4fTIdoeLgv7MDCYWWwQeHeCgFpkRdcShMOiFqB9ImvQ +bfb3/f4ezxf9+3b4z2dwB2SCCCj40O5KqSAYiMAKPl5uCSQ3868ThTZvBwCwm+tZdf9o6dWgedxM +WO8gabDQe/YyHTnNEHeg6GT/Xl8/LUfzpOROXgsnx5G/C9UJt1vswhUn50hmE66JPcOH61tzCpuS +pvFB+FkX4MgIy0uITBJUEyhA7ToE9XjZ5xvcSG8gAuPlDMwQ0mZNLb+y0Pta7j0ezAqQ5mlGMwT/ +GEpoooUKQiUoJhiQKEKAOkOR8LUefcTAIYPHc67d7vOwQqQqWz7YHHArDa8rRTOUJmd/dz1fEJl0 +tuI1v4H6S/aPgvs7xj73R7xJZlplFAs88E3ZDW9AGBzD64+atI1lPt8Pz8vbhId/SynBz8Sw+Roz +rVKigj64eQnyv+1sEvyuY5my5/5WjfWT2HUXy5zwy4NvmMMNWq11hZD+fMnL4UgKZhsmKHl0+bLa +gi9tmyvJJ6mSaQqQ5smzyKBuzo4dSqPK0ZVHeamZM3bjCjDnbuDA0qBTKS8Um02220ZGIxcTZuUq +pxHjwLUA9bC7QoMHlGHy95gAbsCVUPA8S7nhJqYF2Zd7lMl7h1h2t42+s3siB9Id/qDr2rwIS84e +Oh1DWzfD3ASSCASCUQpSNn0xQdQAKdYzacu13SP3wdnAxrfIAUYAoQKMkk8zhmp1W2zS4NPGEQj6 +hXl3+vDtyoLJ58N9Jb2EOFgd1vHNd2FN/ZNm38mXqGI0Fw4g6hNk54zJGSRWLFPPt5ctbmi1YRRr +YfIhThvne7e32CY/ebkVAdclEo2baSpJfiGCa/AG2uE1YCg/ENCNOJFsAYGR6w376ma6WDxOQOmA +GFmBIASCDAOHu3k9XqBjM0gJJQYmJkfHS5HKHK131HzTT3QmAJWgHZwtCWFvLI68aZ5tsQfQGR45 +jhUEJ3R5b6mUIsHmhrSzBuBgbe9tpVtZmC9kp97gRlME6HqRsAgkAiGpn8LGADCwKYNU6yQoIkH4 +URoLdgWqZk4csPBap7paAB7YMQWCrru44J0I4NcI3KbgBpRXf79RM2GguW8kIsd6G4RYKG6fTMQ/ +jVw1WRb2asqLQtMzDdoANABADQ6DK7eRqohz3IFwyhp5UWjj1rUrHn2ypJM2lp0LEFUi0eU3Yfph +xM53nq/oJHl67udpHpTrugSZy0M87efFtNLj96gd5UFv6e7158CPXrtCB4zgJcO40+LgjyZd0HiO +Y/C9Dh5CBzs+oB2GLMCHCJ3INRejFwSWGUp5TprR6Z1aYaS6TvopFcSKJcQ5JaZmqs+Hucth6hde +chrkcxspocfKWFtdSUGKupqI7KXhKIwz2sijgyg1xvlHuk1kdedSR79+wcpCPHGQDpAO4w93cxZ+ +EhcIrpmTedU7YQ2hgTcBp7MQ7piRS11Aay0kWWSGnYPt204bG9U0EZXe7A9BtldOGFdJEcdlP+vu +7+FnuARflg5NNFGgNBdUp68duL9UOesWbQ9ZguPkQOj7QnI8R2NuBCXnYyvM67Imx7XnGyhgvEQv +HGKHj3ySBql8YXRHsXz3mB152Mhj587+OxTNR9uDiXkevOo+NHzhg+w8+b1F9e1eTV6Fr2dZpK3t +/DoYW2XPR9C2eiowPHjktusFt5cCCxJLAehAUICeLIbHFB8iPEXlyI9wzzDEsILP+Pm9wdEO/rAE +AAAO3cqvRc8XnXp8NiWaYQcsXwsAovqGaopgHGrodTMJ/bWvnniNExHo+t60hTz7a5XuJvO3gITY +7K0gN8LOWEhQFhfYQw1OEiUF5PQf3tyO+CiT6dO3aQ6LkyiBm1UvSLaQkm4SuKGoSlu3qtZTZUtJ +AGoJFe0KrlFgwPoHXK0RcbXIm9Rxd6cHq5g6811wJt8kOBR8EagoJMmeARAJiAUq8G+BszW22aFd +Zhq77WZoyOUU8Mu6ZTUWXcmYFiUwRAcuJTskiqQMuHK2agRCD0mkkksHeXZ4TABF4hQmEqZQF4Ic +gVe7idu9CGINeHmZpIsiHBE9RMcq+lLOWmjqZiG6e3gts4fJaX13ue0qzp7PNXBl5xPVaY3fv1nP +bWuGVMJPZEZIPy/HWc3vO/OnNWapg9oIYLSY4+NjhWIKEJltoeBeRVAuonuKCRU5bRipqAwRksiy +IEJM/t4eyIIBSbK6mIrll3sxkzwCqUYJ0vkmOzc4umpodJvcFvEBAiFytX6TIsJFI+3QaxQapPbN +DYDsiQ2j9A16jc6Ba2GwzX8aif1i5A0092M38PZ4bFDfJ6HahxI0rQsFVDElEV8DW3a7u4TxN/DO +SQWQX6TDp8A7+05DwsOJMWjz3gcgQCNFA/Ap7Cks0ooKR64KcS4bFUgBAsUIH2/HB6CmHBQ0HGOP +HBWEMCaKKLAFseLgSW6LCbWDQPWrFBakbQZA7FS2akTT3Iv3Zs8gigJ6DWx6EbBsg8wuicG6bkiU +2iD+foBrRsNLeyDEhg3KA69yzIsUimaGtxyQ6gIHOhTbEihVWGk6BLEevt7exqclHDsyIZ6E75i5 +UpEsxE16TgiiKIqr7yMEjVRS3o8+fcZ7/3eipHcV79sFmiO2j170Ym/vPO3CVd9vEHxZd8w0zSiU +3oZBxKdOe3mMMDDKRkYy2xQXpgTpuazkZ4dx2GEk9AcTBM4CAJEoBPDz8QPWmHeL191OsZa1FCKQ +6Ja7IuYUvJzIVZslVDddC/YgrTe2KD5kKJUYLNtBQZYcUk2fw4DY4PIYHnm4PZ+t+fe9pvCgavaf +wdo9/DGuhA+oPA56nrT2ICe1fcpgAvbqveT7pyWoqUTq79566zMHDALxHYCXbrxiZwQPISAlIXAO +FJBm6hmbwfGRjPCSmjrxp1UEUUu4B1FKjx4B6peHgeBXPcHVToa/s5wn/Y8hJG9fppC7p/16Oj6j +r8VT6T1IeRyB8183rQ7kPsEobejg8+/pO3d6ghznY7sA9yQ7McsLBmNGQeMAyyVPEgpFcQ7x/18G +PzeYBS5xIPd6J78Ik/T9ngA53DHr4Bv7PQ/QH8x8Y/I36I7BOjQbsdyGUKApSvVA1I0Ck79J7b1J +vAwzfQE6BG1n19qHph/RUPnyTu7Hl5dvCYkm55gnGjZhYhO7bfbsHAQIZplhnkYX30eEpREkJNCX +2/KVF/gjUm4l3jvIAVUISnVCPkUreKqhzaYfWEMmvHrzxIMV45xtwhsMQ2R2uCNQF3AQAGywmdBy +xDKMcb9ZetlDR+zaDamXXg5xeZZvBYDUsJb0+sTigVi9BTg1DOHGG/WZeKB60TehqWwFh2eQuZty +R5iQi7MBoFgiw5SYdcQA51x+JcyaYO8yQILTHXdAYB3ceGHq2oZtBPgSbww7tw0YILBH8NYcd/AF +nf37Fw7GHccoUO92UKHbXgk3sM2ykdr6IngHlNvdKJZ8JmKHY6/icg9y9aQbUssowMZPs7+cn1nx +uCfy/D+X0h3C4h17jvgEwQPfh8SWh4GxOXtD09i49Ya01Wj51BSDVfj2XQ7EZYVqESfWUQW0S6Zg +RFkCSKMrBUTGlQzwYZXFmnQrooMNr6dZquaMTGoWJ38g59J2Py1nEC/DtKbHq5hh29Zt8ujz/KNt +8U6+qoDNZhMdWbZRcsKZFBJs1MgwMzT3vt1m0OEhtJ3J7N7ewhtYefgOxD1HuLwaGWYofRhH3edV +0RBV6dq6Cqb9Ps8D1+kE8w7VVXVUT6YPb8fIPfn0Xy8fl16F3wPZB8t+lUeA+zn5uh6QHpHtQIA+ +EGAwdyFwJETft1yhseQhoiQhKlKRmiqKpFEQIsJsh2EAnoN/pTwz6e73e/ty998k63vTRoClMebI +/NlkdBw9IErc6kfbTszEfL7aEERQxeiU2yaj3fYcKrheOqkw2WB4IWwxaPCrMF5KBE+EJgkEksoY +j0Se5hg48sBwsEgxob8O1S/bx3KqLclJAnxvkBz1xF3uvAE1JTQwwPZA6cYAxInC/XTBCg4y4EBX +xce3fa6IDuW3wMVuOugNdu3JrrrYPhfNGNlniDvSnRPneGW8ThfT4+g2Hgj28uQOhqcobdxQ7TWp +hyw353RZIze0az1CHgzXd5BuflzMEFVQFA57O75yF9R/jtOow4knBy4OaddWBQZ3gkh4PpQ2Jy7Q +6dZfWPLfpy5YWdPRZipO5gFgwRUQQOKVHlyEH9WFpQqJAeges+n5r36+Pqx+HgO6M/UJI1Bg7KdC +Onj51eo/zz24tbv0fXWCojvzXx7FHZ1OritjqfbvbiuyuzhfXhI22/SPAMsLQ7PgOY0T0WiCR5oU +NnzdzeqjBJxEcrcCrFh4kTDuAoeS4U9AKjJvyZGDfZ02xPonL7cUDvJEPAjcwKVTqpc35vrSydnM +2wGwQHnJ27b9Nc5mczSdSlvFncLkqD66csFW3xzEyyxWbMQttWwwYP0zQel3NidOxZymwfQ8/LYh +2gEyePkAB4yTPHocrLx17Qh3cuz6jSXIH03IQ3PtdI+OxXg0xJTERHh06PojURA0/glxvK6J9OHC +sn1+lkZwk4t83ke72bfjrce+zmhg6qPfgcRxQjEkZL2dMzXQ7cmjnyU6JqkDuIHpgFoAmgkEIIAq +H5jl8PVy+XiRXYltCR0CWA/E4JFBRER7gUeE8oU9Rx4bHfDef2L1fATlPhERz2n3vbJA6+mXOTze +JAYVP4mFO17vn+TbcOP3+zqgQKcvBARU8UD2TvnrYLtsSeAeZn7J5fHi329Q3DrZDow7uPqQNjLY +6BkPDLH8/OHfzpbcpfUT+J2vJFBRHIgfWMoJgsJrSioj0KtrCiGC2IzmfVzIXaGyH3siMJ9CGLNN +TinJDQmKakUlpzuFhmSUIRCH2UkU0zMyRznjsGtAKzIhCAgoAOXYgVAADPX48/+iLp2lcc/+TJm6 +u4HuCFPt3pz+Yt4+kkWruoiOP0A6uODL5C0dPfjdk78YxoUJNCAc/b7rlHPXeAopvslI2d44QjZs +z4pFNVWucbCO2YdOXUP8hmfX/H8x4Xe942cq6FCdD/HZ6iIu565+h35X+s+F4b0TzpNWKm9f02de +KrqUkKiPCKAJLxJDKFBCqIEECazf3qvMdBmqXfMAcyXQyLCf7RFhghmmmv3gNDHBDRHn1+X1WsUs +bnbiD8BHnf589L1CyR9XvALciLQfY/4WKI/ZKouoUoRJke0KfTxYoA/v/d2zt/Q19PU7dfc8g8yH +0/no53WtKmTENPT0pFAuZouhXp4leqMg7R22VDVAWieg56IIoKahkqZYRTTgnS6mCki0You4NBcS +QBNFLUE00Afo+d7RHns56V7jG2GkNGIKAOgQoWinQFE9tNGnVVBFJMQk1thpIqCSQSKNtPbI5KM+ +PX89zbzMdKigofvOej/iyBDq/wfGfZer3+vps7SJ+cB+U8yHWQPhInP5nce30qCYEaQ4KAwZt4yK +6ZFzTAiRYpsuhyAetQ9mGgRJ9oecVdYzIymKWlppz05oqWpnfouEESCoz6ELbKdj6sITbRQggyWa +zE879nG0MNbCJgOTezHYaGxa60GW6YCxHbnvrNzcKXRNZt9fGajlKwHqinNUUlLHtR4S+MeRFGPW +N3J89ch7h8OBKRiiXzcKiImQMk5eH10vnPGHgeAexUVPeTwZCH56BNaFcfh5YivMALz9eGT1R1ie +Cdc66TMGAcnppkEOcakFoiE4REikQRghOJUAol0uOgVfPDfg6+/U9GTYaAdywdg0S+oToQPjgBuU +cnmRYeqvzc4eBqeojQr4qJwFXbH0ihHCpAqXwQatFF6h9ZHugCX6D0HNbQN8CHy3BIezfBXo7JsQ +FnwocJNoOwhdl/rmYU1vQ6IarRYmrQj8UKadLSYNTMsKLCso5SwogYypJtkK70Rfd1zaenOERrtx +oeZOsUw1JQeQ4YB1e5o4NpxpTzPbGzy64WH6XH3Pm8idwhwIgP5Pp2BJdaf9cMJpNsRJI0BeFDER +PhmEZYd8O28fG81fYfWKwa2pFgIqKcxclmQYBZIgjBQIKUF0YwUYgOjx4zb8Uewy9Gnr7d7Y+zz8 +vPv5pZhIBmuBAGdBjCEgIoGwgkAgH8vk5cbE0cndjkOAz9V/PnrQXOGEKR/SoOIOmJkDOHN8mxCV +MXGMmFiRqJ7IS/tHveMKAGguNIGvWI+W+tbU/cwmzDRqbYrwpahaWCzp3CVhhOsycJOxu8uOC7RC +pOTkT8+tV/GfCQb7gLqtlASCILEFUEcgDoV7bzsAKBABrIQPech19/hnBYRhHvIPmlQ84BoRDgIL +ZicBFS+HBmjasZxhQ7LzsE3pFN4gggX9A+rL3LZz5qQnKtwAwkGRddW9dlldip5alk9ZQUHiBJgh +JfhPv1+f9F9bx9+zjrv9/GzQsIX4SuSjshTCRMsP9MB5tM1AOrCh8aHXpdaSfu7ce35ofNCwLeDC +llv0QmfN/Fx5z68UH6kHYMVUpFTbFYqSJCxCjRELSsOvv7vDPNJBAoNUIQcBBQMLd0Qs5uA4EjuK +zHZJPkVBs5DwT/3gVCwdIHp+PAZERP/jFGPfSBsG/m0NegoXwVTpOroFK9EQlG4fzA+xPzKn5H4y +GBTUf2YzO/2HznTgEPo/BeIqTeLm16PW/vLSdXX7NLVm9a/ywyNHG/LcOzF/r0DG57vYtBvaeCiD +wBJw/aSH8OhGIfgj8OQTwC6kHBetT7EuT4B/sp1nL/Y+rpoOivcypF2tMSsWLFIkoWRTWIgosZGC +AoCDAWjj7YO73Pdhq8uU/X64RPuu3W1zTYca/g8JvHfh97x2Ps73um8KMccT5Op9AdFb2Z68JBJB +NH+f5f6/pnx7d3zcd/3B17DIRkQFJAR9zKhKXMm7Dgno1QX7caA/yY1SlJ8tVKn+u9DyqqIqIgij +PdS1RgXfeZbjzhHdaykyHIKKSoVULbC2WAjA5IVBGC/D6jM2UCbiEEP9HlY5IzkOntm3S6L3D35f +qMTAgoQhhjpODuvv7dXHo/2Z9cdecVZnAVMPwfYvoyIMkBMEYEGipyANZnrY933F00J2JS5P8dH9 +O4lJi/4ettLlpEzj2Hy/VsSTbqm38C0QtKIKZLrtSIc4Z108POds49nr+jsG1TJXyqC/I7Kei3wZ +46NeTsfA88TCS87P5x4z950PmqWlGCWJClKSGAIIIYiUiIgJAiXWZKUAUYh1aIH3D1zWGL2Pq+HQ +PJUo8owIUwYAqqAooopFoSJEJ2TSFUUjTQFFKxEwRU0URUFClDTgnq6HTtwnAG3tMEybxoJI9pzo +tZD58geUBhAdv84CkoSWGSQeDubUDzeRVJ0M0yxCVQV7tRTXpqUW2y8UNQ2UZer6jfeh9vWaUOfd +XKdhDYPhHlvqfy5TzGYkeUPEwIaOS8pIIKqpokqaqCVpgoaYzZ5p+x+Hx9vY6nZDt/YvKf+Px4AD +YBxPFliodhvEYDfb0ZIopIfmDLzSRIiSSBKr4BqsGEoF2jTj7t/hOAM/XDm6PgSeny3Dubqv9ETD +ekVVNFXXi4vxYZuugCjkKOmWrB3PvyWGSpMDIjgGalM0MBSXjRmzb7NNdEhXjnAliuXOoF4tzG6/ +fyuve+Xn9NLfm9W7Nd5wdas3f00isoSL1gt5+zCNsrouycTuDtQOJgo7PGgSyF0CrOkEsCFKhIyK +RyHK92VT1/ocOoOAFwSxEsRkw2hO62xGnCAMgCYSnyqYiQqCiKIrWKn/mR37N4U0xs+b+J9un+I2 +5nf4/LwbmOVX/MI+iE1FDQvyg/rIDqVWlU0nf+mCvskOK3gmHfo67QJwgGaNHE+xKPLoyHIEfXfR +z0Yh4M6NZMhyhzIZtAEP48YGFNGTxNoH6f4PoIeAw5MPye/tfDc9jvnQ57H+EhMp5rwl7Rks4Tyb +sGsHzByDjg9Mh4IAfow4PLudHlI+PkeTF3GjFacDJMk1Zz95jrSKdcNZhDaTQjMRN920y+s283pu +V81T5Y/f97tz9jv+3cbDeZHy30PMLqHv1p+461XNjY1M50InT2ZPr5mM7VQ2nXeh3y+t9YOAO2/Z +xA6wrgcLF4qPr8K5wc4ZbIppWmaTEgPhZ3KOLy4RrRCOpEwtOzDJU6Ca2LDw0NbA9nJofcyjk+ke +E+z0vu95me8R2/N3KFgJBKUChDectwuQtikP2cEDGQwTrTraPjqhlhNWS8rUE0FfPaiOh0IcMNZt +hdroQfSKSPUvfg56X2JpOIoKSioiiEipkiCgiWqKYqgIoipaWpKkZJImgkoJKJloaIppZimYiSKP +nOGGCqpIJaUISSJRpmmIpiimJZpiGQiSkpgiaCKoYiLXHAHHpThfgbLHKmXoXz3qGgSRFgY3ZvRR +YgcwlmJs8t4780/xliY2YJIiohOg0tPC8UA9fCTDwjy23HGaDvfSExkUTvAEjgj0Fv3ckEyUqFuc +jABrwCgO6lADylVopm1C8h7/Wdj2D7Rx8g0J0ho93zenyD6C4TAPf3X1VRElTNxCFt+kT695L83K +54kp9PTPj2wYmVtfnaW178pseMhzYfQihVEgkjQ1EMqUFDUhAwERSUAxKUDShQlE1NKFsiZIkVJS +DVMQEQjSieSdT4NvlXDV7HzcA8LcYnJyh/sHlQHoibop88jXyD/s90PnMTBDZxXT7PuAxX1egO2d +fQftkL9f4fEteh9g58faTBBDBPsPeR9htPTD6Pn78NNTRUAkEc9DeFKWzIpJiSYdMFxWQyjpA0ls +jhyOBomhEpGlB/T8fuRPuI3cJ+xePce559HeWWfT/Hr5HU+zA+veu5GOKnpN8ZLuRmA2CvHpD//f +FjxveOZhj4OteGa6cL8n6kU+mT0ZiZCIRB9/6QfYaTxPr+OncDT00qB7Ce17g4QOyx8pTQPwl+fq +AnvSJZ/KVyr8LKqX/aX6NGLQ1ARQQhNItLBUMLDRyWux9cNJDNEsQE0kQUqTC3hHZNL+S9TE9SEI +QRQ00SSUBqIA2tGcjZPh6swsSoYriDoYecA4flSH5KG49XAns6q+nn0a+XofED8zgp5f2nq6vqpD +2pRk3cZO/ikO+QPO753ShniKBiePGtD8eXYJyZx6A+MAe/3e044zFpImiYDtIcuvyZjgWnkyeHcd +GGavKHbV9YazGcjLTgCQ8AGQ2OnTiSFnYKzSTODzefQ+PedulMtPI8us66OdqiS4ZMRgICd4cE/V +gd+xt7PSefoX3J1jAPY6Iefo2ofawtLgDCX01DDx9NorXw+09ngAcAmKLM7rJ8h87ZvGZ/Z28fP0 +77qOqansHbpTt0zGv7MKe4x1YX1yz+fMKFwu5oJ5nUazioajY5/ufuHXQu/5lpAAfcb8thkXvVng +HGrHNeUBhZBfeNzpZVys4eUBw6woCjunEUNp6Rn2fkvrGvpL+dNfeECDegISz77Z0k7IiMiArskl +WP67/n+7915fs5/V+ML0/tk94iCetI+B8dlgvVDDcE6yiUA5/X2y10Sdn1xHPo6evz3fROgb/PX5 +bQ9Kfen+GD+F2Nap/NDhFOENbUUEltGN19ejW01koZ7XN7TahmO3r1HQeW5aaucy15V0YLw6TGVJ +c2wqaVn7U0xyhULguJZ6kcIyvxLEkSc49O7FWMWHRFPq7/foi4x9W/Jbtb0lvJ1CTOMW2WbFHkAK +DWQF7xy9MTyPrktfh4bu+nvi7bhN7O8YCY8+DPyaiSAECD4Yen1CJYD4ER5/pOitQMSACUkcBTPl +GS00TrMXrgu1H9v6n6l9Uv3Qjko0gkHv2Ya1Kdtnv/VZIwAzA54N+qWImUKWXMFDCz2fmAflA/M/ +SNAYIfLSdAPH7vMHEyadJjCYkwSCgU81C57d9GJoTsne4u1wl8/l6MqC0r0k5jCcgUwJTITmlFno +ztkA74mG1Jc874wJ5CEYIoMJTMJVMMB15Qd/8+PXtXrPsh7xqRPU9/GOnZAd0R6Jx8XvpAfVAhXZ +8vYcc90fMgeqPZDijoG+oqnUBxDoLC0nt/09XkF699kf7SPzdP0DXt9MDeB4sCaA0yfv8/r+yM8x +PwH0Sf0jsnfsc6QT3/p5uln5tpCePmDi/KzgeVT3g/QTVEVQUEZ4QFLIQ0CuxHQYp9BJ466do9MP +eJg+pPp4YgnyB48ucH0+hIJj3SQED/bI+s+B1Ro2zRL3Jy8SGOBi28QwSZe3FSBYLudteRsdAOwg +iDo78fIDwjYCP806soX9k9S/dx6PWeoD2YqJ8j+4K+4+SB85JTyI+71oeEB9PtgIImGkZVWQKBpo +IlgxU7yL49DH0F9nPoPsq7/Vn9jqg9bsPtDkAmiH/HY9Ph5B7e/6RjTvA8DA/XYKr6mkRDuIfBED +2noOe/HrCgiey8x/zyk2PSE7eBnLASXulNwOZcTZnuY1MMa/DNKg76Ou1yhMi5JeREXl4fUhUldW +gIzyNjR36mzr24ZBQM2xwlWApSJYqar52GloKRoQuuO41J05UD0cLgsTBJMJyyHz6cBU5+nWugYe +B7hA1AnylBrJTEzAxqA/K9rvO3bqheOK+7sHhyV0EJO57vYXxw7ZA1AdQ0wcNl7DbUvXPOkEMQ0R +rNaQ1yG3Xk+zsIco7RlvMOoJ08obn5efgL50RQFC0UNFNJS0Sn4DSd9RdFWCf4C7GX0nrkZlxAKe +gJiKU8LhPRU3rkyBKPwhiV/SNQGoUydypu3IFLEDtCgJXUJTALhEcZ4nw5fnxGCJA8vKckLbO8kP +qjSBw/S/2ok2/jHSk9nbW96fpNXv2cb05clRGBt9moICaOoPtT2pVrYFPkoPuIfH5iwRGToaO7k/ +bfSAdU7/KJznUhDx+WT1cH8b27xJq0kPv13dQ+jggbnBO/5zTaU/PY27kI/Yc8Pkq/cwHqHoL6AR +MEKB/v+vXjrHpPPDXqJOnvzUZO0Q6PLmjybKdgj59aafXw88dfqP9WtZbZI9jakIoryM5lVykqCW +U4XOThcLuCT47dVP9pqe91du2xp1BrZBHGrbUrA27pqZRpmBj7szTrMaqyYyttDGCT0M6uPOb4gf +QnvnEU+QiZPTHvh49tiU3oec0oxMBwvX8YmVJFAFwm4ROQv2ECyC9olyXBpcpmN5tFyUrHlczlc1 +IHP5fHMOfDucmy4pkcxnLrilwYN1ZRXWHpJkN3TN0mM3FFSMTGEZhR7UP7d22CUULArNrTdnCVMZ +RCLD63bE1P5/8899H+SgiDJ32WMvYPkdscYhQWIKIuAM4BygOVk9XBjXynm3zVMQzHaGAxiIlLmC +BgYMSChP0sl1afM3f8Zc2TwErzQJ75J7FoLPhmIEr9x/EPwOXqOGCz9TrgSs3p83NXX3pmtFyYuP +S5gyOioIVP1CpcBYSHMANxhxej1YeR5b0njH8k2r6OR9ixHqT0b+3PYdg6+OHKHry6v3wBynD3gN +jtxR37XoGTyOXaZ2J3E6pCIhDIfxZ1k8f/vXvOZ0YDBDOLaQittZDTQfhCnQeKT/VKLDaaKUUPUQ +ontbRtKUeGHhF344Px7310+zb1B8yeGPng+uqL2Pz7eFmG9sv68YPiVw0UrCGo1RqbJrXGbYBTiy +mGrMYb6ovdTmjDBw9Seo6cIcpKHhz57R26zAM3LtEtIcHgUJDm2xilpijt6l/e9PIQ2bYMPfD6nq +xfLMkmUpjwwPx7dXz2HUNv2GAk4hYXkHvD6ep7L5SZ78Dhiq08HmABpDjfv0Zv3svl6VDB84pGoI +c+gw6egBUX5l6gbG17nqoZzgb4JXw+kN4nXM5XWoasDznOeKQ7TzQh2kD3nlrjf+PmN+j8E9Kj8n +QfT68fSohwdzyI+SejH7vTivtlPZL8zrGT24p6zg8TjT7vfUc6txhDNHlaaJsQ92+2Mx0kQavM1o ++sv9OG2KLWUrYjbSD4dGHrDwNvV7bIcvUyrJpjrLndHUORDwfaMWF+4lxx7LW0Ats334NRQ6wHs+ +VBQnsuijeOIewkDdSRI2xjASh9cq+Sm3EHmA3o7BaTjjOUurC0vT7f1/brof4SOBgWtmxxuEEKLp +O7EfYYgdShFilMouw1maxLqmtqeM7fojtORJOQKIAkJBA8xD65Ufv32XSUJx3E8ExBTHqcDvYi/L +2GoAVDMdLCkuUvKsqs973kK4YnuLyVBZgPa6jtxZh3OcDrsRZ+HPx6+j84AGJ+ISMIQgzqPDgeIi +YO/l5Ugxzwgjw6d7zcXk9U3XBHxSowuGWxND+FdSoQjJvsl64v3DFcCBW9ZYg9Jh7jC4Syxgiaaa +YRQSBZJ3TzCKC4b8wekQponnwfeOnhrmgSDQbiLCFiPT5acvZnuyYKSMGVFHZxTRtuYnSvlQ9HuK +4BDIQA0fJSAUWfZNzOLoL77sbN1aF2ofVBDu+zx5xyIAqRECiFQ8Mfxeg0rqiKI1AlFhmfZq5y2y +NzWhmvVRSA3CpcD7UgGlGwUxRJg68A9JJRNrgiCRAHJOoZA1fILCnXyLImTAyYzYP0iG/PgO69Yc +G0QggegshM8vtDf0a7/N1kEQPQi2tltDuYUlYapZrrt4agUTjPDRmBrgRREoPcQ9Vk0y4ZYHZ4fO +09uxgRvpr4a/XU5mxSsQtNmduZOonk92ySdGGc7o3nMo5A5M6muny8vRA2Dt9E7H7vHjp+aqAzyo +bh0J0Q8k8ExqFSL2SFYY1JKhWDG+Gp8ixh8l0GgInzdx5Ch5IeQxNJQoUqNMlpQ8gHVAsxiMCofT ++PSePYo+ZbjJStrvE7eeb4nOGPM+HfqeMp4yBuE2RqTg4wKCjdfmb6R+NtuJaFCKkIJ+G0B0DSUU +Oe3TcPncT5FAUeFiB69SenPAdbjjScxaxNIS8sA+3rwc4S4RuO3L6UOCeLm2+PXDyzDr7U86hfhH +3lPf4/ofT6PSET6yPr0ew0huwD1y4ECbx9jkS/hMlsITYBKiaYL5/Zvkinoy2QW0oq+f0+74EJxw +FOGTcnTyApPb1PwMj1QRkElg1drWrfIddEm/FJMlXMoi5mBToFWFU6Tofeq0yH6v1fq53/XEMMBu +evcy4ommhoDwEwdixM/QvcCHh7lXO67zF00CRNCMKumdWkT+H6MlPZg27Sm1K9VfNXo61F+WWVCF +go3ucSykSMGKc72Z6IsFA9768dj9trqkwDB2xMf9ZXJoOS/tKeO8KCtsFeHtyJ1asIKJZODy1zt0 +uLsYzNFF3J7tttP9648/Pc88iBDx4fevc3+3jP1+atKCP3huobdRlPLwLzdwJ3lXWp5FueP9L3gE +TMYbdrmxdItR3tyc7toJrNsYZhkeTNPMu4stIl04LokGHDuk1IKBWogQ0JA1boRTEoW3NTDNGY61 +L7Tth2HZE3SvSjxB+luv09jPciNEorMiQeH3uKTe/bH3nvC8J8l7blkmZPCejs2UE7net1sZRFRF +FUFETlAVHkER51FNScR54jP8KmkoKxm7OqKbcbYPPhlH4mGK0s2ahjN+cLsUsWBn6WaMQtTqhcYE +MFNUaXSWrGi1omwVhNrY1dnSfTrcjIRL0axL+n/N3kvMnMPJUO5rf6h6tJ08H/DkT+P8eTtFVXml +RdowepPn+z/KtKdOE746X5RsLUHV4LD9k2ubXomBrA9XBo3Nyxm6F9RLXFtDW1APt1hqOElQPhvF +U80P5cnhwGzBxO3Loyf3f84fwk8F8FOnoD06RfCMgTIKHSaRClNLoqqA6N7kyvp+Q6P2hLfywOsa +MxShIOV1Gk3ehOwQ8DxDAB2/wIFg+nXYxhDcwM81oez3g174QCrdUsgxIcUg3+S8vhHy7D29PGWn +0Jyo0JEkOh/OHZ2LWQSFsP7YhWW0F7CZwRDl3BUi3eSKiKQIbLZC9ezAgsB6jBiRElRyy8JUWcZS +rhcuGVzr121tjs76MffDj4fGex6vXt73vPvfH3nuEo2H1zwWKkw0PAMNDRPEyYH8CmMC1XzEuQDO +PKe3tbjU7OAb6J6RaO3R3efnj2dvIZRzozN6gugJPsPZU0NXuDpfe6xp0L7jje6r+XBlaQqEJCTo +/uQFBEAUhvkhvk73S+SFuOTrZwGgNGXnjAmbuKUyHEhxtUnyCId4bCRPiZKR0NUgxIocY4uHmU3D +sPhgp3WO/dA/R+AfD4kwYj4N5A+GxlfEKo0khBS/Mo9z7ExUTQ0BCm4IIT5x0c2QmAu4ClUgJCSq +gpoagiImaiVIlZQ8+5gXiVIecU+WYrhXox+Cj4imeq6d+y3bDDZtPVvRRY+uh23kNw4KHGx3cTel +FiELoA996juoxlItAlLQDSRIg+kgK6GCpKBDQBpaE+/5Q85SSBDyDADh1hoAsH1hAcR98gdQFfE4 +2IgViJOvdNqzuwrohtEi+brsGVJQ1EFJQoROBtxEyQ9H/PzfdNL06T9QL/L5vN/4/3PEPX1F7R4k ++CP1EB6p+ZxKzNrG2Gh/GO5N5KPQdAYykgHWFWZBmgpqChKpIlInRA/T5n8wOTiTLSRTUNNtIMMO +AkM1JMJEB6n16Qe+10AHwkB8xGwPkUmtJfOa2jHvZ8jNAUT7NGItZEpA9tIJgwB5yIO9wHuUyhCD +3QGWtK6Oq5oAj3EtKcL02m9thiBs3W2BbRGKFApYWgHt9odeqfE81k1yPnOnY0+fdWTzB8UNnlwb +OVS/Z6P9dcm78y42FDlURlf0Ve9iz2IZYoOf5wHM53U49Gn+Nzd5PzpHN0aP5ifnyvXRHSO4/T0n +pYsMnt2eAff6whi1Pq+Skn6sk2r1ns57CHVO6+vp9+05M4DpDlfCJKwjbskqH8fpMm2jbubFnvST +b+N3cahCpsxdMzpdGWSXe6Gmr53RbJyYHV4eYl5ebno0wCvC8juSOWeYBcmTiv0XADAQiCogKUIq +US+V5Rn3l+MaPbV/9s+D1+sX7mUXV5tJ3F+bQOpyDU9ydRAnvD9dfRcgCfTeE7T037/i2VKsZdWN +wuUwzMmWwrmOYKSoVhiH0mta1C5ak1DD+f7O2f2D+HX7PP9c8Z+Xzf1+P0D+g+8d9POjYdkiDg9Y +a3F5h59zejCT7k5ZoRMNw2ZgGcsPOa1how/L3es59PWGZ22y2jdmm7hePzv9zxe++PJhFFltruYQ +6fuzkBz0erv1kJIPl6M72eO9rtPkvlQrPkbFS50YgKP9/cen7rYOHsJviUDI8AEMU+PT/OdSf4R5 +wc7zqDzp/iT24QP83+u1N/6Pj0ECxoIeCG9pUgAED0DaT3IjzE+fuHP9iMlmvvT3nwHMVwbjiWsb +QncwriF9Yek/cpJ4G+xvY8mc+9gr4gni3MYmUWTBEt7ST3WZ0bO0Q9RwUoomiCiowWLmapy2PQuD +Tp1Q7mdzWcvyN+07Hf6Ak9CQAvzwGlaV7Z3nJxnm9BpfI8KaYa2c20SmByTS40no9l0cjYOG5rKz +dEynMUaJoR7wFaE6AtmdEEEQBKTBiJUx5HEdJ5cd2GktrxfsfyS6+6Z22waqYj16y7cIZa2YYvrZ +KTW+6du/YvKEhzsBjU7WpsHGEKGYcxIzSZzge4/u9v6aWkgRSZbI1KeFDMIA2G8Wm5FBw3ynUIeM +iBSF9GKZQSV/tKtBqiB6SiOyminpdAUrExAEkLBDEmngkOBlApRgihIkIqBiVomDrSdKGlWYIJaU +OgNIRUtCusQFExMKhQpdtAEiJRRUMKsSz9KdnF306AaDdF6mIIwLfqxA8vMS8SSKqSDUnutgdB6q +cF24K6Y8S64Ty3jvoHpNwvg7DIeEjEl6Xr9Q7fi+bgN4GHmQDobFiPdfEDQkUOuu7WgN+3F+arvM +6BwHiZOPCk3eEkOeIJdFmPVWnlTCiFTA8jwCHeLDYZFKwGXffkNDRQvWqEKUmF0D0RjBQFGMFnrn +ehh4YTsB46vhzLgPgM2Tg+P8Tg6+YJ5uKLS8hDAI956rA7M2BOQbQ1OSQ9kU2opyCDLxTuTJy+Hv +D4JbaN2cXF9P1IweccvS2989589VIEVDW22waA2VgXNbZQ7QNyTAZN9ZxbdsQuzQaNKQsPq9773e +wOpNBQnUv4HXn8+S9e1DbKsZNzl0bBUGXyL4GuuG2KWLOxe4plXONiprfAlZtprhbt73s+929326 +UU4+BRFRTkeZq7BBB84dURMR7kwUyJRfpMvd9E20T2ucBkiJKLz5BFKU+KQo+eFrACM9QfPT3b7T +DbLjrgNjNnkavA8VMj4om+PLHigNRiHMPlLjvrGmLNmvptZG1sL3XuAJhpPhGewZwzv4ybmhjzIS +PL2Pa2Etabaobm4KqqANFBPjGTPo3w94Tul1Cwg3PrTePRUdlJutZJzcQHjvAywbUOJ7nbr/p+GH +qF+ihiCO4HoD2LwPhIUJEnYY+br7BX5W/InRxaphgWlbLJWsixtOnhm7DyH/H/2L7vLJVLCXKR9L +FBTLQytrlscpVtVQ+38eOv3/J58s+0qmyucb7XYl/Wmyg1DeJQGoVVtVxX2VPXt8MmrLAWaOGN2D +AM7TD2+ZebyWCdcW6nK8JiUKb/r5DwvERgViv06/kD6K7fOaz+P6Ecf30fxN7ncSThHVV1F97wld +S8PBz/sgCc36ZXhx8gVOKPG3U8dHeiBs47Txktu++jT1KfaefnnrcgQJtOjbRbCINwvaB1y1AeRG +VVHWClx7zJEh0B/bJ/Nx8wf9MatYNOaQOwIgox89K5Ci3OHb6VQHIOxFTIBI+37f7dNBI8GQCw2M +JP2MApBKIUZAKnFnFLB36IH+Jh6A1V2qKv4q0pujQSCQA2SLW8Lx7hEOIfObDG+8Rm1+AwU1QGJE +HX3LIB/L5fgM18v0fqOMHsAiz6ZHqA6ChN6sZYWg7y7Num8MlO9nq2zJfQU+XDu5dZwMYyTyaT+5 +o+hB1/qJXkr9QQBqET1whn++LqR/H1YmCknXR6uDt/S3YSyHVP9/8dJnezpuBYyT1IGpWJApMhPm +32PzsOM/fOlo5sDt2KxR9ojrzYeaTI1IpXjbNTVDMPOLqienR9v3/R4fk7HJu+irPbnqgN+0PX0C +T+6SGpt9iBwIvFog1povJs+5nH0bA93v6unrGFFT7Ty+YENLuaf06qYfH0mQ7a7g9EhZycR7X8cs +YPgKw0IHNk34TAPjFXHw9/r8vnX+0vXt4ndT2BV84wqGSUFA0ufBT3gobR17ZA/0YwRHsSHTDEay +kOoWkZmbRp4uxIdAJ1FRfUGB7vH2J2TyOyHnj2XvDgSb6oGjyU0ed2PRLiJ5QPHQuRDRJbw441zw +mduO6+XTxrJqIDECC4w7r5BbQo5Qdgfbrfte2HnRXaBAOk+tjwCKmA1IoQ/T+2AcceR7meXd8lwO +4lPMvp9E9upuiJytxUTyQ5Z/yMA8Eg3b7YvcSPYQ+2OhvRkXgWjEvanN+oJphZfCHXJOEOBXZxCk +UToHjE89PR2EYgyAj42lNhmBQDTdA6PZ8h3VJDT8SExJXsh0TrIan8P6eI+T52LHSzxz1mHYufo8 +0jE1mDyNonAmQxGUglkZ03E0XtiySBHxSFkYUo9nPGkKGfBULPZlAiWJleGeHBMI7QQLumlcjhdM +nPs/Y70vXbXe3x1nlhJ+wyMUCqqEOYqe6iDz9wzOiYjDbkaU32zBDc1/ufp+dvO1axLcwRwyZQoJ +KlmKlQFYhoRxiOKL0gIAbBuxYhQP45sUUTcO+4+2z0wiVPjDf9ZoA+ONSGJRfDm5BtuKJA0SlSSQ +qj2MuVRtCrbBxA/y+3f/Pt+06Rn8afUz79c+wZ/2rHEIdfRfHQIrs1Ed727JBfFXSO7Mo8wdmxkQ +IMPd4IyIEy/f4SKcaC9mzd3fKNPKdkCAYGikvYU7ZBeB2EqjN9noM8UT2t7yP4EIuiR+rYJgoEWv +9o5OILsDM40RbIuuahIwXP4dz48rDZ2bfbRuF/EuIKSwNgBHdbNONPZ7xiMX004VPm5cE5lxDR4t +SIel4IB1FAkzpiFqj7pG+dGjclww/AhjsV6CGFTn59Q5YSPYgkkANZA6yTxbW6peUUv7fwH+0n5w +m7h/1o0cbjwsuGf54PqP4z2dYug06Sc2FQOeKUy4AghoIBBGiDUu1G70RIiYKArIDgYaIrLgiGy7 +dnIEm2IeUIIkFgiwDmCTYdMkXont8fHtfq80paFjEKIxSsVCxVUf2H579/9/whP6+/mTdk5oULL5 +866JqJ8pXtJs4P4fvNoPSXiHCK4CV6acWyJA2Zgzd+j/ngyHIQUUnlam9Lhow06OdC8rChrDA31K +m4sdZFrQ/wlQoSSaoqiqGoCCp8nNUhRCQ0SlEpQVTQVFX5Y2CjEAWGNGAng0RUgsTcrzp312InR5 +JyDMU0VHxlIsBXBHWSoIigCxY2h7P2eqcG/h7svASHT0hsc2MPYF9H4f22DZ6w69Z+WC+Dn291N0 +KOpq8wKGacJqBID1xkBW+my1jB8LydDviwbxnAQo8rqIxzZFQXkuJUM1ge78T33pfJ+MyYIu9N0s +cXD+vy3QP73aZZLAtkoznfXysmwkVusx2S2MvsdZ0NtYKtCSHsNs0r+Ce7fWx40vbyPWesecQ+Hg +H7s/R7bqrEQgTKikXygVWi8OyKCBHCkDx85nuROjltZyJPEAApkA5prCnfKImk+at0O21J8lDbIU +RCD8f3+g0R+nZXQzELoR4Pr/0OI+fxe5sjJfee+76NwJQHCmsDD1CIOlxU2TxEKhqFmpWMD/Hjv8 +wbfAJmgJ25dQyT99+JGUEFKDX1dA4fRyonwkBPXx6XX+/SneH+evTNX4Ym7vB4g+UvyAQfQHdl9a +R9N4jM9NdDcTmbJfNOIbBJM9ErE38fLn4mj2QTz2OiAdldIdpV5SyAO6Ht56v2nYfQnuT3veMDZ2 +E9QBgPueXx34cHw49et2enMOEwNG/ZR7BHqwvL2FDkg2OoyUCM/oUWMIoNVIhzu2FtxnKDx6gKQ3 +cBm7kjUcVFy51Ga1Y3h20auZthRMpdZfTpCmTkAXkWMSEkPtgh+Lvrm+GaDJQSePWQ59E6hlpY0C +painLk8zwgURw5xv+ks1Q/gE6yTRwzzBAPs9y5Bu1Xzek5YZ+N3man4B7pCaQL3dPKbJ51PNcQwM +DuOvmOcfb4jA+SWTv8QUSB8H7ySkqIMxSiegSpQAxBFb1SooOgQJL5e7h6dfaGudb1rFPimtHQ40 ++4RPXKmtBw4tRT8g5X1TwHKSqBJy8vKAyO/bteieM9WEO7Y2ksZBcT5qX2sKDttqXWabcUYmF8UY +ckOT2+xu9cieREBHuXojHMMA789eQmgGHp9N/32s49NCcMmrJ0xZDYctNYYGUvm8SGjp6fQR82m+ +feHKbC85D09cDqgV22ptvQ7bWQBZQQNnyDuO4SCFK0lZAKejlt3/OB+ibjKw5Hvocg+fv6e3YNIe +C8iq6W4D6DHvG1Tx+hD3Bx9oFz27nt1kj+Mne5vGDpCn0qD0Pok5Wiqm6T6ABTTAPnAB2/5Scm/e +fIPs7Tv4P4N+V9Tk/gfV2zeneJk3IpgeAdJJsYQEjg5xuz/fiSCYIAaSkVaCKCB+G883T9OpoPe1 +Kzv7tYbO3G2Q8ugsIeplfBHwppumUFqsvhcamzpkhoymVcQOEFBrFPsD/N54lnk4jDptAxkRoAlF +CIwDny79nl5xKEaFopCikghDEMBLpsRaCH+fIuPqG8BjmDMYxBEREQ0B1gxgLVVkZCB2E+sFKKYl +fgH3V4lg/14E8jVBwdUPog4ADu90iJg3bvseHPkr5Dpjh7iOskcbc0YdrmtnaaCarcPqHSG/n29E +A7Iof49Ppj9eymf+fvD1zD3cJTDMLG3MWiuMb1usKlARcbiFEygIl05BcuIhf4ROkIcEMRJj0evG +6pNbP6W8e/Tk3i/e26+dRWhiFKEMXxB73nwkW9nk9BBtEIOgG7njRipfBDOCpHcr+HX9aoXyzAS5 +x5OtOpsGGuOOUydqaNAoDIJx7JyYZQQ6SJd0A7vmY8FRCKmYbWjmXINZZmRTFMW227eld+Q8upc2 +w036ICJJjaoEPJeIl4d0i6KRfY2RsW5djYMY2SCMOxbnPCwbPGuodpp6RWamyMwiMWTdsVbO22s9 +xO5sqy2uomxtlImtsXMMxP0TH9Pp6cpEVk5oKL7zw9LDDzRniJkniTVqYcmF2B2PH7fn4/55cjhg +v29136VQzOl08BsjHz+jP+dSgb2su/JyFuerQsxQTDgvn+seSvhs9owBNUAM+ESbxwxDt5ecabBl +IBKJ7qpm7+5sylkWDGw2bEybSqf4v7/+mb88ohPtLZL7u+/E/zDpx5RCOF+YMZo3+bhFEs84CiGF +RPGXvlkhM6N0ski7HSjwckslFUk6vLlXqUr49+DDQRjIxjperYsaKxmdhNrGRitv839P9v/PH6Uo +MAIpy72HahS7GbMUHgjAzRGIh0QXIoCqwzxb7Lv+lvu2MNme50m2Bq989xse12Q8IlTfh+PTtZHj +rMHpa8bmx/kJEA/fPjqOgPX0ZV6wBy7BFP5iSGvR2msswAEc5BeDai0Fa08auJxTDUUGkrQCDx+E ++nEWNn2rHb09eagVljWHqm9S9o0ne/WO4l5NE37Z0fG9eoGBHitcPCN+t3f8ld9D136+k88DEX+C +GyMhYR32fLBe8DPsXJGmHZHl+Tlo0uF3LImYIf5E+fPvN0PTXe11N5N48hdD/iqIjH6+nCoBppR8 +1wZZq9fNQeLWgdKVwabg27eHcHTDmEYt7rkkE5MNPCa1Q1jh3gdEElwQrQRNmYirt6EqogOLcB5W +hasqOdRrELInLfRmEEWg15V71qkxP0pwz13yOxIp4dcaE9pywPxSB7UtUEF3Ya4o8Eo90CRB2QTQ +lCGk8IdCXDo67h3BIzj5BCbopHUxHzXkdIaI+Xa6C27t0kAlLgSQHBmX06O9Ccoik2ygV8++5gQq +WCoode8t108mvD6fnVbYyfpfvSg7I1g0R4DG5D8Me+kzOk1ju/eEKCD5EaZMSwnEwXn6dw1ABvw/ +b+KH+krr+WxqGiRrrDU2xazl35+iBDq51S4YnydDyw283s3NGj1NcuY/c9DDyZ6T6TZdVsjr4fDP +iDxQaeDRRBwicUR0Dp15IIIkGjR59Zd5LfFU9zm6auy8Xqikhdch9ebgBNYMFvVjAeXMkQVBf6/T +lxg/0NxpdXERc38rmrk7WwYYu9MFl7f3Cd2x3Trdud7GU/idByyRW3yOGFDtHQ3MNBlu4iGcgUXM +mfgpr696Yb97XBe1x5H7/9l/JjRpt9oPrpH7VUivBVTZBpQ4fHORXO4R7pxLKpZ+AaBpAxKpsUMS +oVSSN+lYg9bcLksO3L3Y13ZMPCh/d9M6I9fHbwWPykl2eJf70pgjKafgqQnHQHxIA69t5xv0V2Ov +r8+q7k2XMvLZGo+I3HIJYe3BFP/VuCJMKAqH9UN/FKv33mYzKDIixFjaX7nBUHz5fPjGgCaHvjD8 +POg9xWgCg9kcdHRtRFBAKFBQuMQXTvp7tN5n8hfTGVv98nr79SIaiYeDrIF5r/CX3Qr9PuX6n+MB +H6Gg2V8s+838XpsPwUeAl0BsBQ/BhPmPuh/vkaCB3PMsCUGe/zR+0/iZPRDed0e+0lQ4+q8oxXRZ +sIY5XMik0SH03t/XEXkL2cc6VKFNAhmdZMmGAPX/HDLwg9E+Mh5Brv/D4PEp+TBrmaA72DDtajPX +mzqeacHSfUk+w/P3w5hwHKxRtlAG+MKyEX5v20GNrJAoGKBGIZEGf+UZ+wYsgcnfsxY/Hy+MKC/z +KfQT6aOrqiUQUSYV43lGU9EqpTnXPpCkqCRRyg2d+X87HfICpr+bwb6/qKOH1vbNGndd+qdMcXtu +pxj9X+HcidRnqzWXzpISL06y0hGjGLzCQpI+Dex5yjxvG8BpcKpt7lA0/SrVtm+cQ7NC4yzLpKI4 +bA6fn7eyKWEgUXehO4Qu6KE/jFTFWf8fy8xFCPh7gev3YvnAX4L+0gYuyMZOOSDj8EbMs3O4UFNO +xrdU3T9IG3YKRWs5iYNGMGqsb3vPoYuxzdhcdSJpCbSMSZBrcg3vQbUCO3ncgWSISfsFCv2YS8qC +/6nX6yKkQdlMJ1cNANph6r5Er4+E/TQTYNNgx88uXiDB+GsZct13uJJYUQxL4CgjYQXZkwTpISrI +eacSbPyLsFxAgholSAT/hvg2CRPLfxjsHpj16hnYKG1LAUw6j0OsYCr97d75CdVlzJWpvxobIiGE +na5LO/irZr6Uy/Tu4oOMPSm3aLsbumbNby/20FQ22gLdgi20AgOIJiKDAFFjEerDYMNdeQ1Fd0x4 +2Jm3PfpZwbRKx8dLSQIJkCq1RnLe9O/WUz9OtdIafWiPPFuBd3mslGNWH5qVFDvQGuSGsjOLbq7G ++qkcIvYtLdAPK29wIuQdDMVUMtnHWtZQlwJtYvizwZjDr3HXCp028EvZet7kwDjlLeUCPTwzdwSC +UJtFxGdAPO2aKdz0g6cuLVm204kOjMc5PecSzRsbZ21t21gqPRFgiGCFJNQMlPGHqSk38/fY/Xpe +LRPYseVPv/SrZqAUTDnjQDMz936j4pzXRXJzUMxTdM629coM5TAXl2MjHZ9xvwPn6eHI5dqcYc7w +w0wzRFFWT15f+/n6n6AmQEEXh88/jfGjjgfD7EAJ7Qqimhy6EwizkMYDhwwdBT6vBJu/tjGLHFA+ +HJxKQ7FsVu0kIxI3Vbi4AkWOvB9Kby/09RP+8i7/6KRDLLzxvcCJCCwUJuika9IFDFv4oYQlZnQp +y7sdwuvk68VFfO+rG7rMQ41wbKezxvCiwT2W8umOSigWkhH9CGev6nklhg41F4/1bpxuZjik5Uhm +m7dbhctOeznbNkwd63awbzqQnd/vz/9fw53ZoUy0VJ5kOKVVMWZ4YMFgOgaxlG/KFcRPaD4x8Px3 +wc9AuGfA8quMSE9xdXwuipwaM7YXKG3UOdrjkOL31MmsX9+KYPIcgnKtcw9aPC64C6ABCRW9Ycmk +GgDswBIIJKlIgETFnvIEDnkUCmZB6ZYSptCxue+JHpAtkwTtEe7u8k/b1VUUoqoJaIpl5QrMbGNo +rBerkRBICGG3mOgejtm52OCG6Sc0bp5Tg0hrAF5GnUdgWIbsGTRa9TkAXSUhIMYLTdColgSGyeDH +STIbuGImLm7336x3Hn/P39/Xy+ECVY6XSh70DPpVEATOGGnFGPBIBqvSSFKG24A/D5x/iqfRdDT9 +mzyimkaoAKApBKUfrCD2n+gOSlClRKy/xOEBMVQ5fcwpw/CB4XX7ruGulAsSWJWE2lKQnzmnUBUD +930ft5UFfZCnqqB58BEIglEJmggGC8x8JNHAEhcagVwlip8H5Qc3lTNf39LgQyncHqI8I9peEMIA +wcg9ZeGtGj+N3tRReyPdaLM58eO7rUuztB8w4AqaXUlg+JJTABSwj6z2dfQeXXjb6gKHjDFLMAMg +KVLv2MTsKcHW5XnTgFIIbWQyHcFEfX+3unvajkleRncJ997sOkrkHg9yw3cdkit5O8MmK9Z0U7ko +dxuXv1HLA/I0Kdee3SzoCMBgwEwes1hozXYDiby+d18r6wxgtjkAu4GiGH0kpu+IVdTAGSmMS/CE +5YQ7RItO92ZhmzneuENmg55MDhx7JxBCwfd7g8n0l4A6Mu4ciwdPP7RDopqRCBH1cHS/EKXlxBIx +N4ewJkA1e0DhqYsNUjjpwc9DoByh04MiOIftJzd5eUn8rzSXfmRFFURBeCmypR6z4pLh7fYegMkV +PPcVjMs9dOsNzV2IH2H2U8U7XkB/Tn7KgE8+j4oiKZOzunQQUO4eogSpohhXyaoCB4vC728sPDRp +ByxLQlYapYsIJ0aIgrfTuCjqno6Q+vYSjz3K64wVDVh340HHAa15o/pCIHxlQDqd1ioPjAaSftwd ++z0URoVOkbeyPCJ15ReO58ugmn40dOoj9fx8tp8RHq8pBT8W7pCm22vzY5ovPTaQ6oeS4hJzAcaA +j8j48JEgEv8+N0AkNhU23iOOuV2/U94gTF1b0OCTJOnP0B01pA0CoIlFHHWddYXxOkw9O56h56/f +7YvJ2eN2Yn42DpJkKGDjDxEvmTy6ew24TI4MkxESfcxu3ibHRU0tWZkR9XcO+lOpl18DdQug7q/b +5P5z4/nZD3vgBxR69MMNrPp+7WbIxgxYqIqTTTUBP4R7g+mPVPskwCRfpMDOnxDCGlew9TtFKJTh +v4YdNGaPoDjknqtFFMEUTMSxEMjIJ0XlwXaSVRQJ7x1seD4cWsr5uPdxri4d69gpl/OnWG832Di2 +VDYbqz2uyJ6t9wymk5oVHdsFXCvBZgjN9GQTJYblNtAS/UElDpk33Opz6S9vV/Ly9fkeAe/8pZzk +0MC1G/YqbEOCoZIGEmiZaA7+foX0eoI9Ed0fq94PoNId3kY50Ai4k+Hf5xdHVg+B7+Osj8z7vYeg +QmtpJ6tOfpmL6/N5joSmd5/fQe8ABCePHfy0jqnDMAPgJQ74TqfP5dD9b7kCnqpbb21jrgbn8d11 +ho3VMB9X+GXP11bhf66dohzBgBExBB1A4DQvBRjJRHzDJGHac4kQKMPGQc1dVV1brz1ab2Vrz0ZW +URNC8ZIPP07xTnLY68jx1zsIOx/Pk9A/G2ZhQIAAGgCKZxEKqYoSupSJ+E0GJK3qIuEQSAcI/Gq3 +B9CZsIHd0Qf6+/tEkgKQ83xXvUBPdEoRL4xC9v8H2nKIgeUiZfJhkt0eXbu/TXxDojraKpR3GFvA +Vu0Qp3WDlPFBXMdiI93r7Y4u2L0md6L7+fHQIS4wU9hz0vF4uf6FouPd1JG+wTK4WT7sWOSCEYSA +F2KgRSn6KJL1ke3r6MBN75WjSzgwnAcEBIFW/lfybo7L3b3R1c8VMnBagCRY9SCANDovOmcRLIsY +GohLqR1YOHrxjnvKu2bEQmA0pSsbmzxZ4fJExhpCAvbJLC7n8WAlKI4RKBTEe892LZvknEQI7er2 +vkDmCAW0fv0AoyKLU7SBs5eQcHhGID4svq3ynirky4aiRYESUe25RJz+uyPuHjPSE8eyv9D67iSE +CnxCDUNyAowcUDkA7rfC+iDqCD9BbUYi+KAV4+F+IB1WMDqIT3mRER6CU/uEO+YD1hcPd7AcOSkv +MCSxwNRS+wAiLIjkoSsAowDApcAQ8iMSxvdp26ev7QAfKVHT+/p7vz6A4MfTeiH2iHcA9zMhpA4k +Yez3FDzPceLv+FhX02qwg1aiypYtRpie1N+GTmiD6Eaoo8gwRKTFRTNFVJQYKBF09SAnJccL90n8 +Z0dOfXCGQPThXrHCfvOFV/BD0M8BBQOA/P+Xz89/w7iwJX1gd3vybHQ4Ef4XjwVTSIQQPmCB9wcj ++sUqRJNDBSxMh/E+1M1+H7TfoQfkJ1PD5/2/swRUcioUA2jkAL3UNYTFhfjQeRHVwn4OmA1G5EBQ +PwA/UQyfB1FXxobntkuvXavH1snsNTW4faSvUe7Ij5H2/P3eUzNFucNjstp6bN8dPu8O3PuB7oYI +g/RsUyuUqxZgMtLLSg3uwv0HM8z10faMJ2Oacq3prWTTiYxa6SKyxYpCLmqunPCY/Td5/2X1wQbh +ydR2cN7/2NTx7p2TEbzJqeCHVV4nlpOPJQR9Z6H90kyo+ycD0dT1zxHuP4vnqgyCdg344Jkzwv1i +yIiveMlMpJ4j066zUNylHdD05Ycv2DA+T66eeJ6OVoumrqoqYHiDqL/gm43f0d38PlPCEA8PUBye +T9pBXAPWBpX0vpH0+viBEAkEAjRPhCK012oB1ncCSg/CkyQBXbSIm7s0NuVE1fPZJ+q6JPTw4pUM +H1sGTfgDxE69O9A8wKDswLH4V2/S7cn7ejPJ05XRmJ7KXDQtMq3OU/mf9ycz/jKSTEkFg5VCEM6s +jcAFzd7KiNxEYqaSHfMJt7+uTGUF0E7U4YkzbB9D4+nAPRyMgeHVaR1OQIjoBUSZg+WSAgJAwVEE +EUFrBSBVOK6amyQTXcpcokweQ4OHa5KnVv+/33DU6MHphfGQECYgLqQjGw+gG4BVHBoBKngKNDIw +RCgQSMSShYAZ5EVKP3elS310C97b1qI7+QWMeYzfOFG0qP6MmWiuWlCSLgFUcKeY+oYqQPb2Cvii +v+9FSIKCEJAIKdM7ZHzccPN7ZQm6dBLvVSnNNRA+efRD6kC+SZhYY1q2lVo4yZTLTIV41pRikFUF +huwrA0a/W9jYEfx/Lpve+ELg4f1EwKco6wj0s4UQ1rMvBAxmJwG22kFhN5wmMXgoBWAL8laXXnNd ++OZtsxEFN4hSWy21GI0FLW2mb725qAm1KpOJOQv3e79v6Ye/5DR2pqygZenDAAdWuZDRyQOmREGZ +6IpCbSESgQxAsJyRg6gwPizie9HM2T8fdy79TlZfgeCtOW+5k/DCmkhR9fH2+YOij5UimnEWLlLL +QaGqSJYcmy5xiP1Zf0xL3ml0EHUlEQZy7ZJ0m3fR/Q5bbRycZow1muHhOjnzsrOnE3NBqlYsVFrK +k8hCV58p59Buj4rdxVN00aNTBdW3jYdGyOuAowKKKnpgbtSeAcD2e753z/hK+qSNEQF4gGArdGgk +RFErRBNWL1ppcIH96qyAjpolAuB7OKEB3SSBUhSk/rTrv9nw/bwB8JNJFA4YV+LFVvMys9joZLSk +pbFMnn9uJxaZQ/FhetttoGl6Hy4VfPc85wmAbs7MlxQmMZgLpv65Y5Dxoc0NggksWbfx8DsAT4Bs +MjlNpENKsq3JL4GhAH7GH5ENuP0Rpm4c2OD5aDTo0bMAbYlkPpP3sfHY+cyBF2X2YJkPc2BMYBmG +A2gHj0ncdsHytPD3JI0aU154dRP2h56fo47Nq2N+l82c9cbH6guWEAI4goIYqPyT6Zz82zfx6chj +8s+XFDCNIKKji8YlAUAko8EXV/EtCYcL19quz2Hv628nl4mTYhBHfQGTpKKQxhamHQMG8MQMQk3q +jinQDyD4+4v4nSuXjoT8vLnp6MnUh9dqgwXf0yTkhwFiVEj3C4h+wFHV2+oixaRD3IAnt80xc8rn +oUcqnb1+321TE0psZMp7U0AosUnGTJhP4iE/FVh5XMwjmIAmIkIAeWIDgU8tPRLk86fXeJvKttpy +5DjaLEE8/360thESBLyIhA7CE9qgniwT7jHZMRsqm2jmqHdMPV/idN8drTTNnlt9bX/SI8es9aDM +OQwF76zj0Z8XJ8oOSfv8poACnAqfA4lQfcqSDW8NijFVKxBHpZuLnJ0lBPAKgoWvF8xRxS+8VE6J +0EeGYDeQmjt526DcRl6+trE7IZXIgxFBzAc30HBBGR74PrjFr8vKgAkyy3FAgVFMqWZM398dP1fO +cbU3InPs9i6+65GRTkmaagKQpJlO998ARIBRCSiAlACQgAEJSPA9NzggyoR0+3tdRhFfLC8ScU7D +NEISazuqOq9EE8W+m7KnfEryMI7rwg/PRNpk168PlNCjv6r+D0x8fy/ewxkcvDqI6D8S2c5HpiIg +3Y+g0roZlYePuONZNI31FfLscHByLlcgBPNYPtefCrpRAXSCUdhcW+Zv7u+96X13WjXp1OuBowp6 +W8kLtc4ATomCwcIdoXKj7ebu5Brc49jF/mMAtzj1nh+gRsis4nAdIPG3gnxL6LAjhkgiywGMcZjU +BjWQHwoJtdsziD0VAQCGy1Y5cSJC1vyLLOloTCRCJn5oIq5LNu6pJKrUgK1cCzIjM4PoDmueL96t +wClDr0+Jj1QnZ5jJsIDoXNFTDfdr3b1NolgI9VNqqBgZ99B1LEBg7HVQArFpF4qUQlEJAIIAatzI +zEEnErOj/ZNuHABL6RoiZrAr0zZFYOA9iEvIMCTY50ilACvAFYxIHhMKAGc1bS/RzTMwGu1d8agD +szAIKr1gvYSjigngEF6yJX0QFKDJPSjBwch7j+XuIZClEapklYRCQoyFCyzt82xkn1zzHpOZREKE +nnATWXyP16hUyn4+F8kPBiP+MMaPTIff6/jXlmG9B09kSIOojo12BOOjZMEwdPoOmLRJ8NQM4+bJ +32JYtfR2lTIPpR6dsPbvv9082Kt5HdTktdhs0ccNQn68ctdTSH9t1YKeeyJu3fEgsv7myg74uqhX +RjHNOLITxSSANF1ZeWuN/VRFjVUOhiigD3cLusYvr3mbm7I8PHWEv3c5QkPB37hb0pgIA1EBOdVQ +DCkSlBlCMgXM1hIVSRxBA850FWIfOS3VepRETxkQJL3r1DD94B9b1AQeyauz1j1mlVwgAXJjuUg9 +kMoCBokTso3avcZhds6fX47Ozq+JHYOde8CU+cJBsxhfzTNpHOu/38twcgGdOrU3w6iIV/7+Fzvl +EQRoCgRAWEWDBhBeEh+R5sndfdZ4//5/y5s/f4afvw89nfJ+rq8PvzgfovKf89mn5QP/7CiQjj/b +S19sn/DhQVTcc8izn09FEOz9uy3QvTDYgi6fvnLklP9Uy6deEuHHZ7ZPjPk8fnLc0dSIOYjtlx4X +eNqoSiAkEoAShKIhKc5SshqvtTqc9wUdLjqcHMOldhI1/Qg4fBUggggkG9zLOXHELp88mt74D+ke +ojIQ+utgEiIvTr+/rduR82elMYfOn/WqBp/4flyoyAqyab76Is7/4/VGK9UgJF4p/KwzT/5ahjIE +ZBcqV4VkfM0kQsKPIGIhSmOKnTgC8w5m2U3R6bohQLMdE1Cng6Qeu1elLmP9bXtKNXrY+w2S57LP +siYsZxiTt+8eioroErkpOQPBTwir/9X4+CR/5Ug3v5c9BpWnvddt9Vvd7DAvDn1SbhRJKBt6eTIi +IKOKgAU/32/8NcUQypkTSlY1e7Zs/dGP1unmr/tf/vH3Yovuvpxi3/OTX8dleAeA/fhj+h9LG+Na +Ijx0U6OYR/1BKqMU8P3X0aZOsgmMKozc5dKHCNyj45lvQMfx/kvxZLp/mFkiWT9I7fqiaIcxa75w +ePnCTw/zMl7l0R/dMuz0GO/gc3l9xAbyP1i7eiOXnImVFjEkoIW/g93C+OAH6I/pOOuFGl0uAD+C +JUYKChBQkgkEg9nX4fwZhZDb9/kmd9menz6JNY0J/lC3K9EKaA0v2/YUCbCkoz5WxcyWgDAQmFum ++FfrIgOBcgCCJQiBFN3LewDkIy7d73EoH1DkPfFR9xZO10wvLR6T9HakU+fVnA18pP5JsI47W9b3 +To/KxCxMBxuW4LeOvocKTiMjxn8A4oN+gT1CTdxRkYNszqqAkoB3S0EEWUCpagOeAgDUJM8AMPzF +IhiMcrLcGkUQ7JdzBp2a+a34aIPcgVRNb2dsw0d4RAGBB0KAcqjJDmTLOItEIUi9NzxOzBaxgHTi +xtlqQf20d2S5+Z2DG4a4q9wzjCB/VagbsNeMeML9IGEsL++YSYRFtHSIWp1kIgpwBQgGXMJGEzKE +mOAsGUXGJWTvTpfnBMGTBDWURIG9sYIOzZ5XWzGpv1hw4S6Pv1Anc794j5kQgfCnRpxTodJmRDcB +MUXZ8dhaD56DkhtWrJwSDCBTSmUIhCEU4MAoQQuphBw1CVAluk1yevC5EaGw8jef8PghjfhbUFaH +GromHjkSLDe7eBTh0NfKGnAJvBwglhUmNjLrZBIrSonSbXqQCN6AGQ3pn6NACwNBtsExf+Hv/DYG +6GdfDshbmOdpGRHhxsQVIMAjVBQ3yq4BYmQUDJiIZ/cpUYnDdqK2GYW+WTNW7t0mCY6YcC5rVH1c +zOpztORUOcTgTZmaLdIujnpRBILaBIIgV4pY0WL2WNLbEfm/nSBB3jVO26Io35LjwUJ8BTAoISLO +AEePA4+cKjYcktQrh5Jtgw5psgjBXkYLApoFabJJECooeDm8ozV7zv1z2N2bhTAlQKPUthikeGAZ +sIAJ4CgMEnrsEE4IbjVqIOAX+KgdcZDg6SZAEINDHeMmeVKCGoa2wRSHkvwXYfZJLAtplos9Ebtw +qYbgMGlojxp0zxuQkLioQe9v+ggVTWXPoRnJ9PF0SJah07YXJMlCoeEE7hDxoBiO2GtGKH1wDvA9 +hMIgTIhxhx8zok34bXqMcZL6UajJDEOBt9MyGCBsgkWnepQcO4mHlSHG66Cdo0ggnQDgEByCr5Aw +3t3kgh5eizbBagxMoHKuAnPeRZZBB9RWKh6Q8EChsmNFDUwDtMnaF5nJFXRobGGyo216cuUhtrsc +/w+PkZtBTMNtfQaKH1BTMjnTMwEu0gCcHiQEJJQsMKbZh46OEUUzvnTyeMmiePLXHfwmb97O9OHw +euVvkjuCS7jRGaTBJJlbeHA4HkBCTz942wp/KahbBATYKBcLUzEEwXIC7EUKIVCQ4vJIBKR86HNK +zA5iJk6NX1sEX35aJHWZcd9z5CFYFE6+Etl4ZL37+O4kInQopII2R18gLouuwc53py8muimjNFnR +PeEVSp0rfrc/YZIDvlnCPQkkg2GNlhqlU4IP3b4S9vLy6MG7W2dBs/XbX45r2vRjPDqDkQAeBQkB +AFxjv1aLQaK+2e5M/UI36FJCyRPGaoeVd6fRV+4ovJbqYKcdQvg/zw6Q1XdupzwBQgQEgO1B54hM +ZfVB8MO4ZfS2dJkC+NCmf1gHe0Sw9C8lGteyoG3qPZEEIdiogOHFHE9Q3Lt1XnxGK/z+ntQPvQBS +Q3NGBfFCp2ZD1bvLYPCeOLoxdWt4+jfD93j84LuqUSJ2lfYfd7HILac/NjXbPnb7AAM/Ljw4ccIy +P+fnj3e89dl+jJg+efH5vFncLffOQyEOD/mCaiFKkGuvQf7bK7OUQnxyasI5/4OMdo7a1lmjI7ox +EQzq6SJOrgK6P6/WDtbINsuEYMVgtO+OLZefyvF4idXx9e8jOZ+ZiEzly8zKyhVMIajhZ46+X/e2 +T5P6fv+NHuaMW4ZXDZxHLQ80FQL3tH95LQXlvk8nLvU/j3X+XP2/DXJFObSsKc4xTC0Fig2qoLMM +WhbmStSoef/nveX+9fL85+nx+KPQ7CRv8/+P+H38Hv0dCFDNN08bbBtdq6QCUIZlZFVUJAmGzHpy +0FSQqGGfLkp+MffmXbnGq36Pl+X9fA8YeZnCvrl9gmWpbbbTGUQy0xUkoWZmZgUUTYPzH26l/W1B +Z0R281mdEtWT89vjli8UxAH9n+mUVf6oRYdVf0W9d7ba7Xn2W/j4fDmJHQK+D4D0Hn5cpZRQq36F +DK4FlDBlZSzFizBmZlVl6KvTn0Z7usWX4nb/N3hv36ygLiCVUcD7e+WAwpFdEMe+yLV6N1kN39Or +cMIXeIhztivxVOxOcAurVkhWDdZHfvnqjx5ZZfiTdr1qwHO9d2STHyL71Hn1iP09L0ZPeUEoYqoN +Zd6S0pKioxV6P/fKKc8+v59u77eWnbb+Vpkx8MHpLDywfNHG+m9TIchcc2N99221yI5furiMY3kY +/1290w1IBJF37msxv05niwFEjw39823KMefwslBihNINwAphxXRXFBp7KedaIQXrlfOOUwgAIwYy +B49kVI7Y8ZCeIvWN7USVV9jc/TGPAemkAYNFBj8++vZNeErevfAZdg/eG9J7n/g56pEEOavqFDss +uOErxRE7VjjDIQ7sZ08Bmn/cA5CCmH/f/rsejvyTD4Pn897QN7pZhsPfWNODsf4kPT0tDcaBimdb +0D3cc4aHTFOJ6s7vuYujq6TwiQVlIGYOGjb3OfgdHxHQPwQxlEAvEIBT3cbbXpbnoxZVR6H+XvDv +bdRJfk3NFj/N/63+dSjJH9U+OLTl9fuWuXCosVmwelVdLej+jk+XfjyT100udqweNQnGcdS7+jBf +1j31irupv444xf6r2NvFTtvpAGJBL55YPuO4ZBcQ3gOubBky1mjh29VvdOtjBWi7TZSEszLKQNn8 +bqPkLq7iF09mrFoXv1qMnlfx/lXf0RjqFP8W/1s7cnth/kcfDt2YpBt84SDrzXae/3qWf28b/r2U +DERJKo7iNYlHcMwzvQAa/ZW6ddOfH/V3f1cuc+2y5hVI6H9cQlcShzVslPmHXYMl4SX5BH5RYd3E +QQfvu+a8GtsGel7n8EFo5qN220VoO0ZZ0+Cinhw7XOzkbh/A+xocPfeZCiyXxk+Ug6ICqEKAoapW +igYiloWhUpCJIgWgYgKWlpUoSEKGmICKUqommqFEIkBghIJChKAoAooBZhKKBmgmqqiZqCImgACl +QaBIlVKAaViFAqJiaKEqiiZYgipioJKiiagpSpippJVSkRGgAoFShSJEaBoKQKRGiqoKWlpCiICI +KSmiiJiZiKJCiSKAkooKoqIqSWqGoloqIqCJaKmmKiUBppVKFAaVSlBoEpGIBKoAWgBpQoAWgQaK +hpiZpEooCoiKKKKKgIIqmIkqIqqiSQqKZiapYYiCaYiCpoIqpqimJagmZmpmJgmImiqkKmChamia +qqaKJZIlqKgoKmBEKAAGmlRBpVKCihAaEUpEAoUiAQiIqiGmYkoIoiKCJKpIICkJiYYiCpKQkipo +gqqKKmKaImkgqoaSmmgpYJpYSCiamiCgiiiqIoKKaqJiKKKqAioiqCmKGiJIigqKoqBpqiIiGiqY +KKKiYKCJYJmamJYlpIooapiipKYqIoipoSoIiimYKiaqaaaJoiWIYmIqiKJEAqqCiqUVUoQACZER +GJoAiaKKIiaYgqqggKCiGqJqoqIoqmiKkCJqhaaCZKQpCikoYYoKGploapiJKiqRiSIiWloKpAqS +iYqWqQkJiIiJoiGKlImiaikSAYqlpkiooqWaiJqqqCCgoSCiahgkmYomKYKKagmYoqIkkqoqiWCh +qoqiqJmIoliYpqKJqpqIpokooalgiJiKIohiiKpiKCCpCoiappigKKmKCpkqqpIKKKCKWkiCgiqa +CqqkhoEkpKipGhKIKqiYmCaJJJYCqiKSoggJikokGJWGippIihEIJSZFJgAmIpShKAQVJlViqgFS +SPylRAXIUVGFSkSFAmCSIkqiIiaIoiopmWiKpoiZIZIKCokaKQppqqYpooiiKqFkJKopaKGCopKA +iaCIKSASIAYakKSkoZaiGlJhpIIYoKaiiYCgIgoioqkpIKgmaCCIgpiFmKGmJoZgqqEIZpKiJQiA +IIgKZgJlYgIZoKaJQaKKiKiSglAkRiISIqgZhkgpJqaiCmYaYgqiIIiiplqKZmGmiikoYgKiKYgq +SKJqipCGCpqIUoqSmJqkqJGmIJhglKggYoKGiIqpaakiaJSmqCKmoiJaIiIqgooQpCIKAiChapIJ +kiokCilipKAioadBopQmopmaSqpJIgqoYkYpgpIiliUmaiWqKJUikmSSqWCaAIgKoiWqKIGIghpo +KKCCaqYqSkiaplWoZqqBimKqKKCaioWgWkEiiIqZKIpVCKkKkKmSKKoaaiCYKgkCEqSISKhiImom +KpoYkIEoRQdKqKhiQQp/XKKGJGhQUA2QAHulaQUNS/OEQUJS0BUoiuEAgFAKKHMKgYCBaUZAoimq +loKIKGphAoqKogqIKooaopImBiKZCalpKKShIgfpk0A0UlDU1AU0AVUSRI1JUp7JooKpVCVmhqKh +/uYFwSp8GsA4hJgIkkJZEqCaiCSAoJKgKWmRgpoiAipColoooqmIpoYSSoiKCUWmhGlYJoFqqQGh +CgoVYVoRoEiUoKiUKAKBKKQgChCAiGaKEoSlASWQKQKAKKChWkGgWIiGJSlaqqBKoJgEKFpqIWga +UaVopAoKBoBpaEoUiFoAoKRKBGhSihKiEqhYgpKiBKKGiqaRFSgKAqkAiApFopUCihFpiQiAqkmQ +kkGIQpBKCikAoQpSkIloiKEYJAaAKBUHiEQO8KHSFdkqNAAuGQIkBKRD7oVEDEihQA9ILpFpFBKR +oBBKRpABXlUoDUIBgPaETULd8UdoQkMSzISw3eAwZQ7ShxAvhCpgkkJCQSG4+N+cIfdKj7D6UEiR +BIQjEbGUgiiGBlgIYYlmQjbOMaYZiJ1OMFQiaBfIWhRKKYkaA7EOSHEiJ+f+vUzSBuBiVClVKBQV +/b2PfHIChSCCHcgQxIATpDSAH4H69vtDfT7nPa+m9nqtdFZ7bfjyj3RpIGtx+9IcQBjStFRFUKgK +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 +4L334uylq++UKZsHvauZ31E6yzTOnegaTWVUSQxeiLMSxW81qc4e0khLnoWyeTvfVcUD2zfCvWyY +idJ4sdjUHJEljKtZ7YZLLCCtNMt7G1nC2XEzviM5qgsJJaeo1itKC85yWy2ukXlWbXSJNTQZkq6Q +5nWVkV5ZJDdA02zc72poxXM8zhZZs+nnad74i183OetUphxrJ6veqwDpJ9WlKd5pOdLaw1Znmlda +MHFdHqV4rLS2vWGtKk3wi1aKo880a7pCJPLT1PGnhqNS1Lyuec1U6tq2rMkltp2pJ2VJnmS31qz1 +qmFyo1i4nJ1aQtWUZvq9M3ybRjSsLT1pZ3wtKTOxySNG9j0NVEmrg3bBo0M+gq5SDmldWtGZX1Ss +TB5O0XedoVoNs1WjIaVVNa1IYWsqSS8llWLWok9aW4qjZu1daNtKrpg7zFoW1pndDaerHXBySzKc +9Ue1XRNLJzvi1KNGM2xZhfUtWvNUV5RVM0rWDoyYZzuqLEWnc6M+MVSTyTNYGlw0luDPSu8GqKds +rjFdatE9SssYlEWuz0urSw7XlK+IrXFpNNMXu00iYTSTiGpnD6wuRpsNaVYpKEaaHml5WitjWq0c +3paQnOJUoGkLDDJmyZSmjnibrml2xd5raTvlDeV65JsxnFY1msGlJaDmbWK2dPZ6VlZqub1SUBsT +zmWD1qj0jWcyqIuYfMRa+VOlqg7Pp9Xve88DMHarZw2ZIr3NtO+MxN9Gt74wqtnDaxPGcRZjpmTo +1cOLTlZbXW7WOK2q5yndriYzp1CYrWdKHVLZOjPPNhEpzUrolp2tLUCJ5fTTBtm1Fmqq1JZfVptG +rGmdTlV0lPGa4XUalKupTSJaQxDZNtIyagYOYOk3yz0riokKbeFCkQ3G6bpieoxjG0t2qlHNLyF3 +oLavKSpZdojFaKKK1XtOaHNd2li1GtDZvrM41ij0vqdKarfWGzSjWnEhp0xEWucq3fJ6smK2vlZ3 +uaUypo8lpbWKnPVJitc4DtjIraHZ9NE3W05RGnhlxGjvlWqgW2FzJbWNn1nAnPNHlKq5oaRm+FRm +U6XtWVM3ms75ozOt4xp9Xe+p21a7vGitWqSrg6JNmm9NVg9YsiWQ2ikg2BqdjVby0y1sDZ73alVw +l8Xc6OprNpjFzo1bvq7xFokk4Rom8U1WaWCaShkeH1IYOVLVemBTJuc5apPAzR5ZTUSS+qTmizvm +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 +2d+zspryw3TP9bA/yTNNSKjpQC8UtkAmgron05UT0+F2TutCOlPRSwvCnz2jZcrrTurdHRMiMxc4 +iE31nk9A+DZ1mZkl+aaTUb4rm1cuL9qnxhuNw4GmlR2cOLiwMbt3zpTfTxw9Yr6bWM+t0HGT3W/q +nTr44qBERbcs0ERNS3nbmhIJZkkpAgPmpAu+ANzv9UW/lGHT8tGznl4DDFWyB3df8s38ONej3Vkv +41UG7paNpSfY14VBZFWQbh26Z/S/FyN5GXAgQBVABBkJ+ic5GkBwbyoeWABN4rtdRV08TdfXWjtp +xC+Ukel7vG7GFZC2L03lAPu3rXZ2CpJ/O1f3+/nFX73+oOr/399cpf13cZkCALIyBAAu5AgCMAiA +JJgiIh77R1bqE6X9Tk+4B/qR+eOOs1X8x3dPl/mHH7d8nBAF9bfz7u48O7gQBYfBslh2Rc7di6Xd +vI6bNMmt76nqBPX381r8Xdjf+nXTbS5v5OkimtaIBx4lt0zdJW4fSHT3bOf5CMnS+vM12xcHa7tv +2o6rx6P5/V2tvvh4+L9n/zyvm+/tEQdKaR1z0k/WDTDIBJBD9HvBkFym8uqqcqfdEOedYAIv7eP3 +xF1lbjBhRwp7/qYfSPX1Na63Kjpf3g2Vzuhh68Ubj+je9XbrM5bI8MPq+qp7+W0dvmAju5Z/TTPa +/XPrTYhU8e6Tmr6j/et+yCb+Wr0KmTJtLWPyw1fm6pGSeM2HzpOmKaXbj3sl+cm4dev5/FMx81Df +rc7mpPz3t70BfFmDM59zvv+cabeGnffym04vN7VPcFMtx+fm/KpPzDZDr1BmZmSAi9/Ji2e9eVej +OGmao5qZetl80L3afvz8CGST9evZNnwDl4GX8WO6qZEt96qYEo/nGyWmJKyNlehIKAa/lYhf8ORk +jgGD/2htf5s9orayKlquLAdXpSjtjGf+5/86LKUx/3nrI0DT/ytRZY3vtnQ2aupHOJ61cU2tbYVY +1eusWlREtiaWpK+JypTVtUtrZNpy2V7tfa1ZTtKm2Bl6ZjbaTtVWpm95azRNTOtVmlYcW2s2YlUK +dVF0m+EGa3yL1dK7+hsZFRwKm6gcPzlEZIJebGoKRDisQqs7t4URWAcwiJKQrN2nbx5Iu91Omd2i +bUPGMG3y34Xb0lSffbCvjs6LCH/BA7iY9TyeGQ5GrEoMxdr/kXUWdPKV3qYJjAMHEZkEFGBaZgzM +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 +FQ4DUNYYHBgYB/S/0v8TSLbf0uuqZaabQ2p1GW2luoi3GWXHGXXWzjKkRotptamHXXGFmXGTbDi3 +W3G0aaZW2pp1h1lbC2WXTDTDKMsm23EacbYWppTRbqIw6666pGzamGmGWVKaaYWhxh1xl0pbhZHE +aWp1hxxbLqEZf0PSmWHrTjLLLLrbr11tttbrrLLLLrrrq2m3XVLddYNtKbddZaZdddYccdbbbbbb +bdcYU62tgppppp1pHW3XXXXXXWGGGGHXXXXWWWWWWXXXXW2221NttsOuuNuMssuOOOtMMMOtI662 +2UyyyWy6yyyyytpxx1a1sttqUopl111phxx02bbbddMssuuuuuMNttusOOONtttuOsssssuuuuqW +i3XDDi3HUdWyccdWtbjjrLLLTLLTZ1llllh1p1111brrrrrrrTTTTTrjanTbbilOOOtttttuutNL +dddaUwikW6662yy2266666pkyy4w002222pS3G1MKbbddbdccdWtbrrrrrrbbrrrDDDDB11111xx +xxt1llll1p111tt11pppll111tttx11xx1lllll1phTDCnXXWm3XVusMMMLWy06w44t1bLTbhttx +h1lpxtt1brK3XXXXXVNrW266y0w66pphbrS1OuttutunXHXXXXXXXXW1rW66y66666jbbrq1tttN +uussssunXXVtNNOsNtttONOqdYdbbdccccccdbdYYZYbYYcW40yyjTjjq2nXXVuOtNMMMLaaZadd +dbbbbddRll1111tpp111lx1HXVrWt066666jrrrrqMMMLMtMssstOuuuuttttttOuttsIjhplxxb +rTrTrqIyy6664444yy6666w444402jbrbbbbjrrrTjinG22HEZZWyy446w666660yyyywZadddcc +cbNtttstMtLRTTTTjjji3HHHHGXXXXVrWt1SkbZMrYZddbddbbbbbddaaWaaaddddddccdZZZZMu +uuutttttuutNLdbbbW66addddcZZZZZdaNNNNMsuuuussssssuuuutGmWWWUZaZYYaYdZbZaZZdd +ddYZRl11100006000RtalsOuo666ywy46jrq1rccccRSKccWtanFrW62y6640txxxx1p1t11HGGH +HC1stMMo446622jrqnXXXXXFssqZZdZabdddaaU666y2246ta2222zrrLLLLjjjqNNNNOqU202y6 +6666444666666644222460000066666666wwww000666yy0666ta1o22220y0664444jrTLrrrLR +tt1x1llh1111S0Wy444s66yyyyy4446ttttt11lllp111a3HHHHHWWWWWW23XTrrrrrrTTTTTrrr +bbbbbrpxxxxx1lllphhhlGkaYZdYMnG1MsONsrMOsOuLdcdaRhhYy2s2yZYaYUwwjLrLjS3GGHDQ +ww4iKdYbbbMqbacZacW0ytZtTCmFIjjTrC2mWnW3XEYWpakNrZI4hbLSMsKZbRp11GkbbZaaWt5h +hHHULccbW6ikZWjTTqMMI4acRHWVuttHFNNOsKYWpxh11pltanHFOrIsyw604tp1ttDLK2DbTiMs +qYMMItaNOKdW226yptbjjrRth1g24bKdcYbNKbddbRpxbbjK0ZZZYRpxanWW3XHMow2ja3XHWlNN +tG2XVluodU0804YZZWpx1hta1KZdcNMLcUwinGWCzTDzS2nW1suusKaYaW64too02W6wwyU026cc +abeaOLZbadbabI2pk60w2phl0w00yptltphxFqeU6wttxZsyyyRbKlItTTja0bccadU26wbLU42+ +f209bR1D4pHxGSmFrLfFKfG1qR6y42y228y2ypFtstrUwwjDiG2Xxs2i2XGHGGzDTrjptgpG0eaI +aYaaOuI6ZYWttaLW26jKlLYYaWU44tGGm3VMqfPidaZbcR1EPinr4+No44622UdR1xamkZdZbWyy +400YWRxhalrW46bRlhtlTizLLbBTrLKnGmWFuojjrjK2nXHWmzjSjjK2221OsMuOOOOPOo4jbTDK +1OEdW2acaYKRltlTCMLR1tEdbdYRh1paNKZdbYRFmSkOsqZUt1hbbTJ/Pls44404y09eqcU9U66Z +accaW664tamm1surQ66Up06yimm2DZpp5SEYUy0wwQwhhtbRbrq3GGHWHXlraNrddYdUw0y42pxp +tThphxGnFtmWWFPnkWw66WcZW9Wyyw6pk2y0euPTS0cabZcNMrU4wpbTbTLDLDjTZhhophGm21ss +IyYKcaW4p1a3WWGlLKZbbRFNKdZW60pxEbMONuqccdRhtptpl11ppp1llll1llllt1ppp1llll1l +lllt1ppp1llll1llllt1ppp1llhSkdRhlxlx1ZaOKbdZRHVm3GnG3VGGnXWWENLYWyytphlplTaz +TjbTbrRtp1aOrdRGluMtKcRhlh1aIaW2wpp1hbLbbRhhhpakcRxGkdbU9/Piv6EafXxEfHUI+vXW +mmmnxSm2231FrU02pTLrrTLLLLjjTTTrJFNqcW6tR1hhhpplTC2FsrZZZYcU20ppa20YRtS3WFrW +260yyyyiMqUpttlllllpTri2VrW24jbDamHW1FKdUjrTbrrLi2HVuMuONtqbbbdcWta22mm1qbbb +ZcbaaacZZW09YYW69bZbetNNOssssvWWWWW3WmmnWWWWXWWWWW3WmmnWWWWXWWWWW3WmmnWWWWXW +WWWW3Wmm1KbbbaZZZZdcaaadZZbbbUpx1h1pTalLdRHHGmmmmnWWWWXGWWWW3r8T/weuOONvr1ll +p9aaafWX1S3HHXWW1OsOMuI000pSnWWWWWVsOuuutLWtppplll1llllxbbq2W1OqdaaZZacaabUp +tttplTS2FrZZU666ypTjTrTTSkaaabZZZZddaabUptttSlMutNKdbR11xxpllp1a1tttsOowwwph +1pxtllllxllllt0ptttT429ccbcbetNKUpsjbbr1ttpptSlOuuuusstttNIjTTTLKm3VttttNsss +suNuqYYcdabZZZZcZZZZbdaaadZZZZdZZZZbKU000244222y2200000246yyyyy2Upp9/8H3z6+u +PrqlOuqfVrbfX1xllpllllxta22222WXGWWWXXWmmnWWWWXWWWnFqU4444yy2Rp111ttTbq21OtM +NtttNOMsttqbbbZbbcW44444ypSmXWmmmmlKU00yypo66pttTbDTrjrjjLjjS21NttstMsslKUyy +yyyyy2yppta1rdUpTrSI0600000pSmmWWWnVlKdZaMMtOOMuttNNOMsssttuOssuuo22220pxxa2 +HHEdbcRxxp1ttpppxllll1llllt1TbClLYRtalNttqcdcYUptt11tpptllTqLW44600pTbajC1KW +ta3FtskbbdaaW2jikRx1tllllla223WmnGWWmWWWXHGmmlKU00jTTqlNuMKccUpTbjjjTTSlOMLR +tTTS3FsuNusstKU2ta23ER1pplHXW1rW622yyy4yyyy440006yyyiMsttNKWjbjrLBhTrrriI06p +SnFophbDbbbLrjjjjrLi1raaaZZZZbZaMMKdcbZZZZcZZZZccaaadZbbRHHXHHGmVKaW0yy44ptt +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 +xSlrf6mGGGGGGGFsYwpThwRMUxhKYpiglKU4EMBDA0EMBDQ0Ahoen70wam5fiRqw9k9k/sLbMFLZ +RMNexiTw+bLxFCey/5Wh4C2HhRWLQUEOFQIlChovKI2ww8jS0RhG2m37sKU2yjrrbDTjTLjjRpbj +LLTDjjDCnHGVuuuqZadcYUtxxhbRHHVNttradYcWwjTbbBxxhxxbLinVssqYaRlxbTTjKlOssurU +0jbrCMtMOOMLU62400w20064jTJlpwywppx1pt1txT+i2jpFNqWjTbDizjqOMtMrYMPW2kYaWw/Z +16wj16UjrjiMOtrZRtFuPUbcf05bdZcW049dbU0wtbj1DbqmHFLRpTjbinW3WHHHG3G2mG2mWVNs +NuMrcbcYW46w0wttbDbLjLrTTiMrW4t1pxbCOMtMNtOsssuusstNMGnGW2mlOuOMsslOuMI00w02 +spR00jKlsNNttnDaI2bW4p1llhlam1LWytSLZRxpplbKnVLaNLaKRh1Zxlx1hlRlbqnXGkf5P7eu +NP8wtb62wOutMtsv0iPxlEdYFmXW1tOuv++4caNLGdNv060tbDCmGdtMtHH6YabUi21qaR8aaYQ6 +dbacW44jLLjLKNtNOvrO1Ipt8edaUaQt8fG2HnHrqz6j/e/HXX1tp5hx9bW2+vj8ceZafjalsuND +DDDrj4+Pxw44+vrA9YZesDjDDbT160y0y+sFuowytttaPq1OqYbW066+MstrWcaa/j/cy+suuPx5 +l69evxl59aUfpH6W46w8+7NI4+MMvMstvWDaPrTTrD4j4yjLaPj4+tjbbT6+svMMOvr16+tvP7NN +uuOvX19W+MqbYNtNstot9U022pxbTDbam1Pq38f1a9euOtv0fj622+vrTz9nrTT9MvLfX1Y/3a+O +I4/Ghhg+vr8ZfiKZfX1h4+MHq3mWGDSnXx628000ddZeeuuvikdccfDSn40wy9W2yj6ptbDq2m3H +XVKU6pSnWGEzb69Uw6+sofWmmnHxoW6jTT6+tPMPWVI+MH8064j4y9ZfUev823x8bfjYwy+Mvxl5 +tl8W89fWHWm3rY009evX1p8Rx620tHGGFsMOqZZYdMtP0+sMqW222U0pphxt9fHVuMKetOLRHrDL +DDKnXVHGlvjzbJp8dcaeZNtusPKYeo9Yef9+mmGmnrbiFvWnxgcWw26wyZZfFvOuutMH4jD4+OPj +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 +jrxvhjxF46773gKW7C9nWKXPZ5J3x4DSfFbZukKmXJ7dR4QN+uwRLrBQEDqE/U0+2zWVNidMkL4I +WCC6nEomaDu/ebhnw5MMIi9nzBpyhE5AE7QQoVdd5wjzywOYOk7CHrO3RD5B8dtBGIhAFKOIufDL +pPVVNy9GkNsgOXBNXpao1GhYJ77BlGkwaE4zFjJwqKi7mBQUKAjoI80W2Oe+NC1AuXQGFcaiYM3R +DbWmLg1o0YkQMGLCHDANwYGBo2jcN4zBwIiEQiEQaIXHGmmnHW1NtuOuuutustNuturYZdZddddd +dddW2222w22266666w666wta3XWnXXXXXTrrbDLLLLK1rddZdUpS2EYYYdcddddcOIjjjjji1rW4 +46666666jrq1OuuuuuuuOOuuuuutNNNNNNOuuuuuuutFrW6660666666660wwww666266tTCmFsO +uuuuuuuuuqUp1HFNtsLdZdaOo66444442666ta2GHXXXXWnXXXVKYW6606662UUphxxa3WGHVssq +ZZZdU22222y2266666pEaaaaYaaddcdR111SOuuuuqUp1xhEbbbZddbaaabdddLWst1111111111 +1tG2mmmmXXXHHVrWt11111111111FrUt11111111111ta1sssMMOutuuuuuuuuKbbbYbbabdUy0j +rrrDrrbrrrbiOOLWtbjjjjjrrrrrq1rW60w00pppp11p11tGnWXWXWXXXXXXXXXXVqU66p1111x1 +1tEdddddddddddWpttttltpt1SI6jrDCMLddYZW66660066466yop1111bDDCIw0666646cWtbBa +1uuMsssstOtuuuuuuuuuuuurWtbrrrrrrrrrrrRpppppp11hhhhh1111111111a1rddddddddddd +W0YQ00ww00tGWVsssumkdddYdddddddcbZbNNNOtnXG3XXXXXXVMMKYYdddddddddddW0wwyy666 +260666666ta1uuuuuuuuuuutNGmmmmnXXXXXXXXWGmlMLRh111lEbbdddddddddddYYYYYdddddd +dddddUtFrdddddddddcdRa1rdddddddddddWbYYYddZddddddddNNNMtI006666660662bbbbaaZ +ZZZZZdcddddddU64jTTDTSIy6266pSm1LbaaWw066446666660ta3XXWnXHUddddRxxxtxHHG3HX +VuuuuuutOuuurWtbrrrrTTTTrDKnXWG223XXXXW1uKccccaccdddZbRpo66wy6ppp11111111Gmm +mnG2m3WHXXXXXWGmlNNNMtOuusOuuuuKZZZWy62wwww26626666666aaaaaaaddddddddaaaaaaa +dadddW60pTrrrLrrrrrrrrrbbbbbbbbrrp11111hp1tbbbbbTrrrrrrrTTTTTTTrrrrrqOuqaNNL +aaadddddddYYYYYddddddddddddYbbbW222266666664pGXHHFuOtMsssuuOuOOqWi1uuuuuuuuu +nXVrWW666666yyyyyy666wwwwYdddddddddcdccOLaRl1ppp0ta1uuOsOuuuusuuuIwwwt1l1111 +11111HXWDS2iFMtrRl11bKmEKU24th1HEZRtlbqyKaUwwZbdcW66202tbjqNsNLWtTTTZDSLcaI0 +4jh0txbDLDCOkW020t1xbDba2mm3EbbdadZcaZZddKdWWim3HWBphTDTjSjSkNuo20w2ww4i20Wy +6YKbdRkhbjSOqcbZYNtYZYcUjCm0aRGGVutuqOHHUddYU0y4ph1bjTDLaLcU0tTSOLRa1urZdbdR +tthhtl0UwjjTrrbh0w2tpl1DLTanDTTrq2mnWG1Mowo44ZW20ZbYcKMqcYYUo68ta2mHXXVuqbcU +WjDS0deYZbMMqRxbbDSzTbS1MtssqZLdUw606wtxg2ww226wU0wth11lFMI20ZaebdaWjRkthTCI +0w4o02bcbdNOlKR04plS2WWHTDCmmmkUwhxTbK0W06tgtbDKluuP4fcbZPWzbLLrSi3WDLaMMHpl +8ZRGVI0y20y026jLrjS0OsqMKW6w0ww026644thbDrDqmm2m2FNqcbdddbbYabbcUtGG2lsHGGmW +GGXW0cZZWp1pbSmEdZaRlxalOLdZbdddceUwinVNOMOMNMrYcaW66iKZW0wtGWFMEZZZdcccaRTr +TiMMsutIjSmUWwyw220pxptFurNuusNrOsuLdR1hbSGWWFNNNopSMOsMOtqcaYdOMOqaW66s4y06 +y4UcRtSGVIthSzqG2Vu+OTzz9U4dUU09OItxHXVrR62im2nUddMstMtOLKdUyyp1phG1KacbdZR1 +Bophx1ZhhZtoi2WjTjLrZgw4aYdU2i3VOFMutG1OIjbK2VsNNo6006pttthpHHGXlsMuotRxhHFo +tGmnVtusNKU000600hl1EMIU26pSLZYdZRwwy26pCm2zrilrbUp11DTR1SnXDbbLLi23UIw04wi2 +DSMrbZQiMLWw242WpbjDTJpTjrJpS1qUw6w6tTLR1htpw46ytxpa0X/w/7f+7TL42evVPikefX1h +llTbinFNsn1bbSmHUYWw4wthxbrqm0bacZWyy02dbW0644taOuNtqbdYMrbOuKWw62iNttOLRHXW +VLaaRpopGmG3FsLW6pbCmG0ZcabUpxEcaNrbRlhtHXFqOOmluMssttNMrWpxtDBlpttHUW20ww0w +0ZRx1hTbamEbU2pZlalNKUoywyw4w06dW6wycWU06ywtZhpp1pRk2wy4tlS2mnG22W22mmnWWWWX +WVNKaU44622yyiMssuNNNNOssssuMsqUppxx1ptllllK/tk9p/k0plpx8daaacZZZZesssqU044+ +NuMsssojLrTTTTrLLLLjLLLLa23FuuOsssststuNNNOOussssuMstOLZW0pTDLbLqNNKUptppllx +llll1xppp1lttplbSlMOOKI006jbbTbbqmGFOMOtLcUpTjq1rdddaZZNKYYUpam23FMMtOsstOtN +NOsuqbWi2mlKU0jS2EZdWwiMtKUtSlusnXHVKaabZZZZadRxSnXHHHGWWWWW2lurcWphTKmHHHEY +ddddZaYccdddZdUtlxS2WW22WWWXWWWlKU4tbbbaI02rxaOrbbbdZZaRHHXHHHXHGW23WWVKUyyy +4446y0yyyy44p1bC2G221KUyy40tammmmW2WWWXXWmmnVOOrbbcZZZZbdabddZYddddZdaaabUpt +tttt11HHFuttttNOsmkWt11xlHHHXWHXHGW22XWm2XVKUyyy44iOtMsssssoyinHHVKU60u1tttt +tOMstI000222ptx1lxxxalKcdUwaYbUwtl11pppxlEZbbU224pS1HWGGlOOOsOttNotp1SOsuOLW +tbKnW2223XWmmnGVNtto462426pplTDjD+NZcbcW44402yyyy6yyyy22400000plT1xtTKm1MsMs +OqaWy2tbbjbrTbLLJxGmmnWXWWW3WEW040txhSnG23XW2WWWW21rbddbZZWth11bTrrLLKlKU264 +plSm38qbdeuNuutNNPVKbbaaceuNqU6txSONNNMuNNNOIwp11xxllllxppppp1llllltppppxhhh +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 +niEQhCIhCIQiIhEQxB+JgYRBEQxBEMQEIIiREQREREPt9Xich1lB2FHaaD+1b+1hTaMLUw20RTLC +22mn4+I0wst/jLjauvXUaeut8N+eaU8/6zWWHxkxB8YR8esvXraERDbrqjrLujTTLLTLykQiFLaW +666ttER5ENkcRbdFIRha3xCsKeiY8HApPeewn5Gbw4wqyIoiIuCPhx6tEYetvrbTT4waU20pTb60 +plhTZo4y6aNKYcZWfUREQyy6w8wp6phlx1f4ycREIjbCm2GUeWwjL4+PWHmkcfHxg++vW3qMoiGG +Hr6yMkdUKeR1xTj4y8u8eP7uvXVI6Wppa1I3X2v6nDDSOHEdYdRhxhlhbr4iMtsnHH1bbri1mFrb +R1tHnFLW6ptH0wiEUphGnraxpDSNOsPMMIytaH6YU0phw440MvWmnrLqMIw0tTT4+NMm2ym0aevV +to8jyPj4+KdYdU6tb42txCNKUiIURlthaIfSx+xBEeI8jyI8iEJDBBARERBEQQREMRECCCICIhEi +IgIiIiauroz9ByPJEEREEQxGSTURRfGswgrQYMQESxBEjBEEREIjBERIiFxtNprLjabTYazcXFxc +YlMMOMuOuOKRbjaMOtONqdWtTDqMJ5j938ONPjSkddesHCI8/manknnkRxhxh9aMIunqDvPOLZfX +X1fmA9DCGD73yDIPcONSa740XBteTRFG01G0sQyXXUAzYx1MgWQk/e+i+40hZUEP6fr68/EIhlb+ +GDBCMP2fsthCEafu0/T937v4ccUikREQ+IpEIRHmVssOo5pz1b4+OsojC2XGmnXHHHHGVvXG0Zca +HW2HGnHXWGWjqmHrDLC2HxB1HHWViIRbb16+NPNIiDjJp8fGWkR5phph6plYiPadYaesNHmHUotC +KacbYZQjLz111llEeI6064yZTys1VcZYUbbU9erMMvXXWmm1NrcdZR1xTKnGXGmC2WWT58bGmFLR +5HmlNtvWWVsttHx628y2pSkZOqeYQ9Qw+PVsoyikIUp1o46w80iIyyjr4y820ypo6yywop5TCKZW +4ywRBh8U0jRh11g2hl/pn7flePXCmnxFMLfflKfX1bbbLbjLrba1MLbYcU4400w6pSnVMKcRxl9b +aRtk6lvMvOqZLccYYI49aLRCI8w469ZZYU8plxQtEREIh1hlGXWXn42yWy9dWZQjLq2lsPFNPXVP +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 +""" + +import codecs +import os +import sys +import base64 +import bz2 +import tempfile +import shutil + +def unpack(sources): + temp_dir = tempfile.mkdtemp('-scratchdir', 'unpacker-') + for package, content in sources.items(): + filepath = package.split("/") + dirpath = os.sep.join(filepath[:-1]) + packagedir = os.path.join(temp_dir, dirpath) + if not os.path.isdir(packagedir): + os.makedirs(packagedir) + mod = open(os.path.join(packagedir, filepath[-1]), 'wb') + try: + mod.write(base64.b64decode(content)) + finally: + mod.close() + return temp_dir + + +if __name__ == "__main__": + if sys.version_info >= (3, 0): + exec("def do_exec(co, loc): exec(co, loc)\n") + import pickle + sources = sources.encode("ascii") # ensure bytes + sources = pickle.loads(bz2.decompress(base64.decodebytes(sources))) + else: + import cPickle as pickle + exec("def do_exec(co, loc): exec co in loc\n") + sources = pickle.loads(bz2.decompress(base64.decodestring(sources))) + + try: + temp_dir = unpack(sources) + sys.path.insert(0, temp_dir) + + entry = """ +import pip +pip.bootstrap() +""" + do_exec(entry, locals()) + finally: + shutil.rmtree(temp_dir) diff --git a/chef/cookbooks/python/libraries/matchers.rb b/chef/cookbooks/python/libraries/matchers.rb new file mode 100644 index 0000000..bb0a94c --- /dev/null +++ b/chef/cookbooks/python/libraries/matchers.rb @@ -0,0 +1,25 @@ +if defined?(ChefSpec) + def install_python_pip(package_name) + ChefSpec::Matchers::ResourceMatcher.new(:python_pip, :install, package_name) + end + + def upgrade_python_pip(package_name) + ChefSpec::Matchers::ResourceMatcher.new(:python_pip, :upgrade, package_name) + end + + def remove_python_pip(package_name) + ChefSpec::Matchers::ResourceMatcher.new(:python_pip, :remove, package_name) + end + + def purge_python_pip(package_name) + ChefSpec::Matchers::ResourceMatcher.new(:python_pip, :purge, package_name) + end + + def create_python_virtualenv(virtualenv_name) + ChefSpec::Matchers::ResourceMatcher.new(:python_virtualenv, :create, virtualenv_name) + end + + def delete_python_virtualenv(virtualenv_name) + ChefSpec::Matchers::ResourceMatcher.new(:python_virtualenv, :delete, virtualenv_name) + end +end diff --git a/chef/cookbooks/python/metadata.json b/chef/cookbooks/python/metadata.json new file mode 100644 index 0000000..0fff8e8 --- /dev/null +++ b/chef/cookbooks/python/metadata.json @@ -0,0 +1,43 @@ +{ + "name": "python", + "description": "Installs Python, pip and virtualenv. Includes LWRPs for managing Python packages with `pip` and `virtualenv` isolated Python environments.", + "long_description": "", + "maintainer": "Noah Kantrowitz", + "maintainer_email": "noah@coderanger.net", + "license": "Apache 2.0", + "platforms": { + "debian": ">= 0.0.0", + "ubuntu": ">= 0.0.0", + "centos": ">= 0.0.0", + "redhat": ">= 0.0.0", + "fedora": ">= 0.0.0", + "freebsd": ">= 0.0.0", + "smartos": ">= 0.0.0" + }, + "dependencies": { + "build-essential": ">= 0.0.0", + "yum-epel": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "python": "Installs python, pip, and virtualenv", + "python::package": "Installs python using packages.", + "python::source": "Installs python from source.", + "python::pip": "Installs pip from source.", + "python::virtualenv": "Installs virtualenv using the python_pip resource." + }, + "version": "1.4.6" +} \ No newline at end of file diff --git a/chef/cookbooks/python/metadata.rb b/chef/cookbooks/python/metadata.rb index 8128d6d..39704f3 100644 --- a/chef/cookbooks/python/metadata.rb +++ b/chef/cookbooks/python/metadata.rb @@ -1,12 +1,12 @@ name "python" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" +maintainer "Noah Kantrowitz" +maintainer_email "noah@coderanger.net" license "Apache 2.0" description "Installs Python, pip and virtualenv. Includes LWRPs for managing Python packages with `pip` and `virtualenv` isolated Python environments." -version "1.4.1" +version "1.4.6" depends "build-essential" -depends "yum" +depends "yum-epel" recipe "python", "Installs python, pip, and virtualenv" recipe "python::package", "Installs python using packages." diff --git a/chef/cookbooks/python/providers/pip.rb b/chef/cookbooks/python/providers/pip.rb index e7010d2..800bed6 100644 --- a/chef/cookbooks/python/providers/pip.rb +++ b/chef/cookbooks/python/providers/pip.rb @@ -112,7 +112,7 @@ def current_installed_version # incase you upgrade pip with pip! if new_resource.package_name.eql?('pip') delimeter = /\s/ - version_check_cmd = "pip --version" + version_check_cmd = "#{which_pip(@new_resource)} --version" end result = shell_out(version_check_cmd) (result.exitstatus == 0) ? result.stdout.split(delimeter)[1].strip : nil @@ -151,7 +151,10 @@ end def pip_cmd(subcommand, version='') options = { :timeout => new_resource.timeout, :user => new_resource.user, :group => new_resource.group } - options[:environment] = { 'HOME' => ::File.expand_path("~#{new_resource.user}") } if new_resource.user + environment = Hash.new + environment['HOME'] = Dir.home(new_resource.user) if new_resource.user + environment.merge!(new_resource.environment) if new_resource.environment && !new_resource.environment.empty? + options[:environment] = environment shell_out!("#{which_pip(new_resource)} #{subcommand} #{new_resource.options} #{new_resource.package_name}#{version}", options) end @@ -160,8 +163,8 @@ end def which_pip(nr) if (nr.respond_to?("virtualenv") && nr.virtualenv) ::File.join(nr.virtualenv,'/bin/pip') - elsif node['python']['install_method'].eql?("source") - ::File.join(node['python']['prefix_dir'], "/bin/pip") + elsif ::File.exists?(node['python']['pip_location']) + node['python']['pip_location'] else 'pip' end diff --git a/chef/cookbooks/python/providers/virtualenv.rb b/chef/cookbooks/python/providers/virtualenv.rb index addb724..904c1b4 100644 --- a/chef/cookbooks/python/providers/virtualenv.rb +++ b/chef/cookbooks/python/providers/virtualenv.rb @@ -28,11 +28,16 @@ end action :create do unless exists? + directory new_resource.path do + user new_resource.owner if new_resource.owner + group new_resource.group if new_resource.group + end Chef::Log.info("Creating virtualenv #{new_resource} at #{new_resource.path}") interpreter = new_resource.interpreter ? " --python=#{new_resource.interpreter}" : "" execute "#{virtualenv_cmd}#{interpreter} #{new_resource.options} #{new_resource.path}" do user new_resource.owner if new_resource.owner group new_resource.group if new_resource.group + environment ({ 'HOME' => ::Dir.home(new_resource.owner) }) if new_resource.owner end new_resource.updated_by_last_action(true) end @@ -61,8 +66,8 @@ def load_current_resource end def virtualenv_cmd() - if node['python']['install_method'].eql?("source") - ::File.join(node['python']['prefix_dir'], "/bin/virtualenv") + if ::File.exists?(node['python']['virtualenv_location']) + node['python']['virtualenv_location'] else "virtualenv" end diff --git a/chef/cookbooks/python/recipes/package.rb b/chef/cookbooks/python/recipes/package.rb index 18c1a00..115b53b 100644 --- a/chef/cookbooks/python/recipes/package.rb +++ b/chef/cookbooks/python/recipes/package.rb @@ -23,13 +23,14 @@ major_version = node['platform_version'].split('.').first.to_i # COOK-1016 Handle RHEL/CentOS namings of python packages, by installing EPEL # repo & package if platform_family?('rhel') && major_version < 6 - include_recipe 'yum::epel' + include_recipe 'yum-epel' python_pkgs = ["python26", "python26-devel"] node.default['python']['binary'] = "/usr/bin/python26" else python_pkgs = value_for_platform_family( - "debian" => ["python","python-dev"], - "rhel" => ["python","python-devel"], + "debian" => ["python","python-dev"], + "rhel" => ["python","python-devel"], + "fedora" => ["python","python-devel"], "freebsd" => ["python"], "smartos" => ["python27"], "default" => ["python","python-dev"] diff --git a/chef/cookbooks/python/recipes/pip.rb b/chef/cookbooks/python/recipes/pip.rb index 65acfa7..17110fa 100644 --- a/chef/cookbooks/python/recipes/pip.rb +++ b/chef/cookbooks/python/recipes/pip.rb @@ -25,7 +25,7 @@ if node['python']['install_method'] == 'source' pip_binary = "#{node['python']['prefix_dir']}/bin/pip" -elsif platform_family?("rhel") +elsif platform_family?("rhel", "fedora") pip_binary = "/usr/bin/pip" elsif platform_family?("smartos") pip_binary = "/opt/local/bin/pip" @@ -33,26 +33,12 @@ else pip_binary = "/usr/local/bin/pip" end -remote_file "#{Chef::Config[:file_cache_path]}/ez_setup.py" do - source node['python']['setuptools_script_url'] - mode "0644" - not_if "#{node['python']['binary']} -c 'import setuptools'" -end - -remote_file "#{Chef::Config[:file_cache_path]}/get-pip.py" do - source node['python']['pip_script_url'] +cookbook_file "#{Chef::Config[:file_cache_path]}/get-pip.py" do + source 'get-pip.py' mode "0644" not_if { ::File.exists?(pip_binary) } end -execute "install-setuptools" do - cwd Chef::Config[:file_cache_path] - command <<-EOF - #{node['python']['binary']} ez_setup.py - EOF - not_if "#{node['python']['binary']} -c 'import setuptools'" -end - execute "install-pip" do cwd Chef::Config[:file_cache_path] command <<-EOF @@ -60,3 +46,8 @@ execute "install-pip" do EOF not_if { ::File.exists?(pip_binary) } end + +python_pip 'setuptools' do + action :upgrade + version node['python']['setuptools_version'] +end diff --git a/chef/cookbooks/python/recipes/source.rb b/chef/cookbooks/python/recipes/source.rb index eb8288d..ac8ed86 100644 --- a/chef/cookbooks/python/recipes/source.rb +++ b/chef/cookbooks/python/recipes/source.rb @@ -21,6 +21,7 @@ include_recipe "build-essential" configure_options = node['python']['configure_options'].join(" ") +make_options = node['python']['make_options'].join(" ") packages = value_for_platform_family( "rhel" => ["openssl-devel","bzip2-devel","zlib-devel","expat-devel","db4-devel","sqlite-devel","ncurses-devel","readline-devel"], @@ -34,8 +35,8 @@ end version = node['python']['version'] install_path = "#{node['python']['prefix_dir']}/bin/python#{version.split(/(^\d+\.\d+)/)[1]}" -remote_file "#{Chef::Config[:file_cache_path]}/Python-#{version}.tar.bz2" do - source "#{node['python']['url']}/#{version}/Python-#{version}.tar.bz2" +remote_file "#{Chef::Config[:file_cache_path]}/Python-#{version}.tgz" do + source "#{node['python']['url']}/#{version}/Python-#{version}.tgz" checksum node['python']['checksum'] mode "0644" not_if { ::File.exists?(install_path) } @@ -44,9 +45,9 @@ end bash "build-and-install-python" do cwd Chef::Config[:file_cache_path] code <<-EOF - tar -jxvf Python-#{version}.tar.bz2 + tar -zxvf Python-#{version}.tgz (cd Python-#{version} && ./configure #{configure_options}) - (cd Python-#{version} && make && make install) + (cd Python-#{version} && make && make #{make_options}) EOF environment({ "LDFLAGS" => "-L#{node['python']['prefix_dir']} -L/usr/lib", @@ -56,3 +57,12 @@ bash "build-and-install-python" do }) if platform?("ubuntu") && node['platform_version'].to_f >= 12.04 not_if { ::File.exists?(install_path) } end + +# Link install as the default python, to support Python 3.x +# Otherwise the pip and virtualenv recipes won't work properly +link node['python']['binary'] do + to install_path + not_if { ::File.exists?(node['python']['binary']) } +end + + diff --git a/chef/cookbooks/python/recipes/virtualenv.rb b/chef/cookbooks/python/recipes/virtualenv.rb index 4c28f80..8098492 100644 --- a/chef/cookbooks/python/recipes/virtualenv.rb +++ b/chef/cookbooks/python/recipes/virtualenv.rb @@ -21,5 +21,6 @@ include_recipe "python::pip" python_pip "virtualenv" do - action :install + action :upgrade + version node['python']['virtualenv_version'] end diff --git a/chef/cookbooks/python/resources/pip.rb b/chef/cookbooks/python/resources/pip.rb index cccb224..1475b3a 100644 --- a/chef/cookbooks/python/resources/pip.rb +++ b/chef/cookbooks/python/resources/pip.rb @@ -34,3 +34,4 @@ attribute :virtualenv, :kind_of => String attribute :user, :regex => Chef::Config[:user_valid_regex] attribute :group, :regex => Chef::Config[:group_valid_regex] attribute :options, :kind_of => String, :default => '' +attribute :environment, :kind_of => Hash, :default => {} diff --git a/chef/cookbooks/python/src/1/calculating_with_dictionaries/example.py b/chef/cookbooks/python/src/1/calculating_with_dictionaries/example.py deleted file mode 100644 index 325e4f5..0000000 --- a/chef/cookbooks/python/src/1/calculating_with_dictionaries/example.py +++ /dev/null @@ -1,25 +0,0 @@ -# example.py -# -# Example of calculating with dictionaries - -prices = { - 'ACME': 45.23, - 'AAPL': 612.78, - 'IBM': 205.55, - 'HPQ': 37.20, - 'FB': 10.75 -} - -# Find min and max price -min_price = min(zip(prices.values(), prices.keys())) -max_price = max(zip(prices.values(), prices.keys())) - -print('min price:', min_price) -print('max price:', max_price) - -print('sorted prices:') -prices_sorted = sorted(zip(prices.values(), prices.keys())) -for price, name in prices_sorted: - print(' ', name, price) - - diff --git a/chef/cookbooks/python/src/1/determine_the_top_n_items_occurring_in_a_list/example.py b/chef/cookbooks/python/src/1/determine_the_top_n_items_occurring_in_a_list/example.py deleted file mode 100644 index 3906136..0000000 --- a/chef/cookbooks/python/src/1/determine_the_top_n_items_occurring_in_a_list/example.py +++ /dev/null @@ -1,25 +0,0 @@ -# example.py -# -# Determine the most common words in a list - -words = [ - 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', - 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the', - 'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into', - 'my', 'eyes', "you're", 'under' -] - -from collections import Counter -word_counts = Counter(words) -top_three = word_counts.most_common(3) -print(top_three) -# outputs [('eyes', 8), ('the', 5), ('look', 4)] - -# Example of merging in more words - -morewords = ['why','are','you','not','looking','in','my','eyes'] -word_counts.update(morewords) -print(word_counts.most_common(3)) - - - diff --git a/chef/cookbooks/python/src/1/extracting_a_subset_of_a_dictionary/example.py b/chef/cookbooks/python/src/1/extracting_a_subset_of_a_dictionary/example.py deleted file mode 100644 index ca962c6..0000000 --- a/chef/cookbooks/python/src/1/extracting_a_subset_of_a_dictionary/example.py +++ /dev/null @@ -1,23 +0,0 @@ -# example of extracting a subset from a dictionary -from pprint import pprint - -prices = { - 'ACME': 45.23, - 'AAPL': 612.78, - 'IBM': 205.55, - 'HPQ': 37.20, - 'FB': 10.75 -} - -# Make a dictionary of all prices over 200 -p1 = { key:value for key, value in prices.items() if value > 200 } - -print("All prices over 200") -pprint(p1) - -# Make a dictionary of tech stocks -tech_names = { 'AAPL', 'IBM', 'HPQ', 'MSFT' } -p2 = { key:value for key,value in prices.items() if key in tech_names } - -print("All techs") -pprint(p2) diff --git a/chef/cookbooks/python/src/1/filtering_list_elements/example.py b/chef/cookbooks/python/src/1/filtering_list_elements/example.py deleted file mode 100644 index 7cba0b2..0000000 --- a/chef/cookbooks/python/src/1/filtering_list_elements/example.py +++ /dev/null @@ -1,43 +0,0 @@ -# Examples of different ways to filter data - -mylist = [1, 4, -5, 10, -7, 2, 3, -1] - -# All positive values -pos = [n for n in mylist if n > 0] -print(pos) - -# All negative values -neg = [n for n in mylist if n < 0] -print(neg) - -# Negative values clipped to 0 -neg_clip = [n if n > 0 else 0 for n in mylist] -print(neg_clip) - -# Positive values clipped to 0 -pos_clip = [n if n < 0 else 0 for n in mylist] -print(pos_clip) - -# Compressing example - -addresses = [ - '5412 N CLARK', - '5148 N CLARK', - '5800 E 58TH', - '2122 N CLARK', - '5645 N RAVENSWOOD', - '1060 W ADDISON', - '4801 N BROADWAY', - '1039 W GRANVILLE', -] - -counts = [ 0, 3, 10, 4, 1, 7, 6, 1] - -from itertools import compress - -more5 = [ n > 5 for n in counts ] -a = list(compress(addresses, more5)) -print(a) - - - diff --git a/chef/cookbooks/python/src/1/finding_out_what_two_dictionaries_have_in_common/example.py b/chef/cookbooks/python/src/1/finding_out_what_two_dictionaries_have_in_common/example.py deleted file mode 100644 index 89a083f..0000000 --- a/chef/cookbooks/python/src/1/finding_out_what_two_dictionaries_have_in_common/example.py +++ /dev/null @@ -1,20 +0,0 @@ -# example.py -# -# Find out what two dictionaries have in common - -a = { - 'x' : 1, - 'y' : 2, - 'z' : 3 -} - -b = { - 'w' : 10, - 'x' : 11, - 'y' : 2 -} - -print('Common keys:', a.keys() & b.keys()) -print('Keys in a not in b:', a.keys() - b.keys()) -print('(key,value) pairs in common:', a.items() & b.items()) - diff --git a/chef/cookbooks/python/src/1/finding_the_largest_or_smallest_n_items/example.py b/chef/cookbooks/python/src/1/finding_the_largest_or_smallest_n_items/example.py deleted file mode 100644 index 23ab769..0000000 --- a/chef/cookbooks/python/src/1/finding_the_largest_or_smallest_n_items/example.py +++ /dev/null @@ -1,20 +0,0 @@ -# example.py -# -# Example of using heapq to find the N smallest or largest items - -import heapq - -portfolio = [ - {'name': 'IBM', 'shares': 100, 'price': 91.1}, - {'name': 'AAPL', 'shares': 50, 'price': 543.22}, - {'name': 'FB', 'shares': 200, 'price': 21.09}, - {'name': 'HPQ', 'shares': 35, 'price': 31.75}, - {'name': 'YHOO', 'shares': 45, 'price': 16.35}, - {'name': 'ACME', 'shares': 75, 'price': 115.65} -] - -cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price']) -expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price']) - -print(cheap) -print(expensive) diff --git a/chef/cookbooks/python/src/1/grouping-records-together-based-on-a-field/grouping.py b/chef/cookbooks/python/src/1/grouping-records-together-based-on-a-field/grouping.py deleted file mode 100644 index 290a186..0000000 --- a/chef/cookbooks/python/src/1/grouping-records-together-based-on-a-field/grouping.py +++ /dev/null @@ -1,33 +0,0 @@ -rows = [ - {'address': '5412 N CLARK', 'date': '07/01/2012'}, - {'address': '5148 N CLARK', 'date': '07/04/2012'}, - {'address': '5800 E 58TH', 'date': '07/02/2012'}, - {'address': '2122 N CLARK', 'date': '07/03/2012'}, - {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'}, - {'address': '1060 W ADDISON', 'date': '07/02/2012'}, - {'address': '4801 N BROADWAY', 'date': '07/01/2012'}, - {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}, -] - -from itertools import groupby - -rows.sort(key=lambda r: r['date']) -for date, items in groupby(rows, key=lambda r: r['date']): - print(date) - for i in items: - print(' ', i) - -# Example of building a multidict -from collections import defaultdict -rows_by_date = defaultdict(list) -for row in rows: - rows_by_date[row['date']].append(row) - -for r in rows_by_date['07/01/2012']: - print(r) - - - - - - diff --git a/chef/cookbooks/python/src/1/implementing_a_priority_queue/example.py b/chef/cookbooks/python/src/1/implementing_a_priority_queue/example.py deleted file mode 100644 index 7117b69..0000000 --- a/chef/cookbooks/python/src/1/implementing_a_priority_queue/example.py +++ /dev/null @@ -1,35 +0,0 @@ -# example.py -# -# Example of a priority queue - -import heapq - -class PriorityQueue: - def __init__(self): - self._queue = [] - self._index = 0 - - def push(self, item, priority): - heapq.heappush(self._queue, (-priority, self._index, item)) - self._index += 1 - - def pop(self): - return heapq.heappop(self._queue)[-1] - -# Example use -class Item: - def __init__(self, name): - self.name = name - def __repr__(self): - return 'Item({!r})'.format(self.name) - -q = PriorityQueue() -q.push(Item('foo'), 1) -q.push(Item('bar'), 5) -q.push(Item('spam'), 4) -q.push(Item('grok'), 1) - -print("Should be bar:", q.pop()) -print("Should be spam:", q.pop()) -print("Should be foo:", q.pop()) -print("Should be grok:", q.pop()) diff --git a/chef/cookbooks/python/src/1/keeping_the_last_n_items/example.py b/chef/cookbooks/python/src/1/keeping_the_last_n_items/example.py deleted file mode 100644 index f3cc450..0000000 --- a/chef/cookbooks/python/src/1/keeping_the_last_n_items/example.py +++ /dev/null @@ -1,17 +0,0 @@ -from collections import deque - -def search(lines, pattern, history=5): - previous_lines = deque(maxlen=history) - for line in lines: - if pattern in line: - yield line, previous_lines - previous_lines.append(line) - -# Example use on a file -if __name__ == '__main__': - with open('somefile.txt') as f: - for line, prevlines in search(f, 'python', 5): - for pline in prevlines: - print(pline, end='') - print(line, end='') - print('-'*20) diff --git a/chef/cookbooks/python/src/1/keeping_the_last_n_items/somefile.txt b/chef/cookbooks/python/src/1/keeping_the_last_n_items/somefile.txt deleted file mode 100644 index 0255124..0000000 --- a/chef/cookbooks/python/src/1/keeping_the_last_n_items/somefile.txt +++ /dev/null @@ -1,86 +0,0 @@ -=== Keeping the Last N Items - -==== Problem - -You want to keep a limited history of the last few items seen -during iteration or during some other kind of processing. - -==== Solution - -Keeping a limited history is a perfect use for a `collections.deque`. -For example, the following code performs a simple text match on a -sequence of lines and prints the matching line along with the previous -N lines of context when found: - -[source,python] ----- -from collections import deque - -def search(lines, pattern, history=5): - previous_lines = deque(maxlen=history) - for line in lines: - if pattern in line: - for pline in previous_lines: - print(lline, end='') - print(line, end='') - print() - previous_lines.append(line) - -# Example use on a file -if __name__ == '__main__': - with open('somefile.txt') as f: - search(f, 'python', 5) ----- - -==== Discussion - -Using `deque(maxlen=N)` creates a fixed size queue. When new items -are added and the queue is full, the oldest item is automatically -removed. For example: - -[source,pycon] ----- ->>> q = deque(maxlen=3) ->>> q.append(1) ->>> q.append(2) ->>> q.append(3) ->>> q -deque([1, 2, 3], maxlen=3) ->>> q.append(4) ->>> q -deque([2, 3, 4], maxlen=3) ->>> q.append(5) ->>> q -deque([3, 4, 5], maxlen=3) ----- - -Although you could manually perform such operations on a list (e.g., -appending, deleting, etc.), the queue solution is far more elegant and -runs a lot faster. - -More generally, a `deque` can be used whenever you need a simple queue -structure. If you don't give it a maximum size, you get an unbounded -queue that lets you append and pop items on either end. For example: - -[source,pycon] ----- ->>> q = deque() ->>> q.append(1) ->>> q.append(2) ->>> q.append(3) ->>> q -deque([1, 2, 3]) ->>> q.appendleft(4) ->>> q -deque([4, 1, 2, 3]) ->>> q.pop() -3 ->>> q -deque([4, 1, 2]) ->>> q.popleft() -4 ----- - -Adding or popping items from either end of a queue has O(1) -complexity. This is unlike a list where inserting or removing -items from the front of the list is O(N). diff --git a/chef/cookbooks/python/src/1/mapping_names_to_sequence_elements/example1.py b/chef/cookbooks/python/src/1/mapping_names_to_sequence_elements/example1.py deleted file mode 100644 index 5709457..0000000 --- a/chef/cookbooks/python/src/1/mapping_names_to_sequence_elements/example1.py +++ /dev/null @@ -1,22 +0,0 @@ -# example.py - -from collections import namedtuple - -Stock = namedtuple('Stock', ['name', 'shares', 'price']) - -def compute_cost(records): - total = 0.0 - for rec in records: - s = Stock(*rec) - total += s.shares * s.price - return total - -# Some Data -records = [ - ('GOOG', 100, 490.1), - ('ACME', 100, 123.45), - ('IBM', 50, 91.15) -] - -print(compute_cost(records)) - diff --git a/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example.py b/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example.py deleted file mode 100644 index 141123e..0000000 --- a/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example.py +++ /dev/null @@ -1,15 +0,0 @@ -# example.py -# -# Remove duplicate entries from a sequence while keeping order - -def dedupe(items): - seen = set() - for item in items: - if item not in seen: - yield item - seen.add(item) - -if __name__ == '__main__': - a = [1, 5, 2, 1, 9, 1, 5, 10] - print(a) - print(list(dedupe(a))) diff --git a/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example2.py b/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example2.py deleted file mode 100644 index 87cfa48..0000000 --- a/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example2.py +++ /dev/null @@ -1,23 +0,0 @@ -# example2.py -# -# Remove duplicate entries from a sequence while keeping order - -def dedupe(items, key=None): - seen = set() - for item in items: - val = item if key is None else key(item) - if val not in seen: - yield item - seen.add(val) - -if __name__ == '__main__': - a = [ - {'x': 2, 'y': 3}, - {'x': 1, 'y': 4}, - {'x': 2, 'y': 3}, - {'x': 2, 'y': 3}, - {'x': 10, 'y': 15} - ] - print(a) - print(list(dedupe(a, key=lambda a: (a['x'],a['y'])))) - diff --git a/chef/cookbooks/python/src/1/sort_a_list_of_dictionaries_by_a_common_key/example.py b/chef/cookbooks/python/src/1/sort_a_list_of_dictionaries_by_a_common_key/example.py deleted file mode 100644 index 2a27d49..0000000 --- a/chef/cookbooks/python/src/1/sort_a_list_of_dictionaries_by_a_common_key/example.py +++ /dev/null @@ -1,27 +0,0 @@ -# example.py -# -# Sort a list of a dicts on a common key - -rows = [ - {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, - {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, - {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, - {'fname': 'Big', 'lname': 'Jones', 'uid': 1004} -] - -from operator import itemgetter - -rows_by_fname = sorted(rows, key=itemgetter('fname')) -rows_by_uid = sorted(rows, key=itemgetter('uid')) - -from pprint import pprint - -print("Sorted by fname:") -pprint(rows_by_fname) - -print("Sorted by uid:") -pprint(rows_by_uid) - -rows_by_lfname = sorted(rows, key=itemgetter('lname','fname')) -print("Sorted by lname,fname:") -pprint(rows_by_lfname) diff --git a/chef/cookbooks/python/src/1/sort_objects_without_native_comparison_support/example.py b/chef/cookbooks/python/src/1/sort_objects_without_native_comparison_support/example.py deleted file mode 100644 index be196b7..0000000 --- a/chef/cookbooks/python/src/1/sort_objects_without_native_comparison_support/example.py +++ /dev/null @@ -1,14 +0,0 @@ -from operator import attrgetter - -class User: - def __init__(self, user_id): - self.user_id = user_id - def __repr__(self): - return 'User({})'.format(self.user_id) - -# Example -users = [User(23), User(3), User(99)] -print(users) - -# Sort it by user-id -print(sorted(users, key=attrgetter('user_id'))) diff --git a/chef/cookbooks/python/src/1/transforming_and_reducing_data_at_the_same_time/example.py b/chef/cookbooks/python/src/1/transforming_and_reducing_data_at_the_same_time/example.py deleted file mode 100644 index adc2503..0000000 --- a/chef/cookbooks/python/src/1/transforming_and_reducing_data_at_the_same_time/example.py +++ /dev/null @@ -1,24 +0,0 @@ -# example.py -# -# Some examples of using generators in arguments - -import os -files = os.listdir(os.path.expanduser('~')) -if any(name.endswith('.py') for name in files): - print('There be python!') -else: - print('Sorry, no python.') - -# Output a tuple as CSV -s = ('ACME', 50, 123.45) -print(','.join(str(x) for x in s)) - -# Data reduction across fields of a data structure -portfolio = [ - {'name':'GOOG', 'shares': 50}, - {'name':'YHOO', 'shares': 75}, - {'name':'AOL', 'shares': 20}, - {'name':'SCOX', 'shares': 65} -] -min_shares = min(s['shares'] for s in portfolio) -print(min_shares) diff --git a/chef/cookbooks/python/src/1/unpack_a_fixed_number_of_elements_from_iterables_of_arbitrary_length/example.py b/chef/cookbooks/python/src/1/unpack_a_fixed_number_of_elements_from_iterables_of_arbitrary_length/example.py deleted file mode 100644 index 23d8a57..0000000 --- a/chef/cookbooks/python/src/1/unpack_a_fixed_number_of_elements_from_iterables_of_arbitrary_length/example.py +++ /dev/null @@ -1,21 +0,0 @@ -# example.py -# -# Unpacking of tagged tuples of varying sizes - -records = [ - ('foo', 1, 2), - ('bar', 'hello'), - ('foo', 3, 4), -] - -def do_foo(x,y): - print('foo', x, y) - -def do_bar(s): - print('bar', s) - -for tag, *args in records: - if tag == 'foo': - do_foo(*args) - elif tag == 'bar': - do_bar(*args) diff --git a/chef/cookbooks/python/src/1/working_with_multiple_mappings_as_a_single_mapping/example.py b/chef/cookbooks/python/src/1/working_with_multiple_mappings_as_a_single_mapping/example.py deleted file mode 100644 index 6bd1533..0000000 --- a/chef/cookbooks/python/src/1/working_with_multiple_mappings_as_a_single_mapping/example.py +++ /dev/null @@ -1,51 +0,0 @@ -# example.py -# -# Example of combining dicts into a chainmap - -a = {'x': 1, 'z': 3 } -b = {'y': 2, 'z': 4 } - -# (a) Simple example of combining -from collections import ChainMap -c = ChainMap(a,b) -print(c['x']) # Outputs 1 (from a) -print(c['y']) # Outputs 2 (from b) -print(c['z']) # Outputs 3 (from a) - -# Output some common values -print('len(c):', len(c)) -print('c.keys():', list(c.keys())) -print('c.values():', list(c.values())) - -# Modify some values -c['z'] = 10 -c['w'] = 40 -del c['x'] -print("a:", a) - - -# Example of stacking mappings (like scopes) -values = ChainMap() -values['x'] = 1 - -# Add a new mapping -values = values.new_child() -values['x'] = 2 - -# Add a new mapping -values = values.new_child() -values['x'] = 3 - -print(values) -print(values['x']) - -# Discard last mapping -values = values.parents -print(values) -print(values['x']) - -# Discard last mapping -values = values.parents -print(values) -print(values['x']) - diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/explicit_load.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/explicit_load.py deleted file mode 100644 index 6ab1d9e..0000000 --- a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/explicit_load.py +++ /dev/null @@ -1,23 +0,0 @@ -# Example of explicit module loading using imp library - -import imp -import urllib.request -import sys - -def load_module(url): - u = urllib.request.urlopen(url) - source = u.read().decode('utf-8') - mod = sys.modules.setdefault(url, imp.new_module(url)) - code = compile(source, url, 'exec') - mod.__file__ = url - mod.__package__ = '' - exec(code, mod.__dict__) - return mod - -if __name__ == '__main__': - fib = load_module('http://localhost:15000/fib.py') - print(fib.fib(10)) - spam = load_module('http://localhost:15000/spam.py') - spam.hello('Guido') - print(fib) - print(spam) diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/metaexample.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/metaexample.py deleted file mode 100644 index 13014e7..0000000 --- a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/metaexample.py +++ /dev/null @@ -1,16 +0,0 @@ -# metaexample.py -# -# Example of using a meta-path importer - -# Enable for debugging -if False: - import logging - logging.basicConfig(level=logging.DEBUG) - -import urlimport -urlimport.install_meta('http://localhost:15000') - -import fib -import spam -import grok.blah -print(grok.blah.__file__) diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/pathexample.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/pathexample.py deleted file mode 100644 index f4d8af2..0000000 --- a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/pathexample.py +++ /dev/null @@ -1,18 +0,0 @@ -# Example of path-path import hook - -# Enable for debugging -if False: - import logging - logging.basicConfig(level=logging.DEBUG) - -import urlimport -urlimport.install_path_hook() - -import sys -sys.path.append('http://localhost:15000') - -import fib -import spam -import grok.blah -print(grok.blah.__file__) - diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/fib.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/fib.py deleted file mode 100644 index 269c28c..0000000 --- a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/fib.py +++ /dev/null @@ -1,7 +0,0 @@ -print("I'm fib") - -def fib(n): - if n < 2: - return 1 - else: - return fib(n-1) + fib(n-2) diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/__init__.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/__init__.py deleted file mode 100644 index 13ad048..0000000 --- a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/__init__.py +++ /dev/null @@ -1 +0,0 @@ -print("I'm grok.__init__") diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/blah.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/blah.py deleted file mode 100644 index 8b3733a..0000000 --- a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/blah.py +++ /dev/null @@ -1 +0,0 @@ -print("I'm grok.blah") diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/spam.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/spam.py deleted file mode 100644 index 8d75f63..0000000 --- a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/spam.py +++ /dev/null @@ -1,4 +0,0 @@ -print("I'm spam") - -def hello(name): - print('Hello %s' % name) diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/urlimport.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/urlimport.py deleted file mode 100644 index 96b6a57..0000000 --- a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/urlimport.py +++ /dev/null @@ -1,227 +0,0 @@ -# urlimport.py - -import sys -import importlib.abc -import imp -from urllib.request import urlopen -from urllib.error import HTTPError, URLError -from html.parser import HTMLParser - -# Debugging -import logging -log = logging.getLogger(__name__) - -# Get links from a given URL -def _get_links(url): - class LinkParser(HTMLParser): - def handle_starttag(self, tag, attrs): - if tag == 'a': - attrs = dict(attrs) - links.add(attrs.get('href').rstrip('/')) - - links = set() - try: - log.debug('Getting links from %s' % url) - u = urlopen(url) - parser = LinkParser() - parser.feed(u.read().decode('utf-8')) - except Exception as e: - log.debug('Could not get links. %s', e) - log.debug('links: %r', links) - return links - -class UrlMetaFinder(importlib.abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - self._links = { } - self._loaders = { baseurl : UrlModuleLoader(baseurl) } - - def find_module(self, fullname, path=None): - log.debug('find_module: fullname=%r, path=%r', fullname, path) - if path is None: - baseurl = self._baseurl - else: - if not path[0].startswith(self._baseurl): - return None - baseurl = path[0] - - parts = fullname.split('.') - basename = parts[-1] - log.debug('find_module: baseurl=%r, basename=%r', baseurl, basename) - - # Check link cache - if basename not in self._links: - self._links[baseurl] = _get_links(baseurl) - - # Check if it's a package - if basename in self._links[baseurl]: - log.debug('find_module: trying package %r', fullname) - fullurl = self._baseurl + '/' + basename - # Attempt to load the package (which accesses __init__.py) - loader = UrlPackageLoader(fullurl) - try: - loader.load_module(fullname) - self._links[fullurl] = _get_links(fullurl) - self._loaders[fullurl] = UrlModuleLoader(fullurl) - log.debug('find_module: package %r loaded', fullname) - except ImportError as e: - log.debug('find_module: package failed. %s', e) - loader = None - return loader - - # A normal module - filename = basename + '.py' - if filename in self._links[baseurl]: - log.debug('find_module: module %r found', fullname) - return self._loaders[baseurl] - else: - log.debug('find_module: module %r not found', fullname) - return None - - def invalidate_caches(self): - log.debug('invalidating link cache') - self._links.clear() - -# Module Loader for a URL -class UrlModuleLoader(importlib.abc.SourceLoader): - def __init__(self, baseurl): - self._baseurl = baseurl - self._source_cache = {} - - def module_repr(self, module): - return '' % (module.__name__, module.__file__) - - # Required method - def load_module(self, fullname): - code = self.get_code(fullname) - mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) - mod.__file__ = self.get_filename(fullname) - mod.__loader__ = self - mod.__package__ = fullname.rpartition('.')[0] - exec(code, mod.__dict__) - return mod - - # Optional extensions - def get_code(self, fullname): - src = self.get_source(fullname) - return compile(src, self.get_filename(fullname), 'exec') - - def get_data(self, path): - pass - - def get_filename(self, fullname): - return self._baseurl + '/' + fullname.split('.')[-1] + '.py' - - def get_source(self, fullname): - filename = self.get_filename(fullname) - log.debug('loader: reading %r', filename) - if filename in self._source_cache: - log.debug('loader: cached %r', filename) - return self._source_cache[filename] - try: - u = urlopen(filename) - source = u.read().decode('utf-8') - log.debug('loader: %r loaded', filename) - self._source_cache[filename] = source - return source - except (HTTPError, URLError) as e: - log.debug('loader: %r failed. %s', filename, e) - raise ImportError("Can't load %s" % filename) - - def is_package(self, fullname): - return False - -# Package loader for a URL -class UrlPackageLoader(UrlModuleLoader): - def load_module(self, fullname): - mod = super().load_module(fullname) - mod.__path__ = [ self._baseurl ] - mod.__package__ = fullname - - def get_filename(self, fullname): - return self._baseurl + '/' + '__init__.py' - - def is_package(self, fullname): - return True - -# Utility functions for installing/uninstalling the loader -_installed_meta_cache = { } -def install_meta(address): - if address not in _installed_meta_cache: - finder = UrlMetaFinder(address) - _installed_meta_cache[address] = finder - sys.meta_path.append(finder) - log.debug('%r installed on sys.meta_path', finder) - -def remove_meta(address): - if address in _installed_meta_cache: - finder = _installed_meta_cache.pop(address) - sys.meta_path.remove(finder) - log.debug('%r removed from sys.meta_path', finder) - -# Path finder class for a URL -class UrlPathFinder(importlib.abc.PathEntryFinder): - def __init__(self, baseurl): - self._links = None - self._loader = UrlModuleLoader(baseurl) - self._baseurl = baseurl - - def find_loader(self, fullname): - log.debug('find_loader: %r', fullname) - parts = fullname.split('.') - basename = parts[-1] - # Check link cache - if self._links is None: - self._links = [] # See discussion - self._links = _get_links(self._baseurl) - - # Check if it's a package - if basename in self._links: - log.debug('find_loader: trying package %r', fullname) - fullurl = self._baseurl + '/' + basename - # Attempt to load the package (which accesses __init__.py) - loader = UrlPackageLoader(fullurl) - try: - loader.load_module(fullname) - log.debug('find_loader: package %r loaded', fullname) - except ImportError as e: - log.debug('find_loader: %r is a namespace package', fullname) - loader = None - return (loader, [fullurl]) - - # A normal module - filename = basename + '.py' - if filename in self._links: - log.debug('find_loader: module %r found', fullname) - return (self._loader, []) - else: - log.debug('find_loader: module %r not found', fullname) - return (None, []) - - def invalidate_caches(self): - log.debug('invalidating link cache') - self._links = None - -# Check path to see if it looks like a URL -_url_path_cache = {} -def handle_url(path): - if path.startswith(('http://', 'https://')): - log.debug('Handle path? %s. [Yes]', path) - if path in _url_path_cache: - finder = _url_path_cache[path] - else: - finder = UrlPathFinder(path) - _url_path_cache[path] = finder - return finder - else: - log.debug('Handle path? %s. [No]', path) - -def install_path_hook(): - sys.path_hooks.append(handle_url) - sys.path_importer_cache.clear() - log.debug('Installing handle_url') - -def remove_path_hook(): - sys.path_hooks.remove(handle_url) - sys.path_importer_cache.clear() - log.debug('Removing handle_url') diff --git a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/bar-package/spam/grok.py b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/bar-package/spam/grok.py deleted file mode 100644 index deac877..0000000 --- a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/bar-package/spam/grok.py +++ /dev/null @@ -1 +0,0 @@ -print('bar-package grok!') diff --git a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/example.py b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/example.py deleted file mode 100644 index 3a56460..0000000 --- a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/example.py +++ /dev/null @@ -1,4 +0,0 @@ -import sys -sys.path.extend(['foo-package', 'bar-package']) -import spam.blah -import spam.grok diff --git a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/foo-package/spam/blah.py b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/foo-package/spam/blah.py deleted file mode 100644 index fe4d20c..0000000 --- a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/foo-package/spam/blah.py +++ /dev/null @@ -1 +0,0 @@ -print('foo-package blah!') diff --git a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example1.py b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example1.py deleted file mode 100644 index 7ce1992..0000000 --- a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example1.py +++ /dev/null @@ -1,8 +0,0 @@ -from postimport import when_imported - -@when_imported('threading') -def warn_threads(mod): - print('Threads? Are you crazy?') - -if __name__ == '__main__': - import threading diff --git a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example2.py b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example2.py deleted file mode 100644 index 1083531..0000000 --- a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example2.py +++ /dev/null @@ -1,22 +0,0 @@ -from postimport import when_imported -from functools import wraps - - -def logged(func): - @wraps(func) - def wrapper(*args, **kwargs): - print('Calling', func.__name__, args, kwargs) - return func(*args, **kwargs) - return wrapper - -# Example -@when_imported('math') -def add_logging(mod): - mod.cos = logged(mod.cos) - mod.sin = logged(mod.sin) - -if __name__ == '__main__': - import math - print(math.cos(2)) - print(math.sin(2)) - diff --git a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/postimport.py b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/postimport.py deleted file mode 100644 index 66b60ad..0000000 --- a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/postimport.py +++ /dev/null @@ -1,40 +0,0 @@ -# postimport.py - -import importlib -import sys -from collections import defaultdict - -_post_import_hooks = defaultdict(list) - -class PostImportFinder: - def __init__(self): - self._skip = set() - - def find_module(self, fullname, path=None): - if fullname in self._skip: - return None - self._skip.add(fullname) - return PostImportLoader(self) - -class PostImportLoader: - def __init__(self, finder): - self._finder = finder - - def load_module(self, fullname): - importlib.import_module(fullname) - module = sys.modules[fullname] - for func in _post_import_hooks[fullname]: - func(module) - self._finder._skip.remove(fullname) - return module - -def when_imported(fullname): - def decorate(func): - if fullname in sys.modules: - func(sys.modules[fullname]) - else: - _post_import_hooks[fullname].append(func) - return func - return decorate - -sys.meta_path.insert(0, PostImportFinder()) diff --git a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/example.py b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/example.py deleted file mode 100644 index 5fe4ee3..0000000 --- a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/example.py +++ /dev/null @@ -1,6 +0,0 @@ -import mymodule -a = mymodule.A() -a.spam() - -b = mymodule.B() -b.bar() diff --git a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/__init__.py b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/__init__.py deleted file mode 100644 index 99152c1..0000000 --- a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# __init__.py - -from .a import A -from .b import B - diff --git a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/a.py b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/a.py deleted file mode 100644 index f34204f..0000000 --- a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/a.py +++ /dev/null @@ -1,6 +0,0 @@ -# a.py - -class A: - def spam(self): - print('A.spam') - diff --git a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/b.py b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/b.py deleted file mode 100644 index 28a0dbf..0000000 --- a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/b.py +++ /dev/null @@ -1,8 +0,0 @@ -# b.py - -from .a import A - -class B(A): - def bar(self): - print('B.bar') - diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoclient.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoclient.py deleted file mode 100644 index 5c7c8fd..0000000 --- a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoclient.py +++ /dev/null @@ -1,25 +0,0 @@ -# echoclient.py -# -# An example of a client that connects to an SSL server -# and verifies its certificate - -from socket import socket, AF_INET, SOCK_STREAM -import ssl - -s = socket(AF_INET, SOCK_STREAM) - -# Wrap with an SSL layer and require the server to present its certificate -ssl_s = ssl.wrap_socket(s, - cert_reqs=ssl.CERT_REQUIRED, - ca_certs='server_cert.pem', - ) - -ssl_s.connect(('localhost', 20000)) - -# Communicate with the server -ssl_s.send(b'Hello World!') -resp = ssl_s.recv(8192) -print('Got:', resp) - -# Done -ssl_s.close() diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoserv.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoserv.py deleted file mode 100644 index eb77555..0000000 --- a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoserv.py +++ /dev/null @@ -1,38 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM -from socket import SOL_SOCKET, SO_REUSEADDR -import ssl - -KEYFILE = 'server_key.pem' # Private key of the server -CERTFILE = 'server_cert.pem' # Server certificate (given to client) - -def echo_client(s): - while True: - data = s.recv(8192) - if data == b'': - break - s.send(data) - s.close() - print('Connection closed') - -def echo_server(address): - s = socket(AF_INET, SOCK_STREAM) - s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) - s.bind(address) - s.listen(1) - - # Wrap with an SSL layer requiring client certs - s_ssl = ssl.wrap_socket(s, - keyfile=KEYFILE, - certfile=CERTFILE, - server_side=True - ) - # Wait for connections - while True: - try: - c,a = s_ssl.accept() - print('Got connection', c, a) - echo_client(c) - except Exception as e: - print('{}: {}'.format(e.__class__.__name__, e)) - -echo_server(('', 20000)) diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/makecerts.sh b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/makecerts.sh deleted file mode 100644 index 32d3e9f..0000000 --- a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/makecerts.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -openssl req -new -x509 -days 365 -nodes -out server_cert.pem -keyout server_key.pem -openssl req -new -x509 -days 365 -nodes -out client_cert.pem -keyout client_key.pem diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_client.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_client.py deleted file mode 100644 index 12bc378..0000000 --- a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_client.py +++ /dev/null @@ -1,35 +0,0 @@ -# ssl_xmlrpc_client.py -# -# An XML-RPC client that verifies the server certificate - -from xmlrpc.client import SafeTransport, ServerProxy -import ssl - -class VerifyCertSafeTransport(SafeTransport): - def __init__(self, cafile, certfile=None, keyfile=None): - super().__init__() - self._ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - self._ssl_context.load_verify_locations(cafile) - if certfile: - self._ssl_context.load_cert_chain(certfile, keyfile) - self._ssl_context.verify_mode = ssl.CERT_REQUIRED - - def make_connection(self, host): - s = super().make_connection((host, {'context': self._ssl_context})) - - return s - -# Create the client proxy -s = ServerProxy('https://localhost:15000', - transport=VerifyCertSafeTransport('server_cert.pem', 'client_cert.pem', 'client_key.pem'), -# transport=VerifyCertSafeTransport('server_cert.pem'), - allow_none=True) - -s.set('foo', 'bar') -s.set('spam', [1, 2, 3]) -print(s.keys()) -print(s.get('foo')) -print(s.get('spam')) -s.delete('spam') -print(s.exists('spam')) - diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_server.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_server.py deleted file mode 100644 index f3f3b36..0000000 --- a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_server.py +++ /dev/null @@ -1,49 +0,0 @@ -# ssl_xmlrpc_server.py -# -# An example of an SSL-XMLRPC Server. - -import ssl -from xmlrpc.server import SimpleXMLRPCServer -from sslmixin import SSLMixin - -class SSLSimpleXMLRPCServer(SSLMixin, SimpleXMLRPCServer): - pass - -class KeyValueServer: - _rpc_methods_ = ['get', 'set', 'delete', 'exists', 'keys'] - def __init__(self, *args, **kwargs): - self._data = {} - self._serv = SSLSimpleXMLRPCServer(*args, allow_none=True, **kwargs) - for name in self._rpc_methods_: - self._serv.register_function(getattr(self, name)) - - def get(self, name): - return self._data[name] - - def set(self, name, value): - self._data[name] = value - - def delete(self, name): - del self._data[name] - - def exists(self, name): - return name in self._data - - def keys(self): - return list(self._data) - - def serve_forever(self): - self._serv.serve_forever() - -if __name__ == '__main__': - KEYFILE='server_key.pem' # Private key of the server - CERTFILE='server_cert.pem' # Server certificate - CA_CERTS='client_cert.pem' # Certificates of accepted clients - - kvserv = KeyValueServer(('', 15000), - keyfile=KEYFILE, - certfile=CERTFILE, - ca_certs=CA_CERTS, - cert_reqs=ssl.CERT_REQUIRED, - ) - kvserv.serve_forever() diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/sslmixin.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/sslmixin.py deleted file mode 100644 index da19e8c..0000000 --- a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/sslmixin.py +++ /dev/null @@ -1,26 +0,0 @@ -import ssl - -class SSLMixin: - def __init__(self, *args, - keyfile=None, certfile=None, ca_certs=None, cert_reqs=ssl.CERT_NONE, - **kwargs): - self._keyfile = keyfile - self._certfile = certfile - self._ca_certs = ca_certs - self._cert_reqs = cert_reqs - super().__init__(*args, **kwargs) - - def get_request(self): - client, addr = super().get_request() - client_ssl = ssl.wrap_socket(client, - keyfile = self._keyfile, - certfile = self._certfile, - ca_certs = self._ca_certs, - cert_reqs = self._cert_reqs, - server_side = True) - return client_ssl, addr - - - - - diff --git a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/client1.py b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/client1.py deleted file mode 100644 index 7b4f9ef..0000000 --- a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/client1.py +++ /dev/null @@ -1,7 +0,0 @@ -from urllib.request import urlopen - -u = urlopen('http://localhost:8080/hello?name=Guido') -print(u.read().decode('utf-8')) - -u = urlopen('http://localhost:8080/localtime') -print(u.read().decode('utf-8')) diff --git a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/example1.py b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/example1.py deleted file mode 100644 index 75b24d0..0000000 --- a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/example1.py +++ /dev/null @@ -1,47 +0,0 @@ -import time - -_hello_resp = '''\ - - - Hello {name} - - -

Hello {name}!

- -''' - -def hello_world(environ, start_response): - start_response('200 OK', [ ('Content-type','text/html')]) - params = environ['params'] - resp = _hello_resp.format(name=params.get('name')) - yield resp.encode('utf-8') - -_localtime_resp = '''\ - -''' - -def localtime(environ, start_response): - start_response('200 OK', [ ('Content-type', 'application/xml') ]) - resp = _localtime_resp.format(t=time.localtime()) - yield resp.encode('utf-8') - -if __name__ == '__main__': - from resty import PathDispatcher - from wsgiref.simple_server import make_server - - # Create the dispatcher and register functions - dispatcher = PathDispatcher() - dispatcher.register('GET', '/hello', hello_world) - dispatcher.register('GET', '/localtime', localtime) - - # Launch a basic server - httpd = make_server('', 8080, dispatcher) - print('Serving on port 8080...') - httpd.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/resty.py b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/resty.py deleted file mode 100644 index 5e979db..0000000 --- a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/resty.py +++ /dev/null @@ -1,24 +0,0 @@ -# resty.py - -import cgi - -def notfound_404(environ, start_response): - start_response('404 Not Found', [ ('Content-type', 'text/plain') ]) - return [b'Not Found'] - -class PathDispatcher: - def __init__(self): - self.pathmap = { } - - def __call__(self, environ, start_response): - path = environ['PATH_INFO'] - params = cgi.FieldStorage(environ['wsgi.input'], - environ=environ) - method = environ['REQUEST_METHOD'].lower() - environ['params'] = { key: params.getvalue(key) for key in params } - handler = self.pathmap.get((method,path), notfound_404) - return handler(environ, start_response) - - def register(self, method, path, function): - self.pathmap[method.lower(), path] = function - return function diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoclient.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoclient.py deleted file mode 100644 index a389866..0000000 --- a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoclient.py +++ /dev/null @@ -1,9 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM -s = socket(AF_INET, SOCK_STREAM) -s.connect(('localhost', 20000)) - -s.send(b'Hello\n') -resp = s.recv(8192) -print('Response:', resp) -s.close() - diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv.py deleted file mode 100644 index b2507d4..0000000 --- a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv.py +++ /dev/null @@ -1,15 +0,0 @@ -from socketserver import BaseRequestHandler, TCPServer - -class EchoHandler(BaseRequestHandler): - def handle(self): - print('Got connection from', self.client_address) - while True: - msg = self.request.recv(8192) - if not msg: - break - self.request.send(msg) - -if __name__ == '__main__': - serv = TCPServer(('', 20000), EchoHandler) - print('Echo server running on port 20000') - serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv1.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv1.py deleted file mode 100644 index b2507d4..0000000 --- a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv1.py +++ /dev/null @@ -1,15 +0,0 @@ -from socketserver import BaseRequestHandler, TCPServer - -class EchoHandler(BaseRequestHandler): - def handle(self): - print('Got connection from', self.client_address) - while True: - msg = self.request.recv(8192) - if not msg: - break - self.request.send(msg) - -if __name__ == '__main__': - serv = TCPServer(('', 20000), EchoHandler) - print('Echo server running on port 20000') - serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv2.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv2.py deleted file mode 100644 index 8574dce..0000000 --- a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv2.py +++ /dev/null @@ -1,14 +0,0 @@ -from socketserver import StreamRequestHandler, TCPServer - -class EchoHandler(StreamRequestHandler): - def handle(self): - print('Got connection from', self.client_address) - # self.rfile is a file-like object for reading - for line in self.rfile: - # self.wfile is a file-like object for writing - self.wfile.write(line) - -if __name__ == '__main__': - serv = TCPServer(('', 20000), EchoHandler) - print('Echo server running on port 20000') - serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv3.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv3.py deleted file mode 100644 index 24ca788..0000000 --- a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv3.py +++ /dev/null @@ -1,21 +0,0 @@ -from socketserver import StreamRequestHandler, TCPServer - -class EchoHandler(StreamRequestHandler): - def handle(self): - print('Got connection from', self.client_address) - # self.rfile is a file-like object for reading - for line in self.rfile: - # self.wfile is a file-like object for writing - self.wfile.write(line) - -if __name__ == '__main__': - import socket - - serv = TCPServer(('', 20000), EchoHandler, bind_and_activate=False) - # Set up various socket options - serv.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) - # Bind and activate - serv.server_bind() - serv.server_activate() - print('Echo server running on port 20000') - serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv4.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv4.py deleted file mode 100644 index e5a83c2..0000000 --- a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv4.py +++ /dev/null @@ -1,22 +0,0 @@ -from socketserver import StreamRequestHandler, TCPServer -import socket - -class EchoHandler(StreamRequestHandler): - timeout = 5 - rbufsize = -1 - wbufsize = 0 - disable_nagle_algorithm = False - def handle(self): - print('Got connection from', self.client_address) - # self.rfile is a file-like object for reading - try: - for line in self.rfile: - # self.wfile is a file-like object for writing - self.wfile.write(line) - except socket.timeout: - print('Timed out!') - -if __name__ == '__main__': - serv = TCPServer(('', 20000), EchoHandler) - print('Echo server running on port 20000') - serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv5.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv5.py deleted file mode 100644 index 44cb709..0000000 --- a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv5.py +++ /dev/null @@ -1,23 +0,0 @@ -# Echo server using sockets directly - -from socket import socket, AF_INET, SOCK_STREAM - -def echo_handler(address, client_sock): - print('Got connection from {}'.format(address)) - while True: - msg = client_sock.recv(8192) - if not msg: - break - client_sock.sendall(msg) - client_sock.close() - -def echo_server(address, backlog=5): - sock = socket(AF_INET, SOCK_STREAM) - sock.bind(address) - sock.listen(backlog) - while True: - client_sock, client_addr = sock.accept() - echo_handler(client_addr, client_sock) - -if __name__ == '__main__': - echo_server(('', 20000)) diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/threadedserv.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/threadedserv.py deleted file mode 100644 index 70f69ca..0000000 --- a/chef/cookbooks/python/src/11/creating_a_tcp_server/threadedserv.py +++ /dev/null @@ -1,20 +0,0 @@ -from socketserver import StreamRequestHandler, TCPServer - -class EchoHandler(StreamRequestHandler): - def handle(self): - print('Got connection from', self.client_address) - # self.rfile is a file-like object for reading - for line in self.rfile: - # self.wfile is a file-like object for writing - self.wfile.write(line) - -if __name__ == '__main__': - from threading import Thread - NWORKERS = 16 - serv = TCPServer(('', 20000), EchoHandler) - for n in range(NWORKERS): - t = Thread(target=serv.serve_forever) - t.daemon = True - t.start() - print('Multithreaded server running on port 20000') - serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_udp_server/client.py b/chef/cookbooks/python/src/11/creating_a_udp_server/client.py deleted file mode 100644 index 6838d36..0000000 --- a/chef/cookbooks/python/src/11/creating_a_udp_server/client.py +++ /dev/null @@ -1,5 +0,0 @@ -from socket import socket, AF_INET, SOCK_DGRAM - -s = socket(AF_INET, SOCK_DGRAM) -s.sendto(b'', ('localhost', 20000)) -print(s.recvfrom(8192)) diff --git a/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv1.py b/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv1.py deleted file mode 100644 index 99bebf0..0000000 --- a/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv1.py +++ /dev/null @@ -1,14 +0,0 @@ -from socketserver import BaseRequestHandler, UDPServer -import time - -class TimeHandler(BaseRequestHandler): - def handle(self): - print('Got connection from', self.client_address) - # Get message and client socket - msg, sock = self.request - resp = time.ctime() - sock.sendto(resp.encode('ascii'), self.client_address) - -if __name__ == '__main__': - serv = UDPServer(('', 20000), TimeHandler) - serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv2.py b/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv2.py deleted file mode 100644 index d753494..0000000 --- a/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv2.py +++ /dev/null @@ -1,14 +0,0 @@ -from socket import socket, AF_INET, SOCK_DGRAM -import time - -def time_server(address): - sock = socket(AF_INET, SOCK_DGRAM) - sock.bind(address) - while True: - msg, addr = sock.recvfrom(8192) - print('Got message from', addr) - resp = time.ctime() - sock.sendto(resp.encode('ascii'), addr) - -if __name__ == '__main__': - time_server(('', 20000)) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/eventhandler.py b/chef/cookbooks/python/src/11/event_driven_io_explained/eventhandler.py deleted file mode 100644 index 70e8bd1..0000000 --- a/chef/cookbooks/python/src/11/event_driven_io_explained/eventhandler.py +++ /dev/null @@ -1,32 +0,0 @@ -class EventHandler: - def fileno(self): - 'Return the associated file descriptor' - raise NotImplemented('must implement') - - def wants_to_receive(self): - 'Return True if receiving is allowed' - return False - - def handle_receive(self): - 'Perform the receive operation' - pass - - def wants_to_send(self): - 'Return True if sending is requested' - return False - - def handle_send(self): - 'Send outgoing data' - pass - -import select - -def event_loop(handlers): - while True: - wants_recv = [h for h in handlers if h.wants_to_receive()] - wants_send = [h for h in handlers if h.wants_to_send()] - can_recv, can_send, _ = select.select(wants_recv, wants_send, []) - for h in can_recv: - h.handle_receive() - for h in can_send: - h.handle_send() diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/tcpclient.py b/chef/cookbooks/python/src/11/event_driven_io_explained/tcpclient.py deleted file mode 100644 index df65e8a..0000000 --- a/chef/cookbooks/python/src/11/event_driven_io_explained/tcpclient.py +++ /dev/null @@ -1,7 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM - -s = socket(AF_INET, SOCK_STREAM) -s.connect(('localhost', 16000)) -s.send(b'Hello\n') -print('Got:', s.recv(8192)) -s.close() diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/tcpserver.py b/chef/cookbooks/python/src/11/event_driven_io_explained/tcpserver.py deleted file mode 100644 index 61d9e26..0000000 --- a/chef/cookbooks/python/src/11/event_driven_io_explained/tcpserver.py +++ /dev/null @@ -1,61 +0,0 @@ -# TCP Example - -import socket -from eventhandler import EventHandler, event_loop - -class TCPServer(EventHandler): - def __init__(self, address, client_handler, handler_list): - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) - self.sock.bind(address) - self.sock.listen(1) - self.client_handler = client_handler - self.handler_list = handler_list - - def fileno(self): - return self.sock.fileno() - - def wants_to_receive(self): - return True - - def handle_receive(self): - client, addr = self.sock.accept() - # Add the client to the event loop's handler list - self.handler_list.append(self.client_handler(client, self.handler_list)) - -class TCPClient(EventHandler): - def __init__(self, sock, handler_list): - self.sock = sock - self.handler_list = handler_list - self.outgoing = bytearray() - - def fileno(self): - return self.sock.fileno() - - def close(self): - self.sock.close() - # Remove myself from the event loop's handler list - self.handler_list.remove(self) - - def wants_to_send(self): - return True if self.outgoing else False - - def handle_send(self): - nsent = self.sock.send(self.outgoing) - self.outgoing = self.outgoing[nsent:] - -class TCPEchoClient(TCPClient): - def wants_to_receive(self): - return True - - def handle_receive(self): - data = self.sock.recv(8192) - if not data: - self.close() - else: - self.outgoing.extend(data) - -if __name__ == '__main__': - handlers = [] - handlers.append(TCPServer(('',16000), TCPEchoClient, handlers)) - event_loop(handlers) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/threadpool.py b/chef/cookbooks/python/src/11/event_driven_io_explained/threadpool.py deleted file mode 100644 index 54b647a..0000000 --- a/chef/cookbooks/python/src/11/event_driven_io_explained/threadpool.py +++ /dev/null @@ -1,65 +0,0 @@ -import socket -from concurrent.futures import ThreadPoolExecutor -from eventhandler import EventHandler, event_loop - -class ThreadPoolHandler(EventHandler): - def __init__(self, nworkers): - self.signal_done_sock, self.done_sock = socket.socketpair() - self.pending = [] - self.pool = ThreadPoolExecutor(nworkers) - - def fileno(self): - return self.done_sock.fileno() - - # Callback that executes when the thread is done - def _complete(self, callback, r): - self.pending.append((callback, r.result())) - self.signal_done_sock.send(b'x') - - # Run a function in a thread pool - def run(self, func, args=(), kwargs={},*,callback): - r = self.pool.submit(func, *args, **kwargs) - r.add_done_callback(lambda r: self._complete(callback, r)) - - def wants_to_receive(self): - return True - - # Run callback functions of completed work - def handle_receive(self): - # Invoke all pending callback functions - for callback, result in self.pending: - callback(result) - self.done_sock.recv(1) - self.pending = [] - -# A really bad fibonacci implementation -def fib(n): - if n < 2: - return 1 - else: - return fib(n - 1) + fib(n - 2) - -class UDPServer(EventHandler): - def __init__(self, address): - self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.sock.bind(address) - - def fileno(self): - return self.sock.fileno() - - def wants_to_receive(self): - return True - -class UDPFibServer(UDPServer): - def handle_receive(self): - msg, addr = self.sock.recvfrom(128) - n = int(msg) - pool.run(fib, (n,), callback=lambda r: self.respond(r, addr)) - - def respond(self, result, addr): - self.sock.sendto(str(result).encode('ascii'), addr) - -if __name__ == '__main__': - pool = ThreadPoolHandler(16) - handlers = [ pool, UDPFibServer(('',16000))] - event_loop(handlers) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/thrpoolclient.py b/chef/cookbooks/python/src/11/event_driven_io_explained/thrpoolclient.py deleted file mode 100644 index 4c0b681..0000000 --- a/chef/cookbooks/python/src/11/event_driven_io_explained/thrpoolclient.py +++ /dev/null @@ -1,6 +0,0 @@ -from socket import * -sock = socket(AF_INET, SOCK_DGRAM) -for x in range(40): - sock.sendto(str(x).encode('ascii'), ('localhost', 16000)) - resp = sock.recvfrom(8192) - print(resp[0]) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/udpclient.py b/chef/cookbooks/python/src/11/event_driven_io_explained/udpclient.py deleted file mode 100644 index 47f68ca..0000000 --- a/chef/cookbooks/python/src/11/event_driven_io_explained/udpclient.py +++ /dev/null @@ -1,7 +0,0 @@ -from socket import * -s = socket(AF_INET, SOCK_DGRAM) -s.sendto(b'', ('localhost', 14000)) -print(s.recvfrom(128)) - -s.sendto(b'Hello', ('localhost', 15000)) -print(s.recvfrom(128)) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/udpserver.py b/chef/cookbooks/python/src/11/event_driven_io_explained/udpserver.py deleted file mode 100644 index c476fd1..0000000 --- a/chef/cookbooks/python/src/11/event_driven_io_explained/udpserver.py +++ /dev/null @@ -1,29 +0,0 @@ -import socket -import time - -from eventhandler import EventHandler, event_loop - -class UDPServer(EventHandler): - def __init__(self, address): - self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.sock.bind(address) - - def fileno(self): - return self.sock.fileno() - - def wants_to_receive(self): - return True - -class UDPTimeServer(UDPServer): - def handle_receive(self): - msg, addr = self.sock.recvfrom(1) - self.sock.sendto(time.ctime().encode('ascii'), addr) - -class UDPEchoServer(UDPServer): - def handle_receive(self): - msg, addr = self.sock.recvfrom(8192) - self.sock.sendto(msg, addr) - -if __name__ == '__main__': - handlers = [ UDPTimeServer(('',14000)), UDPEchoServer(('',15000)) ] - event_loop(handlers) diff --git a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpclient.py b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpclient.py deleted file mode 100644 index 9a6ecbd..0000000 --- a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpclient.py +++ /dev/null @@ -1,22 +0,0 @@ -import json - -class RPCProxy: - def __init__(self, connection): - self._connection = connection - def __getattr__(self, name): - def do_rpc(*args, **kwargs): - self._connection.send(json.dumps((name, args, kwargs))) - result = json.loads(self._connection.recv()) - return result - return do_rpc - -# Example use -from multiprocessing.connection import Client -c = Client(('localhost', 17000), authkey=b'peekaboo') -proxy = RPCProxy(c) -print(proxy.add(2, 3)) -print(proxy.sub(2, 3)) -try: - print(proxy.sub([1, 2], 4)) -except Exception as e: - print(e) diff --git a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpcserver.py b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpcserver.py deleted file mode 100644 index d87a806..0000000 --- a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpcserver.py +++ /dev/null @@ -1,50 +0,0 @@ -# rpcserver.py -import json - -class RPCHandler: - def __init__(self): - self._functions = { } - - def register_function(self, func): - self._functions[func.__name__] = func - - def handle_connection(self, connection): - try: - while True: - # Receive a message - func_name, args, kwargs = json.loads(connection.recv()) - # Run the RPC and send a response - try: - r = self._functions[func_name](*args,**kwargs) - connection.send(json.dumps(r)) - except Exception as e: - connection.send(json.dumps(str(e))) - except EOFError: - pass - -# Example use -from multiprocessing.connection import Listener -from threading import Thread - -def rpc_server(handler, address, authkey): - sock = Listener(address, authkey=authkey) - while True: - client = sock.accept() - t = Thread(target=handler.handle_connection, args=(client,)) - t.daemon = True - t.start() - -# Some remote functions -def add(x, y): - return x + y - -def sub(x, y): - return x - y - -# Register with a handler -handler = RPCHandler() -handler.register_function(add) -handler.register_function(sub) - -# Run the server -rpc_server(handler, ('localhost', 17000), authkey=b'peekaboo') diff --git a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcclient.py b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcclient.py deleted file mode 100644 index 98c2e34..0000000 --- a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcclient.py +++ /dev/null @@ -1,24 +0,0 @@ -import pickle - -class RPCProxy: - def __init__(self, connection): - self._connection = connection - def __getattr__(self, name): - def do_rpc(*args, **kwargs): - self._connection.send(pickle.dumps((name, args, kwargs))) - result = pickle.loads(self._connection.recv()) - if isinstance(result, Exception): - raise result - return result - return do_rpc - -# Example use -from multiprocessing.connection import Client -c = Client(('localhost', 17000), authkey=b'peekaboo') -proxy = RPCProxy(c) -print(proxy.add(2, 3)) -print(proxy.sub(2, 3)) -try: - proxy.sub([1, 2], 4) -except Exception as e: - print(e) diff --git a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcserver.py b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcserver.py deleted file mode 100644 index 2eb2e18..0000000 --- a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcserver.py +++ /dev/null @@ -1,50 +0,0 @@ -# rpcserver.py - -import pickle -class RPCHandler: - def __init__(self): - self._functions = { } - - def register_function(self, func): - self._functions[func.__name__] = func - - def handle_connection(self, connection): - try: - while True: - # Receive a message - func_name, args, kwargs = pickle.loads(connection.recv()) - # Run the RPC and send a response - try: - r = self._functions[func_name](*args,**kwargs) - connection.send(pickle.dumps(r)) - except Exception as e: - connection.send(pickle.dumps(e)) - except EOFError: - pass - -# Example use -from multiprocessing.connection import Listener -from threading import Thread - -def rpc_server(handler, address, authkey): - sock = Listener(address, authkey=authkey) - while True: - client = sock.accept() - t = Thread(target=handler.handle_connection, args=(client,)) - t.daemon = True - t.start() - -# Some remote functions -def add(x, y): - return x + y - -def sub(x, y): - return x - y - -# Register with a handler -handler = RPCHandler() -handler.register_function(add) -handler.register_function(sub) - -# Run the server -rpc_server(handler, ('localhost', 17000), authkey=b'peekaboo') diff --git a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example1.py b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example1.py deleted file mode 100644 index e550bc5..0000000 --- a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example1.py +++ /dev/null @@ -1,26 +0,0 @@ -# A basic GET request - -from urllib import request, parse - -# Base URL being accessed -url = 'http://httpbin.org/get' - -# Dictionary of query parameters (if any) -parms = { - 'name1' : 'value1', - 'name2' : 'value2' -} - -# Encode the query string -querystring = parse.urlencode(parms) - -# Make a GET request and read the response -u = request.urlopen(url+'?' + querystring) -resp = u.read() - -import json -from pprint import pprint - -json_resp = json.loads(resp.decode('utf-8')) -pprint(json_resp) - diff --git a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example2.py b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example2.py deleted file mode 100644 index ca9a42b..0000000 --- a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example2.py +++ /dev/null @@ -1,26 +0,0 @@ -# A basic POST request - -from urllib import request, parse - -# Base URL being accessed -url = 'http://httpbin.org/post' - -# Dictionary of query parameters (if any) -parms = { - 'name1' : 'value1', - 'name2' : 'value2' -} - -# Encode the query string -querystring = parse.urlencode(parms) - -# Make a POST request and read the response -u = request.urlopen(url, querystring.encode('ascii')) -resp = u.read() - -import json -from pprint import pprint - -json_resp = json.loads(resp.decode('utf-8')) -pprint(json_resp) - diff --git a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example3.py b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example3.py deleted file mode 100644 index 02da1ab..0000000 --- a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example3.py +++ /dev/null @@ -1,25 +0,0 @@ -# A POST request using requests library -import requests - -# Base URL being accessed -url = 'http://httpbin.org/post' - -# Dictionary of query parameters (if any) -parms = { - 'name1' : 'value1', - 'name2' : 'value2' -} - -# Extra headers -headers = { - 'User-agent' : 'none/ofyourbusiness', - 'Spam' : 'Eggs' -} - -resp = requests.post(url, data=parms, headers=headers) - -# Decoded text returned by the request -text = resp.text - -from pprint import pprint -pprint(resp.json) diff --git a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example4.py b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example4.py deleted file mode 100644 index e8f51fc..0000000 --- a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example4.py +++ /dev/null @@ -1,15 +0,0 @@ -# Example of a HEAD request - -import requests - -resp = requests.head('http://www.python.org/index.html') - -status = resp.status_code -last_modified = resp.headers['last-modified'] -content_type = resp.headers['content-type'] -content_length = resp.headers['content-length'] - -print(status) -print(last_modified) -print(content_type) -print(content_length) diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/client1.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/client1.py deleted file mode 100644 index 004f9b9..0000000 --- a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/client1.py +++ /dev/null @@ -1,9 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM - -s = socket(AF_INET, SOCK_STREAM) -s.connect(('localhost', 15000)) -s.send(b'Hello\n') -print('Got:', s.recv(8192)) -s.send(b'World\n') -print('Got:', s.recv(8192)) -s.close() diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server.py deleted file mode 100644 index 2dd6790..0000000 --- a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server.py +++ /dev/null @@ -1,38 +0,0 @@ -# server.py -import socket -import struct - -def send_fd(sock, fd): - ''' - Send a single file descriptor. - ''' - sock.sendmsg([b'x'], - [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('i', fd))]) - ack = sock.recv(2) - assert ack == b'OK' - -def server(work_address, port): - # Wait for the worker to connect - work_serv = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - work_serv.bind(work_address) - work_serv.listen(1) - worker, addr = work_serv.accept() - - # Now run a TCP/IP server and send clients to worker - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) - s.bind(('',port)) - s.listen(1) - while True: - client, addr = s.accept() - print('SERVER: Got connection from', addr) - send_fd(worker, client.fileno()) - client.close() - -if __name__ == '__main__': - import sys - if len(sys.argv) != 3: - print('Usage: server.py server_address port', file=sys.stderr) - raise SystemExit(1) - - server(sys.argv[1], int(sys.argv[2])) diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server1.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server1.py deleted file mode 100644 index 3df1d18..0000000 --- a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server1.py +++ /dev/null @@ -1,42 +0,0 @@ -# Example of file descriptor passing using multiprocessing - -import multiprocessing -from multiprocessing.reduction import recv_handle, send_handle -import socket - -def worker(in_p, out_p): - out_p.close() - while True: - fd = recv_handle(in_p) - print('CHILD: GOT FD', fd) - with socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=fd) as s: - while True: - msg = s.recv(1024) - if not msg: - break - print('CHILD: RECV {!r}'.format(msg)) - s.send(msg) - -def server(address, in_p, out_p, worker_pid): - in_p.close() - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) - s.bind(address) - s.listen(1) - while True: - client, addr = s.accept() - print('SERVER: Got connection from', addr) - send_handle(out_p, client.fileno(), worker_pid) - client.close() - -if __name__ == '__main__': - c1, c2 = multiprocessing.Pipe() - worker_p = multiprocessing.Process(target=worker, args=(c1,c2)) - worker_p.start() - - server_p = multiprocessing.Process(target=server, - args=(('', 15000), c1, c2, worker_p.pid)) - server_p.start() - - c1.close() - c2.close() diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/servermp.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/servermp.py deleted file mode 100644 index d7bab24..0000000 --- a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/servermp.py +++ /dev/null @@ -1,29 +0,0 @@ -# servermp.py -from multiprocessing.connection import Listener -from multiprocessing.reduction import send_handle -import socket - -def server(work_address, port): - # Wait for the worker to connect - work_serv = Listener(work_address, authkey=b'peekaboo') - worker = work_serv.accept() - worker_pid = worker.recv() - - # Now run a TCP/IP server and send clients to worker - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) - s.bind(('', port)) - s.listen(1) - while True: - client, addr = s.accept() - print('SERVER: Got connection from', addr) - send_handle(worker, client.fileno(), worker_pid) - client.close() - -if __name__ == '__main__': - import sys - if len(sys.argv) != 3: - print('Usage: server.py server_address port', file=sys.stderr) - raise SystemExit(1) - - server(sys.argv[1], int(sys.argv[2])) diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/worker.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/worker.py deleted file mode 100644 index 2ee5933..0000000 --- a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/worker.py +++ /dev/null @@ -1,37 +0,0 @@ -# worker.py -import socket -import struct - -def recv_fd(sock): - ''' - Receive a single file descriptor - ''' - msg, ancdata, flags, addr = sock.recvmsg(1, - socket.CMSG_LEN(struct.calcsize('i'))) - - cmsg_level, cmsg_type, cmsg_data = ancdata[0] - assert cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS - sock.sendall(b'OK') - return struct.unpack('i', cmsg_data)[0] - -def worker(server_address): - serv = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - serv.connect(server_address) - while True: - fd = recv_fd(serv) - print('WORKER: GOT FD', fd) - with socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=fd) as client: - while True: - msg = client.recv(1024) - if not msg: - break - print('WORKER: RECV {!r}'.format(msg)) - client.send(msg) - -if __name__ == '__main__': - import sys - if len(sys.argv) != 2: - print('Usage: worker.py server_address', file=sys.stderr) - raise SystemExit(1) - - worker(sys.argv[1]) diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/workermp.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/workermp.py deleted file mode 100644 index 4717897..0000000 --- a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/workermp.py +++ /dev/null @@ -1,28 +0,0 @@ -# workermp.py - -from multiprocessing.connection import Client -from multiprocessing.reduction import recv_handle -import os -import socket - -def worker(server_address): - serv = Client(server_address, authkey=b'peekaboo') - serv.send(os.getpid()) - while True: - fd = recv_handle(serv) - print('WORKER: GOT FD', fd) - with socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=fd) as client: - while True: - msg = client.recv(1024) - if not msg: - break - print('WORKER: RECV {!r}'.format(msg)) - client.send(msg) - -if __name__ == '__main__': - import sys - if len(sys.argv) != 2: - print('Usage: worker.py server_address', file=sys.stderr) - raise SystemExit(1) - - worker(sys.argv[1]) diff --git a/chef/cookbooks/python/src/11/simple_authentication_of_clients/auth.py b/chef/cookbooks/python/src/11/simple_authentication_of_clients/auth.py deleted file mode 100644 index 2cb9f89..0000000 --- a/chef/cookbooks/python/src/11/simple_authentication_of_clients/auth.py +++ /dev/null @@ -1,26 +0,0 @@ -# auth.py - -import hmac -import os - -def client_authenticate(connection, secret_key): - ''' - Authenticate client to a remote service. - connection represents a network connection. - secret_key is a key known only to both client/server. - ''' - message = connection.recv(32) - hash = hmac.new(secret_key, message) - digest = hash.digest() - connection.send(digest) - -def server_authenticate(connection, secret_key): - ''' - Request client authentication. - ''' - message = os.urandom(32) - connection.send(message) - hash = hmac.new(secret_key, message) - digest = hash.digest() - response = connection.recv(len(digest)) - return hmac.compare_digest(digest,response) diff --git a/chef/cookbooks/python/src/11/simple_authentication_of_clients/client.py b/chef/cookbooks/python/src/11/simple_authentication_of_clients/client.py deleted file mode 100644 index 2c82daa..0000000 --- a/chef/cookbooks/python/src/11/simple_authentication_of_clients/client.py +++ /dev/null @@ -1,11 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM -from auth import client_authenticate - -secret_key = b'peekaboo' - -s = socket(AF_INET, SOCK_STREAM) -s.connect(('localhost', 18000)) -client_authenticate(s, secret_key) -s.send(b'Hello World') -resp = s.recv(1024) -print('Got:', resp) diff --git a/chef/cookbooks/python/src/11/simple_authentication_of_clients/server.py b/chef/cookbooks/python/src/11/simple_authentication_of_clients/server.py deleted file mode 100644 index 09f575e..0000000 --- a/chef/cookbooks/python/src/11/simple_authentication_of_clients/server.py +++ /dev/null @@ -1,26 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM -from auth import server_authenticate - -secret_key = b'peekaboo' - -def echo_handler(client_sock): - if not server_authenticate(client_sock, secret_key): - client_sock.close() - return - while True: - msg = client_sock.recv(8192) - if not msg: - break - client_sock.sendall(msg) - -def echo_server(address): - s = socket(AF_INET, SOCK_STREAM) - s.bind(address) - s.listen(5) - while True: - c,a = s.accept() - echo_handler(c) - -print('Echo server running on port 18000') - -echo_server(('', 18000)) diff --git a/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoclient.py b/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoclient.py deleted file mode 100644 index 79551d1..0000000 --- a/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoclient.py +++ /dev/null @@ -1,9 +0,0 @@ -from multiprocessing.connection import Client -c = Client(('localhost', 25000), authkey=b'peekaboo') -c.send('hello') -print('Got:', c.recv()) -c.send(42) -print('Got:', c.recv()) -c.send([1, 2, 3, 4, 5]) -print('Got:', c.recv()) - diff --git a/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoserv.py b/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoserv.py deleted file mode 100644 index f9659b4..0000000 --- a/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoserv.py +++ /dev/null @@ -1,21 +0,0 @@ -from multiprocessing.connection import Listener -import traceback - -def echo_client(conn): - try: - while True: - msg = conn.recv() - conn.send(msg) - except EOFError: - print('Connection closed') - -def echo_server(address, authkey): - serv = Listener(address, authkey=authkey) - while True: - try: - client = serv.accept() - echo_client(client) - except Exception: - traceback.print_exc() - -echo_server(('', 25000), authkey=b'peekaboo') diff --git a/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/client.py b/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/client.py deleted file mode 100644 index 48685cd..0000000 --- a/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/client.py +++ /dev/null @@ -1,9 +0,0 @@ -from xmlrpc.client import ServerProxy -s = ServerProxy('http://localhost:15000', allow_none=True) -s.set('foo', 'bar') -s.set('spam', [1, 2, 3]) -print(s.keys()) -print(s.get('foo')) -print(s.get('spam')) -s.delete('spam') -print(s.exists('spam')) diff --git a/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/keyserv.py b/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/keyserv.py deleted file mode 100644 index 9d6410c..0000000 --- a/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/keyserv.py +++ /dev/null @@ -1,32 +0,0 @@ -from xmlrpc.server import SimpleXMLRPCServer - -class KeyValueServer: - _rpc_methods_ = ['get', 'set', 'delete', 'exists', 'keys'] - def __init__(self, address): - self._data = {} - self._serv = SimpleXMLRPCServer(address, allow_none=True) - for name in self._rpc_methods_: - self._serv.register_function(getattr(self, name)) - - def get(self, name): - return self._data[name] - - def set(self, name, value): - self._data[name] = value - - def delete(self, name): - del self._data[name] - - def exists(self, name): - return name in self._data - - def keys(self): - return list(self._data) - - def serve_forever(self): - self._serv.serve_forever() - -# Example -if __name__ == '__main__': - kvserv = KeyValueServer(('', 15000)) - kvserv.serve_forever() diff --git a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/client.py b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/client.py deleted file mode 100644 index 48b3e50..0000000 --- a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/client.py +++ /dev/null @@ -1,12 +0,0 @@ -from zerocopy import recv_into -from socket import * - -c = socket(AF_INET, SOCK_STREAM) -c.connect(('localhost', 25000)) - -import numpy -a = numpy.zeros(shape=50000000, dtype=float) -print(a[0:10]) -recv_into(a, c) -print(a[0:10]) -print(a[-10:]) diff --git a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/server.py b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/server.py deleted file mode 100644 index 5e16653..0000000 --- a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/server.py +++ /dev/null @@ -1,13 +0,0 @@ -from zerocopy import send_from -from socket import * - -s = socket(AF_INET, SOCK_STREAM) -s.bind(('', 25000)) -s.listen(1) -c,a = s.accept() - -import numpy -a = numpy.arange(0.0, 50000000.0) -send_from(a, c) -c.close() - diff --git a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/zerocopy.py b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/zerocopy.py deleted file mode 100644 index 42253ec..0000000 --- a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/zerocopy.py +++ /dev/null @@ -1,13 +0,0 @@ -# zerocopy.py - -def send_from(arr, dest): - view = memoryview(arr).cast('B') - while len(view): - nsent = dest.send(view) - view = view[nsent:] - -def recv_into(arr, source): - view = memoryview(arr).cast('B') - while len(view): - nrecv = source.recv_into(view) - view = view[nrecv:] diff --git a/chef/cookbooks/python/src/12/defining_an_actor_task/actor.py b/chef/cookbooks/python/src/12/defining_an_actor_task/actor.py deleted file mode 100644 index 735a4f3..0000000 --- a/chef/cookbooks/python/src/12/defining_an_actor_task/actor.py +++ /dev/null @@ -1,75 +0,0 @@ -from queue import Queue -from threading import Thread, Event - -# Sentinel used for shutdown -class ActorExit(Exception): - pass - -class Actor: - def __init__(self): - self._mailbox = Queue() - - def send(self, msg): - ''' - Send a message to the actor - ''' - self._mailbox.put(msg) - - def recv(self): - ''' - Receive an incoming message - ''' - msg = self._mailbox.get() - if msg is ActorExit: - raise ActorExit() - return msg - - def close(self): - ''' - Close the actor, thus shutting it down - ''' - self.send(ActorExit) - - def start(self): - ''' - Start concurrent execution - ''' - self._terminated = Event() - t = Thread(target=self._bootstrap) - t.daemon = True - t.start() - - def _bootstrap(self): - try: - self.run() - except ActorExit: - pass - finally: - self._terminated.set() - - def join(self): - self._terminated.wait() - - def run(self): - ''' - Run method to be implemented by the user - ''' - while True: - msg = self.recv() - -# Sample ActorTask -class PrintActor(Actor): - def run(self): - while True: - msg = self.recv() - print("Got:", msg) - -if __name__ == '__main__': - # Sample use - p = PrintActor() - p.start() - p.send("Hello") - p.send("World") - p.close() - p.join() - diff --git a/chef/cookbooks/python/src/12/defining_an_actor_task/tagged.py b/chef/cookbooks/python/src/12/defining_an_actor_task/tagged.py deleted file mode 100644 index 85ca6bb..0000000 --- a/chef/cookbooks/python/src/12/defining_an_actor_task/tagged.py +++ /dev/null @@ -1,24 +0,0 @@ -from actor import Actor - -class TaggedActor(Actor): - def run(self): - while True: - tag, *payload = self.recv() - getattr(self,"do_"+tag)(*payload) - - # Methods correponding to different message tags - def do_A(self, x): - print("Running A", x) - - def do_B(self, x, y): - print("Running B", x, y) - -# Example -if __name__ == '__main__': - a = TaggedActor() - a.start() - a.send(('A', 1)) # Invokes do_A(1) - a.send(('B', 2, 3)) # Invokes do_B(2,3) - a.close() - a.join() - diff --git a/chef/cookbooks/python/src/12/defining_an_actor_task/worker.py b/chef/cookbooks/python/src/12/defining_an_actor_task/worker.py deleted file mode 100644 index 22981de..0000000 --- a/chef/cookbooks/python/src/12/defining_an_actor_task/worker.py +++ /dev/null @@ -1,36 +0,0 @@ -from actor import Actor -from threading import Event - -class Result: - def __init__(self): - self._evt = Event() - self._result = None - - def set_result(self, value): - self._result = value - self._evt.set() - - def result(self): - self._evt.wait() - return self._result - -class Worker(Actor): - def submit(self, func, *args, **kwargs): - r = Result() - self.send((func, args, kwargs, r)) - return r - - def run(self): - while True: - func, args, kwargs, r = self.recv() - r.set_result(func(*args, **kwargs)) - -# Example use -if __name__ == '__main__': - worker = Worker() - worker.start() - r = worker.submit(pow, 2, 3) - print(r.result()) - worker.close() - worker.join() - diff --git a/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example1.py b/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example1.py deleted file mode 100644 index d96cf30..0000000 --- a/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example1.py +++ /dev/null @@ -1,43 +0,0 @@ -from queue import Queue -from threading import Thread -import time - -_sentinel = object() - -# A thread that produces data -def producer(out_q): - n = 10 - while n > 0: - # Produce some data - out_q.put(n) - time.sleep(2) - n -= 1 - - - # Put the sentinel on the queue to indicate completion - out_q.put(_sentinel) - -# A thread that consumes data -def consumer(in_q): - while True: - # Get some data - data = in_q.get() - - # Check for termination - if data is _sentinel: - in_q.put(_sentinel) - break - - # Process the data - print('Got:', data) - print('Consumer shutting down') - -if __name__ == '__main__': - q = Queue() - t1 = Thread(target=consumer, args=(q,)) - t2 = Thread(target=producer, args=(q,)) - t1.start() - t2.start() - t1.join() - t2.join() - diff --git a/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example2.py b/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example2.py deleted file mode 100644 index 80fa230..0000000 --- a/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example2.py +++ /dev/null @@ -1,49 +0,0 @@ -import heapq -import threading -import time - -class PriorityQueue: - def __init__(self): - self._queue = [] - self._count = 0 - self._cv = threading.Condition() - def put(self, item, priority): - with self._cv: - heapq.heappush(self._queue, (-priority, self._count, item)) - self._count += 1 - self._cv.notify() - - def get(self): - with self._cv: - while len(self._queue) == 0: - self._cv.wait() - return heapq.heappop(self._queue)[-1] - -def producer(q): - print('Producing items') - q.put('C', 5) - q.put('A', 15) - q.put('B', 10) - q.put('D', 0) - q.put(None, -100) - -def consumer(q): - time.sleep(5) - print('Getting items') - while True: - item = q.get() - if item is None: - break - print('Got:', item) - print('Consumer done') - -if __name__ == '__main__': - q = PriorityQueue() - t1 = threading.Thread(target=producer, args=(q,)) - t2 = threading.Thread(target=consumer, args=(q,)) - t1.start() - t2.start() - t1.join() - t2.join() - - diff --git a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example1.py b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example1.py deleted file mode 100644 index 1b13c46..0000000 --- a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example1.py +++ /dev/null @@ -1,27 +0,0 @@ -from socket import AF_INET, SOCK_STREAM, socket -from concurrent.futures import ThreadPoolExecutor - -def echo_client(sock, client_addr): - ''' - Handle a client connection - ''' - print('Got connection from', client_addr) - while True: - msg = sock.recv(65536) - if not msg: - break - sock.sendall(msg) - print('Client closed connection') - sock.close() - -def echo_server(addr): - print('Echo server running at', addr) - pool = ThreadPoolExecutor(128) - sock = socket(AF_INET, SOCK_STREAM) - sock.bind(addr) - sock.listen(5) - while True: - client_sock, client_addr = sock.accept() - pool.submit(echo_client, client_sock, client_addr) - -echo_server(('',15000)) diff --git a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example2.py b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example2.py deleted file mode 100644 index 6ae0c94..0000000 --- a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example2.py +++ /dev/null @@ -1,36 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM -from threading import Thread -from queue import Queue - -def echo_client(q): - ''' - Handle a client connection - ''' - sock, client_addr = q.get() - print('Got connection from', client_addr) - while True: - msg = sock.recv(65536) - if not msg: - break - sock.sendall(msg) - print('Client closed connection') - sock.close() - -def echo_server(addr, nworkers): - print('Echo server running at', addr) - # Launch the client workers - q = Queue() - for n in range(nworkers): - t = Thread(target=echo_client, args=(q,)) - t.daemon = True - t.start() - - # Run the server - sock = socket(AF_INET, SOCK_STREAM) - sock.bind(addr) - sock.listen(5) - while True: - client_sock, client_addr = sock.accept() - q.put((client_sock, client_addr)) - -echo_server(('',15000), 128) diff --git a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example3.py b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example3.py deleted file mode 100644 index c9f76e8..0000000 --- a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example3.py +++ /dev/null @@ -1,16 +0,0 @@ -from concurrent.futures import ThreadPoolExecutor -import urllib.request - -def fetch_url(url): - u = urllib.request.urlopen(url) - data = u.read() - return data - -pool = ThreadPoolExecutor(10) -# Submit work to the pool -a = pool.submit(fetch_url, 'http://www.python.org') -b = pool.submit(fetch_url, 'http://www.pypy.org') - -# Get the results back -x = a.result() -y = b.result() diff --git a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example1.py b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example1.py deleted file mode 100644 index bb73197..0000000 --- a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example1.py +++ /dev/null @@ -1,23 +0,0 @@ -from threading import Thread, Event -import time - -# Code to execute in an independent thread -def countdown(n, started_evt): - print("countdown starting") - started_evt.set() - while n > 0: - print("T-minus", n) - n -= 1 - time.sleep(5) - -# Create the event object that will be used to signal startup -started_evt = Event() - -# Launch the thread and pass the startup event -print("Launching countdown") -t = Thread(target=countdown, args=(10,started_evt)) -t.start() - -# Wait for the thread to start -started_evt.wait() -print("countdown is running") diff --git a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example2.py b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example2.py deleted file mode 100644 index 3802f34..0000000 --- a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example2.py +++ /dev/null @@ -1,53 +0,0 @@ -import threading -import time - -class PeriodicTimer: - def __init__(self, interval): - self._interval = interval - self._flag = 0 - self._cv = threading.Condition() - - def start(self): - t = threading.Thread(target=self.run) - t.daemon = True - t.start() - - def run(self): - ''' - Run the timer and notify waiting threads after each interval - ''' - while True: - time.sleep(self._interval) - with self._cv: - self._flag ^= 1 - self._cv.notify_all() - - def wait_for_tick(self): - ''' - Wait for the next tick of the timer - ''' - with self._cv: - last_flag = self._flag - while last_flag == self._flag: - self._cv.wait() - -# Example use of the timer -ptimer = PeriodicTimer(5) -ptimer.start() - -# Two threads that synchronize on the timer -def countdown(nticks): - while nticks > 0: - ptimer.wait_for_tick() - print("T-minus", nticks) - nticks -= 1 - -def countup(last): - n = 0 - while n < last: - ptimer.wait_for_tick() - print("Counting", n) - n += 1 - -threading.Thread(target=countdown, args=(10,)).start() -threading.Thread(target=countup, args=(5,)).start() diff --git a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example3.py b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example3.py deleted file mode 100644 index de742ed..0000000 --- a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example3.py +++ /dev/null @@ -1,27 +0,0 @@ -import threading -import time - -# Worker thread -def worker(n, sema): - # Wait to be signalled - sema.acquire() - # Do some work - print("Working", n) - -# Create some threads -sema = threading.Semaphore(0) -nworkers = 10 -for n in range(nworkers): - t = threading.Thread(target=worker, args=(n, sema,)) - t.daemon=True - t.start() - -print('About to release first worker') -time.sleep(5) -sema.release() -time.sleep(1) -print('About to release second worker') -time.sleep(5) -sema.release() -time.sleep(1) -print('Goodbye') diff --git a/chef/cookbooks/python/src/12/how_to_lock_critical_sections/example1.py b/chef/cookbooks/python/src/12/how_to_lock_critical_sections/example1.py deleted file mode 100644 index fa57d04..0000000 --- a/chef/cookbooks/python/src/12/how_to_lock_critical_sections/example1.py +++ /dev/null @@ -1,45 +0,0 @@ -import threading - -class SharedCounter: - ''' - A counter object that can be shared by multiple threads. - ''' - def __init__(self, initial_value = 0): - self._value = initial_value - self._value_lock = threading.Lock() - - def incr(self,delta=1): - ''' - Increment the counter with locking - ''' - with self._value_lock: - self._value += delta - - def decr(self,delta=1): - ''' - Decrement the counter with locking - ''' - with self._value_lock: - self._value -= delta - -def test(c): - for n in range(1000000): - c.incr() - for n in range(1000000): - c.decr() - -if __name__ == '__main__': - c = SharedCounter() - t1 = threading.Thread(target=test, args=(c,)) - t2 = threading.Thread(target=test, args=(c,)) - t3 = threading.Thread(target=test, args=(c,)) - t1.start() - t2.start() - t3.start() - print('Running test') - t1.join() - t2.join() - t3.join() - - assert c._value == 0 - print('Looks good!') diff --git a/chef/cookbooks/python/src/12/how_to_start_and_stop_threads/example.py b/chef/cookbooks/python/src/12/how_to_start_and_stop_threads/example.py deleted file mode 100644 index a67b950..0000000 --- a/chef/cookbooks/python/src/12/how_to_start_and_stop_threads/example.py +++ /dev/null @@ -1,26 +0,0 @@ -from threading import Thread -import time - -class CountdownTask: - def __init__(self): - self._running = True - - def terminate(self): - self._running = False - - def run(self, n): - while self._running and n > 0: - print("T-minus", n) - n -= 1 - time.sleep(5) - -c = CountdownTask() -t = Thread(target=c.run, args=(10,)) -t.start() - -time.sleep(20) -print('About to terminate') -c.terminate() -t.join() -print('Terminated') - diff --git a/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange1.py b/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange1.py deleted file mode 100644 index 0f58cd7..0000000 --- a/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange1.py +++ /dev/null @@ -1,46 +0,0 @@ -from collections import defaultdict - -class Exchange: - def __init__(self): - self._subscribers = set() - - def attach(self, task): - self._subscribers.add(task) - - def detach(self, task): - self._subscribers.remove(task) - - def send(self, msg): - for subscriber in self._subscribers: - subscriber.send(msg) - -# Dictionary of all created exchanges -_exchanges = defaultdict(Exchange) - -# Return the Exchange instance associated with a given name -def get_exchange(name): - return _exchanges[name] - -if __name__ == '__main__': - # Example task (just for testing) - class Task: - def __init__(self, name): - self.name = name - def send(self, msg): - print('{} got: {!r}'.format(self.name, msg)) - - task_a = Task('A') - task_b = Task('B') - - exc = get_exchange('spam') - exc.attach(task_a) - exc.attach(task_b) - exc.send('msg1') - exc.send('msg2') - - exc.detach(task_a) - exc.detach(task_b) - exc.send('msg3') - - - diff --git a/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange2.py b/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange2.py deleted file mode 100644 index e530237..0000000 --- a/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange2.py +++ /dev/null @@ -1,55 +0,0 @@ -from contextlib import contextmanager -from collections import defaultdict - -class Exchange: - def __init__(self): - self._subscribers = set() - - def attach(self, task): - self._subscribers.add(task) - - def detach(self, task): - self._subscribers.remove(task) - - @contextmanager - def subscribe(self, *tasks): - for task in tasks: - self.attach(task) - try: - yield - finally: - for task in tasks: - self.detach(task) - - def send(self, msg): - for subscriber in self._subscribers: - subscriber.send(msg) - - -# Dictionary of all created exchanges -_exchanges = defaultdict(Exchange) - -# Return the Exchange instance associated with a given name -def get_exchange(name): - return _exchanges[name] - -# Example of using the subscribe() method -if __name__ == '__main__': - # Example task (just for testing) - class Task: - def __init__(self, name): - self.name = name - def send(self, msg): - print('{} got: {!r}'.format(self.name, msg)) - - task_a = Task('A') - task_b = Task('B') - - exc = get_exchange('spam') - with exc.subscribe(task_a, task_b): - exc.send('msg1') - exc.send('msg2') - - exc.send('msg3') - - diff --git a/chef/cookbooks/python/src/12/launching_a_daemon_process_on_unix/daemon.py b/chef/cookbooks/python/src/12/launching_a_daemon_process_on_unix/daemon.py deleted file mode 100644 index 62c1f86..0000000 --- a/chef/cookbooks/python/src/12/launching_a_daemon_process_on_unix/daemon.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python3 -# daemon.py - -import os -import sys -import atexit -import signal - -def daemonize(pidfile, *, stdin='/dev/null', - stdout='/dev/null', - stderr='/dev/null'): - - if os.path.exists(pidfile): - raise RuntimeError('Already running') - - # First fork (detaches from parent) - try: - if os.fork() > 0: - raise SystemExit(0) # Parent exit - except OSError as e: - raise RuntimeError('fork #1 failed.') - - os.chdir('/') - os.umask(0) - os.setsid() - # Second fork (relinquish session leadership) - try: - if os.fork() > 0: - raise SystemExit(0) - except OSError as e: - raise RuntimeError('fork #2 failed.') - - # Flush I/O buffers - sys.stdout.flush() - sys.stderr.flush() - - # Replace file descriptors for stdin, stdout, and stderr - with open(stdin, 'rb', 0) as f: - os.dup2(f.fileno(), sys.stdin.fileno()) - with open(stdout, 'ab', 0) as f: - os.dup2(f.fileno(), sys.stdout.fileno()) - with open(stderr, 'ab', 0) as f: - os.dup2(f.fileno(), sys.stderr.fileno()) - - # Write the PID file - with open(pidfile,'w') as f: - print(os.getpid(),file=f) - - # Arrange to have the PID file removed on exit/signal - atexit.register(lambda: os.remove(pidfile)) - - # Signal handler for termination (required) - def sigterm_handler(signo, frame): - raise SystemExit(1) - - signal.signal(signal.SIGTERM, sigterm_handler) - -def main(): - import time - sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid())) - while True: - sys.stdout.write('Daemon Alive! {}\n'.format(time.ctime())) - time.sleep(10) - -if __name__ == '__main__': - PIDFILE = '/tmp/daemon.pid' - - if len(sys.argv) != 2: - print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr) - raise SystemExit(1) - - if sys.argv[1] == 'start': - try: - daemonize(PIDFILE, - stdout='/tmp/daemon.log', - stderr='/tmp/dameon.log') - except RuntimeError as e: - print(e, file=sys.stderr) - raise SystemExit(1) - - main() - - elif sys.argv[1] == 'stop': - if os.path.exists(PIDFILE): - with open(PIDFILE) as f: - os.kill(int(f.read()), signal.SIGTERM) - else: - print('Not running', file=sys.stderr) - raise SystemExit(1) - - else: - print('Unknown command {!r}'.format(sys.argv[1]), file=sys.stderr) - raise SystemExit(1) - diff --git a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/deadlock.py b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/deadlock.py deleted file mode 100644 index 394a263..0000000 --- a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/deadlock.py +++ /dev/null @@ -1,28 +0,0 @@ -import threading -from contextlib import contextmanager - -# Thread-local state to stored information on locks already acquired -_local = threading.local() - -@contextmanager -def acquire(*locks): - # Sort locks by object identifier - locks = sorted(locks, key=lambda x: id(x)) - - # Make sure lock order of previously acquired locks is not violated - acquired = getattr(_local, 'acquired',[]) - if acquired and max(id(lock) for lock in acquired) >= id(locks[0]): - raise RuntimeError('Lock Order Violation') - - # Acquire all of the locks - acquired.extend(locks) - _local.acquired = acquired - try: - for lock in locks: - lock.acquire() - yield - finally: - # Release locks in reverse order of acquisition - for lock in reversed(locks): - lock.release() - del acquired[-len(locks):] diff --git a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example1.py b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example1.py deleted file mode 100644 index dedbbf7..0000000 --- a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example1.py +++ /dev/null @@ -1,29 +0,0 @@ -import threading -from deadlock import acquire - -x_lock = threading.Lock() -y_lock = threading.Lock() - -def thread_1(): - while True: - with acquire(x_lock, y_lock): - print("Thread-1") - -def thread_2(): - while True: - with acquire(y_lock, x_lock): - print("Thread-2") - -input('This program runs forever. Press [return] to start, Ctrl-C to exit') - -t1 = threading.Thread(target=thread_1) -t1.daemon = True -t1.start() - -t2 = threading.Thread(target=thread_2) -t2.daemon = True -t2.start() - -import time -while True: - time.sleep(1) diff --git a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example2.py b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example2.py deleted file mode 100644 index adfd222..0000000 --- a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example2.py +++ /dev/null @@ -1,34 +0,0 @@ -import threading -import time -from deadlock import acquire - - -x_lock = threading.Lock() -y_lock = threading.Lock() - -def thread_1(): - while True: - with acquire(x_lock): - with acquire(y_lock): - print("Thread-1") - time.sleep(1) - -def thread_2(): - while True: - with acquire(y_lock): - with acquire(x_lock): - print("Thread-2") - time.sleep(1) - -input('This program crashes with an exception. Press [return] to start') - -t1 = threading.Thread(target=thread_1) -t1.daemon = True -t1.start() - -t2 = threading.Thread(target=thread_2) -t2.daemon = True -t2.start() - -time.sleep(5) - diff --git a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example3.py b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example3.py deleted file mode 100644 index 12e2815..0000000 --- a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example3.py +++ /dev/null @@ -1,26 +0,0 @@ -import threading -from deadlock import acquire - -# The philosopher thread -def philosopher(left, right): - while True: - with acquire(left,right): - print(threading.currentThread(), 'eating') - -# The chopsticks (represented by locks) -NSTICKS = 5 -chopsticks = [threading.Lock() for n in range(NSTICKS)] - -# Create all of the philosophers -for n in range(NSTICKS): - t = threading.Thread(target=philosopher, - args=(chopsticks[n],chopsticks[(n+1) % NSTICKS])) - t.daemon = True - t.start() - -import time -while True: - time.sleep(1) - - - diff --git a/chef/cookbooks/python/src/12/polling_multiple_thread_queues/pqueue.py b/chef/cookbooks/python/src/12/polling_multiple_thread_queues/pqueue.py deleted file mode 100644 index 47edd1f..0000000 --- a/chef/cookbooks/python/src/12/polling_multiple_thread_queues/pqueue.py +++ /dev/null @@ -1,63 +0,0 @@ -import queue -import socket -import os - -class PollableQueue(queue.Queue): - def __init__(self): - super().__init__() - # Create a pair of connected sockets - if os.name == 'posix': - self._putsocket, self._getsocket = socket.socketpair() - else: - # Compatibility on non-POSIX systems - server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server.bind(('127.0.0.1', 0)) - server.listen(1) - self._putsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self._putsocket.connect(server.getsockname()) - self._getsocket, _ = server.accept() - server.close() - - def fileno(self): - return self._getsocket.fileno() - - def put(self, item): - super().put(item) - self._putsocket.send(b'x') - - def get(self): - self._getsocket.recv(1) - return super().get() - -# Example code that performs polling: - -if __name__ == '__main__': - import select - import threading - import time - - def consumer(queues): - ''' - Consumer that reads data on multiple queues simultaneously - ''' - while True: - can_read, _, _ = select.select(queues,[],[]) - for r in can_read: - item = r.get() - print('Got:', item) - - q1 = PollableQueue() - q2 = PollableQueue() - q3 = PollableQueue() - t = threading.Thread(target=consumer, args=([q1,q2,q3],)) - t.daemon = True - t.start() - - # Feed data to the queues - q1.put(1) - q2.put(10) - q3.put('hello') - q2.put(15) - - # Give thread time to run - time.sleep(1) diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots.py b/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots.py deleted file mode 100644 index 1dcd60c..0000000 --- a/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots.py +++ /dev/null @@ -1,37 +0,0 @@ -# findrobots.py - -import gzip -import io -import glob - -def find_robots(filename): - ''' - Find all of the hosts that access robots.txt in a single log file - ''' - robots = set() - with gzip.open(filename) as f: - for line in io.TextIOWrapper(f,encoding='ascii'): - fields = line.split() - if fields[6] == '/robots.txt': - robots.add(fields[0]) - return robots - -def find_all_robots(logdir): - ''' - Find all hosts across and entire sequence of files - ''' - files = glob.glob(logdir+"/*.log.gz") - all_robots = set() - for robots in map(find_robots, files): - all_robots.update(robots) - return all_robots - -if __name__ == '__main__': - import time - start = time.time() - robots = find_all_robots("logs") - end = time.time() - for ipaddr in robots: - print(ipaddr) - print('Took {:f} seconds'.format(end-start)) - diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots_par.py b/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots_par.py deleted file mode 100644 index 3e7a588..0000000 --- a/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots_par.py +++ /dev/null @@ -1,38 +0,0 @@ -# findrobots.py - -import gzip -import io -import glob -from concurrent import futures - -def find_robots(filename): - ''' - Find all of the hosts that access robots.txt in a single log file - ''' - robots = set() - with gzip.open(filename) as f: - for line in io.TextIOWrapper(f,encoding='ascii'): - fields = line.split() - if fields[6] == '/robots.txt': - robots.add(fields[0]) - return robots - -def find_all_robots(logdir): - ''' - Find all hosts across and entire sequence of files - ''' - files = glob.glob(logdir+"/*.log.gz") - all_robots = set() - with futures.ProcessPoolExecutor() as pool: - for robots in pool.map(find_robots, files): - all_robots.update(robots) - return all_robots - -if __name__ == '__main__': - import time - start = time.time() - robots = find_all_robots("logs") - end = time.time() - for ipaddr in robots: - print(ipaddr) - print('Took {:f} seconds'.format(end-start)) diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121217.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121217.log.gz deleted file mode 100644 index 8dd67b8bc383f10fc248360696f7ea7ebebbdc94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139539 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

F*=jP%3_<@eD)k&g0^(7uZ%)>jHsWnf& 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

T=p! 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 z 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 diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121220.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121220.log.gz deleted file mode 100644 index 3578581bc92f5ff0148bca2e3e49f9b3dd9da20b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148719 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 diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121221.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121221.log.gz deleted file mode 100644 index 72dbf6cd22960602819c7fa7e7af0d850c7039ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137687 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 diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121222.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121222.log.gz deleted file mode 100644 index 15f3ed0c1037a7b44946bb9a5eab3bf162006c19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113107 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

7qQ?0z}=xLlMhkdLd)kYTPe*O!y$G0wV0QQzU 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 G;r$PqeTvNh diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121223.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121223.log.gz deleted file mode 100644 index 6ffc86dd1306752d16f870a3e3c7655e79f97ce8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115180 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%0DUymr3F=s1q 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 BuowUU diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121224.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121224.log.gz deleted file mode 100644 index bf68125fead25213f369fe7b87bc0a56464ffa5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119009 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

2Fu8~$zy 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 hY?ML+Dc#i(CU$OMb5>NR2`=T#U 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 bG4HzG{)Nbw{Licg*SE}4N_vr{r1$;@S~s?` diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121225.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121225.log.gz deleted file mode 100644 index 134c58d1a1b5758b5c80140e31e798ab3252b743..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 121461 zcmYhCWmw$o*0ynXcXt`4xDK&QKylX=*FteCu0@6xcZV_sio1JpifaY^(r54I z+wYHcJ{AKbZ(i5MZtFtwqldG*KpQtdOpa>to7{8zhAphCK z4}Qk~jGtc!1gs&fb_FJ~3?%RU2@b}x#3ymV*VWm)phG2yPQ)X7r6Vmq%|l3(QUb zJlzGy-rnBLUA7OJx~J}ov~>){)>L1zO*}0aJe>^CKVHt=oNo5s7zE#rmxlwhGx~k&<@Z%IQ0jLFtm;b#G&kP1 zZ8lVpXAcRB;2;S>?f+sLS|);;(PE@yW18%3@50=O!m8nfq2w}+r?9H;iUtU*xYb6& zMr-|7`FZ--cU8_onfWi4wskd3kuH zG7##;aC`dETS1cC#&xDM{(Xn1mNElXk6)`~KUY>xE|O?B=#_isO+(&xSF(q`BhIc! zM?7bm?>n@{H+4V)Ea(j;0>Ui5tF(peho!iv7Pyddtb49Df! zn{s`tP^|261YGFyBMx-=9*A2*w=$7#Pmv74E^$l0E>t0E8Ivomj3m7BX%IqT)RU7c^! zZ|AO67yc*u;tez5Esipx8mPnist$2zvyvuheL~jM{HJIclVVtTcJ@0Chm_5utg7m7 z%w^)9rcdVIrNjofyC&2h9^0hFkj8jP4|vUEMyg~u7H$dUTX+=A$8ToQ7PjSJ^`pxm zyc+fnc>l@I3(*p;0@<7z$hy}_EbO8)$?yi-tDw~ipO$u?m%JvO&%8?i4d1{(gw~Jo z@w5rgm(fCBx;c|PI52UC-Adsh1+`&{lpqwIVK+vopX=uK7b$MyprU+*V90V#WT~_{ z=qTQ!twdya*JGU{L*2I+;u5|T(U7a~I2)dphHRNF>=XirsnUzKopFsPr^+J$oHes8Gf z`-8*?+>$83ElGf3*eJu&bE4_uv@759db!BHUaErl=!L&-oOtW0LC#!|`|2saL85E* zLQcoroxQNsQ~AlEegyp-!nE?vK0cGX<@e;5e6UhkSZs(QEC;RZ=s@uw85^=ho2X7c z)iNdT?lB-ggCjvlGJ5(JIM8ib_;CU{>XmO2lltzm_@7K9Ud;k@M0fxl3d)r)0m10L ztW52971$uLtt1^!ymO=AcNH{b?1a}~VC#QH)!UCbZSoJ4uInD{ryjJ2EX(>Yl#85& zcv+U6$dcDTUVP|IPRjz+C(gOSN? z>}qs>O{3?*Ux=6(%hR=@et$= z=_~Zk-=8R>Wu=qh<7=FsW;lBnqPUKY7~4sXMCQwf!b2gk#HRMjY$F+Z41(UtCv(leud$a}Z znD&yiC_Cz(-`!%vMy-ySEJKGc@uK0;o!J>WdG+3%(qVzn{y;!fb!w;Rg38I^; zWLTD)k&^9JuoQ}%xwVstP`|VK{?>Y3F8lH6{$+uWSe6vq>yCBl^zVssHP*xvnr&Rm zL~VXw-gfvK_Ae?fWuz!iukQRQrBfC7%$FubwEm}M{Z)QLVr-O1ROZatl`;PoZ(xdBS=7@C{-DE}^N_-AD2_fqU`X$)&u9S8P?$4ud<73NmxLI9zIv zWs#g_AQx3dfuRdUc^X1HlC8=O&}r9?fG@wDZ!XXMI>cW;wRO-_Ywv46f0*%q+V~3L zd`f)olG@)W6{ke@SX_BjS<@h#AIp22<8c4g;LeusZriS}xrT9lMj)`o{O;`X@y5Y@ zpU>?}kkFP$X>e^l_BmQwfg}?Bd;O1d>%oT?HTAEK^BM)+KfOj}M`op@5S6VBOO;I_ z6l70-esd_Xe;4Ih7oYlE$kLj7Z!(??r8v%LppXDhtI z9wb$jwYN2sZD0MdWi`BsmCbTFu4`9tanj^PIPSlqO&r-Wp%{IEOILfc(HjlC2=+}y zqh@oRhW74gQaMqyQ1sk_K$N&*$KgGZ?y-W4V3%Yx>TkTjerQvDl2+Kl^eikS3^#nv zuGXTK3Rf6MzZkBnOYl1Gxo@WsZE114y?FVBgc-p&%gffEN?NnLb{>lXU7rNW&lF4B z-tuvc_517CgR0H7(=Tyrh+w!5+Q5p8r~3m^cI*-PBt&PKx$b%TWF;0Mu9`msG&~;@ z_zsTWDfsi?Q-lC3{qz3yObh)1a{hO|=Icx6N*BchGzO@bcJ76V6)d)sbmA3_o1W{H zrI>=tud)dy$y0<&reM=3RL)$QZxbSw=~?&@cdtLFZ$+{S+6Bt@Fu6vDDB1_2@TeUz zLpyy%-O}s>Ii0%qEV5%o@Mj>MzL|SlW4(eR_9%*?vcFDYKt^bku%Ez<5kc)u=*J_@ zAs?IjT8Oi*zABG&F4NIOLbcKPgk`F^siED_h(7*X2UOCavzgQ4${Rin>BLUo+cK>M zu0>dBTV%A9bzmyDR#~5*GZQ*62Dg*YUIlt8r_2NSa82I>KYoT?E9(Q7FNSmv^z=my z_wr!p?Jp)Y26raG`!n@z_09_387L3GFVB50&fS^@eR7_4d!8zaB5>V}zFXf`&@mI! zI}E7yI{#w%*^Bbc&bzGZ&N#91`2;fYVU=RR4@J|2TQgtQ?a)`&PAs=KjQj9n>OYxs zclNwRD?U~z9XeEaS)MN#UCYiTmwBszx>L{2#gHMXgV=T)jWtMm3??2k&9BH&W{etJ zh*tKzc=0?t_RZE5q?0&tkBWSAi0*1Au*;O@o=fcWCwiZ-z-^gZni0*)a@I8s4}J?u z!xrIuGit_YJz)oL^ke%xdrDm4_nA}w5rOt&I~aG{8O=tt8_vI6E$2;qyiHXv zw&TW$AJ6xCd;J<>6kjwQ6|lpIDXTdlA2QP?3;P6-UMxTR>9soz9UO~ES;H{=aIDl* z@4S=8YfSap3}dk{g{((LzXwbBQ^^!`1=B>p^Rl%K0)(NKXnZqkqG}_ruSU_B z4WSs}3|%MN=JJjgf^hWmqxqS2T6!kTr9%L^kV2NFvL<03PbIfuu#doTD zjtXN|1e){v8K_m101}z=d%{{f`M3~w>(!n)JP_&E#tMSr{xo;s=@X~!>23|lR&UQ~ z9=rvggX)G3y@_5qWOu3w{25`Ce1nPWVTfV4Qwz*8Ef}H6y>dN8uU(2-3o{apfrLg^ zC0rthlL4Ci-w2Ph{G4%3Hghe6z_7?rtP+mPA0zmWa7Of$zAE|j=#g5a(&$XDSmb=i zLFBxmoc#BlpITJ%TdZ^@|H`JNT|I5cU$B;KY09QTex6l`YKT$d3k(LEH)y*hQBTn2 z3jk-C{(Qy)e6 zb?hsekx(^FsBy#FmhC7;=ahP()xz0`$Tu%!&&zSu{H)U{$adgX?}jI=9KVrG`6x14 zC8*m{JQE{)S*rD>dCtq81>CNyAfJLvV3=onnsL0k~Jy3f-=ogIa0+{j$k_5~oflu}a2$Ic;T_^rd) zc7x(^^-=Yp1f!r-%UK5fV{we;7tcIm$?umi8d9~$p~tCbTE0tIXWqFCzU>PI!cp5> zo0}Q<0}E0F_g{klxLcBX8)a9Yul;nni71dt<>wn2j(7^XbGfQDU1jClEaxYuQr#kY zx@c9EE-3ejk1*3LH*cvitIE(py_+AB%5``t{>N)zq`y~K_|K=CNpeLyQb+kr3O0oP zp&OR?j!tv)wQ7-tvw7F~rPIw7^Np&rr`F$hH#IfncXMkKM=KAOU(DiZU+u;y`N__G zVx;t}yvX}axbbvp+tnkwTy<`ZqzmW1#9=FJ)h1j8*Y;mZ9a#qwg zf`geEHCW$6T&u9+=R@#%2)|xEgG%)(&#W{W33k%%4)bLo~4oOwn=y9=7L6=x6zsXdo702a`@;*QX1c3~``Gsw;_sA^gLk zn<+YYaS1$ht;SXUR5qe6NNop$mI<*5JbSHw1vgi3?A$Nh??W)^szI6=n~K4m9yi53 zOeY>jWk9f@+w9l0_O<=QtUR_So@t~*L~FP*&<0Bc1CQOm!X!B>J8B=^@0%0mytkDJ z18K;@&kJSoM$=0Rp>M+&(IZVx(A_HI)A>;&BSl2NLR;tZk$bCt(oncG?e{?J)pnhE z{KPlmrJZXU>NDxE9%!{7Dw=00CpeQ^GZfycb}lo2@>7~TO}YQHSixtE@M{Pg!h7xm z#E{l7vcCc$q&1Rw9#PVY^Jk{WHe4b~n5NUyuJ>QjU<4^Akx*sNOQaAn=M+}{re3G@ zI^Zrm_2Q*d0?x$A&+lsMT2-a8_Jlw-6k7uSCdi2txu5O{%{lt)vKoL4d(j!&SmJKC z67x6vCjzDeDu1W12O*r4X5AP1S9{IvBXL68llm3m!ryIxAAQWAuqq+Cx8gjTuy>kY zc@_y84W=vvo=nh3N`qP2rkz(2k6jUi|J@xu!US#as=__=v~8%3ZSn5FkNm`Uj~ zof2}2U5>^*{-M~ol#L|@UVSwfx(WMAyx3e8)nlJH(*~a9afGA~Q_p28I9R4t?p9eE zQ+?q1`(7YKifno#2CnQH32j?!J80mys2$S;xxAl-Hzo^=DVF(uhk#s^+8~Q@9?m&l ziTOfSzvDz)Pa_ppJVaQQ(>oZanpfkM&AF_8)l&;O#C)CsIRx(31!Z_GgbO`# z_oWz^Cu2BtW{yi!-|I2;YD&RU!05vrq^ zMr_bloc?w85n-n&n~R2g?$u`NCm7biZd~E7#NmFwb1i7sbU_?U2$_OM`?j|SsZ<*0 zA5nO49+Ic(^>%bR8!#v zG6XV@1)vTNL5u7cSe3N3tgz1+s3fD+*Z^>&05}$ZA=Hrg4n--_9cKT{6d5T}s16v4 z=&?`>O(#4a%JCe*I(JI;Ba#SklNLf-%4R2q0e>#}rds3`6GRT7b~G}2_oJ5(rSAvk zD7VF`^rQaX8}s!_k%2j>m9_YO$HR4R--jQ~p7hHsFVwaUn@f^a?H#xQ7M}GHIzJAi zoOVFUdC7vS888eyW4Q5=$f}+LH1CoDIyD>~k_wx=$?}xXU17mfyUo1bqbwU;Gf*`8 zSkkH)4J}^9G*u9l)_HkQ>(dAStJ_r0!Au_o(x!R1+EnPN>zM8x=#hgmy4&HFa? z_hsCsAXl-;X@-sX5XEk4#Ml54B(guJg1_Z>BPXUBL>cS?5-FX%WhOD`1(pv(#&?2j zM6lf)7Qr6RN`}I#^=VGo9A17nCqV@}`kVop2Kz&x3dR#ZRxF42)(nIjCH){i) znl9o$erpJM2IMGi7*Ko|`T{}tQSf@{ZvRKURQ}4Fu@aar+qZ=9r`p!Lpo^7ZMXzR4 z^H-YMI(Szgnkk3l_Mt(KO&Uq+`+p~w@1JLli!nc(*LIuGhJk(`WzuzUA1%Lqhrj-b z-nIBQ|Cz%P{@WJO>Ypv^Q!mV&mxSk|g7>S~Yy_=Se<@vGTDXhmt~+Zyc5xrAI*9$9 zZ#H1E8_&3w^EfPokrzt#ux5rOe#xOZgLMrnOM1{`pL1vx4JDBJW4%s$>$mZ{%s5Z( zd*8LtL@q{RkZAY1%kYg)XUQwmEcLAT!;_KBZM)lZRqV5i_LGp5dyNe zch1D-=huaIJO|nLheZUsdY-&7vcnA+bswX?g}2Bte#N>x$iYGOlf2v^-X647+uy26 zB+Xr|G=dLFR{joThW>U+(TMdPP8cO0O4a{gW-G+NGrY3dj$mTnu7^pI1tCLRKG|7kLcc z@D)71GmoOUyIm*ha?C@W#^f{Alm;#^$J?FY2f! z_@Mjo0pI*-GpbDbem(F({=wTkU@h?14gSeO;EngwKuz$c?#JGEkt_^R!uBbOgMGLFp6KE6pJwK|V z3g;J*cMn^|7X#jH3X=2T;6vo$puv9ZUi)f`=@9hTih$RG(7L)X+*CAxSWP3~3Tsdm z!hd+IB?YXJIQZ1JcD2wLDnzLYyJ5+)@BD`n*K5}*pF+E+g+t|pn>_lOFYd0-S}KRpgV@_RdQ$*0SwPkwi=4Y-A5V+ zeb*X>S&2eEH_F`q8_6&Y{pF;W4HUt1>jij`p#Ye8Uq%aN#*0rEe|T=_kH-l6yp2cV zL=mJIj@w%UjQ(#@fF(NOLth^kMWFT5VJw0xB}4f5EA!B(Bb#QRy)=yoN@^|tDKA^# z0dR9vwiq5MW``Iz@xw*VjJIy?^9QwYEUKiBJw0JKsAy!bIl#3H@}Z&5QOI+hR%!WH zdy)c7Q_TX!SX>?T#m=@$e-q^IhNYep>EM0iYM#pra{GvIrdvx zTP3F)CKRiB#g@}!x$t==6dvPPp!&-T zB)Lr(h`q)~h!w{k#Py9+p!e^Z=IwbZn_gI1RC!a4(9ZtR8z? zKt)9-1io5?5RQwqVqLs`7h``IE>7Y^un2>rg*sFHBUE)iJ#gvjf3%V|3Y%YTo%$KE zd@tA2_h~ew@~GYMvf=XGhY+V8u0*^(!NO$}WNMaR;9+r@0X!^%?ZGQ2nYMaNQVz2X znWf+=#zIK*3gJ%uS^`7Hi4RH}(b~SkS6DJOBd3K*zr@$HSDnld)qlm*o-iS|NNU|^ z(O4A#j9dU&^-ve!Vn%iiphXPXrikizjsY1_LMCmp6J)^OK(yyFX7{{0-OQBUz8C zb*C2KOKT-LvSPbW>X2=xARSBIV;7o2Ii66&Dlq~BekUv-*V1H!4ApW*IC`qDDHy<; zjU~6oI*frN!$Gkq6>SRF)5TPu^(8@`rAx^}(b>N!A##*jUhc<=|UQ=cn%b{5^i|8w!y z>E&)}yY>(UQ02ZclB?okNSiBsnb~}bzUyHv&5==YE87N{!XFmONq^y`g)#RdJP1Ui zGRl5F_!9E0ri8JSj==EcAz~=&zoIR>pW#G3L@&IOM6l{C44!)h#4jN3!r%($5bMCK z8%7lMaOOJ>qZbV$G2%GZl?|ZHZ>tJ=#}~EBl^gyVPalXEPg;I0xVGGFKWqgF%_&#h z>XRtnst;k;&2<|6LIA3J=!H`-Nf{aQgWPPToaVpj=l*epoR0M8y)j4c{{HbCyT-9Y zmH$S}!Lyw>efEOD%DpjX@5yT`fn^8p$@_m6);mdBmfo7%$U0gza+~g9QnX56Af3kD z0ZqnhT{QSc<676_Y2j|Zw6i8*_pULJgzog&ojn9+$cD(D*sp({tXnt6p9IV{%=JVR zA*RTDLF3|Y(tQjN^7 zH4D^!=r?&}P+jjs%&hhD=Av^d*-cV+A8rqQy4r$Tf*QtstQXg&D)DkOFvEqI0VnS(R{hP>APdm}4!^9c(#-xR{&j!-)RM_cQo&6!#c5Z#A zc!PfDEBkmu`l_h}XLZ2ii1{BO!Fta(DZd1*O*Up4`IlbM(9?lk%P82C{r9(M?E?@} zZ4(AW_;-PNde5i!p=TT?%7L>6uL8;BM)*UI*`*&?P=W(RT{2wL@nd@#50NUWFJ0QP z;qr6=-XF|5asqZrK7&*vT9*9Z(yqbb7*1+d0_YW z$6CdE*zM3}1y8xNFl?Yp}OlXjt&Bk(Yz<~6=BlZg(<|8?U3e5 zH+?27>?p>rUoPWM??uN3qNkbkDK$wMy=2{4UP;^x!6&@A>F3sNo!v7DsKWaCGDIXa z^)A@u%Z2seVBDAbws17F$C`lp%-9$6sWSoSCC!e;8Z-Hoz0kLh{OFPUe4c=~#Ei=*Wo!C`?mzeQ zW3;_7;4G>269p#Ay}&$`Zah?RjI1uDnAq@;v{CTW5)xu;HZMW#zrxKAmzYN!Bwpq9 z#jk0(MtEfIZ7=%^mfXoJHue`9YpS7fpVi*FjXl%ax+3P(qdiE|#qm38c2TCQtyua5Hg@%HR`Fl?- z1tNNSW|;bNreUp~y1WJ=lKTINtA{Z6ylDE8zz9;{VxG)ridY4t%qYxKor`Ck7Iy%-Q}csy`0wMUb_;sPGkNHgmU=KO&71 zZv02gVaB5*Iy6?_3_p9@3NQ5}$=GSa|WYPJ}J{Lb0LA#k$G;^7& zP~_fheBk&4zV|fGRla1ApY8Mw|C{%C0C~?KK-L(d6wJT!u*l*G5C=hHQS}tTUQC4J zis-*RAzmTU1OJFAt`G0s52J_k>_}543-vG%WAzz<+~?gi6k6tt>iM~4wnys8z%!NG zTCWuByoI}4G(!qp0n=oF%8`myJCE6uyrXtl@BD}n-TaP;Wtl7{$FiY%G?#Q1?g$bzN5-fZ!+tWj!_kHg}Ma;KXX-K)o-LKETWwWWYR9@KB2jOklLM z+`cz*UR(Qw;RlyZdR$-%X}RD521lS&<}&iq=Ldhm9)#X9eE?h4D06Dn%u^ z)ER1V?zC69_Vp{&r%vU4TVCsMAB+TM#w7zP?;LQ~*_BL)8HQ4W*54wjKh8LtUxJ z*$5~Lj-)jG4$Z?cA9M01t!Ec52JCW_OG0?gP2xz-P3?=3D#MuhXiAk9BWc3=ZCzDI8q=l6%k ztVM=iFMSgzNMi0V?l$8i|Fv?oGI!CsoQSTTma6;Vx{GPIkci6bG!2b0vhTwxn zx9IjSkKZ^ul==j#3_{>xvSk#&W)fs+ocvZ9?tXFDS{KMB1P8!SqF6ZR>U&miaLu`HX~%Z1+RLz)Jp=ZKrwXnlt@m&Tg1Rl+zMr6; ztWZYnoc`$prc9|lrjcM#3Z!20n7rm=<-Pu-!m)&Uv?8nRg@^=AZv*oI9@BGF6=99*!lwG~* z++1*y-xkHRt1W&a{=m(i6c^0QEB*r{v1sXL&*es{dUAc&(ypY44fC(dc6H|KuW8W2c2ZEY~FclNMh(DC5V7TB>ur~hWj#gktB z;}FPj2=sO-HlWV@suXl>PEkGFOXizOsz|$cDTIkWsR@}kdUl^!@R)gWY8L;cv3q3X z1%KSKcPg`bdz{$Y<$8SLy9NbGwPY>Wu|}Z9;}Cg1^7fx9jPv7J9}lwiUwUruK%KP~ zY4PMUzU6N-A~)@jMW5C%OC+SFUP`JwOY%go8hwB019jc-1is#|?`Q?b_jlOnOY#QUe>n|yKl)~Gi}QBg;8cXRwYR`hs( z4rfvax9NIx`yWzejl`ZWo@?uhENQu<#Bf9?D1J&Hw9>iGV2kf><5S0?Rk6;WgUfB< z)s3>x@O?hZEQrx-SB+rd!b+YjEFwU0%Cu4n$MqZq7)62+0`sMQpf>c^Ik--!Th#CJ z_lSi$VhXVU2qVX}6LYM8AZHR1&>6i?cF;yYo~EMGR~*r!M-@r}I~|6#vijY}dR}a< z36j&VYkInSuhnb6a7yn*U=C?*Z3m6@&v^s(hiY|CR`Kj;)k;>DM&9Dj;WRhjWeC2*q1 z1c(K)F7QDp7p^b{Z-(E4_cL5028LQ$yDT*7h;(4e{#W?DACUt!R+f$}%4>w8IFnk;~SF%kobo<87{}tD) zqn7L(izFW;QExw$ya)M2*Z z=$|kQJVIjJSpOAOAdPjkn!T~EdfC*Q2)mN1^|Os(6iA*WuUPRZ)o@Xrdc` zjoSD)d!0b60&x$7{>PmFj6~ivF+)bjW-&TJvixmXtIdCf;&vdhN%J8rMTyaviX@Q< zu=Rlfj}Z-Ud!_c;0ZkLbqOIw+o7RFo)P`^-U74PLBl6=09Z&xIkAO8gBm`If3ve|K z0JMkm00aI^D= zoA~0H{W2EA{WC}NS9@RoJfLH1u>A%2Wf+hap>bEu)1B7yb%mjPVa`)V( zv5G82VZ{Ma1rUYt`J=}uO$ZiluTAdozrCYmRQ21zS$v|U)yQhm)KP2W!siPlN&YyR z&WFO4%U?WZKKO2LVcPDB+~YG-xeq_J+3mYr*R&ZomNvjgKZFZZ_gxsEIv&V~u_@FT ziw^de$B}X9r6Qwt706*kG(g=LQI-==$k@DE*`Wokl`9;W6KI)0Yh|4Q@;2)O{GI{P zFeK<|`&q8`)aH3^swQkou#*$(??#|$V+Ls2(2$4Q>ryZsdn~4qSu+Gz3ep z2NB;wQx+y|TuTL;;6P#UJ5U&O<5+AS4}`z5F%gpW1Q$EQ@$HD@u1oz$RT>YDMUsb3 zcE?bRsTw5#4tWB=Q|CkgO8Q5Hpw0wB@(q)2?MO7l=$>hI81_KnUi3ITnWy2Qz&4U=(o1QB7J#grf2)Q5gO!77OKRd!~sjc{NS(!@_XIX8~3v57Zj~kty&_e4<=| z<8uoNr$%4`mdBFktE7?q;xNch8q%E@q;IX>dSdf-K$8q2GZGnU9MH7`hQYrnF{3yw zBk5DI`ZD5QVA(XFI3>KWJ?`DM=Pk*yyyvF^c&b33dX6tH>wJLD7Jc-1051bM zGuwNKmN`PZin(~}fPVe`dz)%}c5Tyif`Csj;zjb2ZNCo66;qu#F^tzS9SWoF!|yG8 zeCLIi7?QJ?^tZoXzQVs>7+Z4)76l2vIT{Nr5^d$osm4(Y1$B0C&8o1kn~HHdnuE-E z&AW}OPR$ac`XyCcy{lpHCmWEoXp#@uG)1t*;OJnK_k|*K!HW^x0)407*~I}m}_$BS{({`4M|48H#xr@ zo2OohBIzdrLc8O`<_sM1%3Ia7XOahC3`k`1g=xAS<3*pl4kxEK?`Hfyb>+`>2l8j( zMC;!NU(Iy3b6=iXEe)K5iP^dH#q-m9G~#x=svSjsq(aR!SA2K06Tdrin&rRDK&l#Q zXknI5NEMGdso3u5mjWrSw?i1d9)8|VMt0|2oDE?N4_|DHc>n~)rjH=^sC-EF~(>ma(IH-@O@!We zoIO6=(RW|q2jz2Tj#+ii7w$dVdww@R>F-%b9~;zawQSk8D&>;kNpVT>aNLDnTrc%L znfqzt#Lud2O@+p*HE0oZdf#hUEv)>(WZXZ8aCO9B<-r8IA$w@GBGdk{!PNm~?K@ic z&}w5=Y-e*D|HJw|r=_B$W!--%=;!Lu@82c6!Jx-3Gl7Yc81FLIRt3!?t^y!KulBw$S zo=%LDnLev7^mq%w!4SR;91GXxwMy-44abR0t4GLQd(D)eJLHRz`IyC$K$Wdkg8c=R zh~(&ngz}&({>J^lY~5kdrG~_cd(p+(>EX4|D0xpJpM}2!^Wu;RZp(eg?n=jE*J1vE zXtC;-ha_L#tIDe-z150#-jdVIHV{9jfbDkH(^+Zc*Bglhl zc_xffAt~G}R}@YOu`_Mdzr~YZqH3;0V24{+%i-a069bGXD+!rh>j*WHY~q|nDn4;{ zelwFjT|=5MO|#1~yIqD3RsDL3u~vbe7U>@`)QA#F8e%~vk4ZS{hdnuT!k>L@bKO}) zZmll!4(yG|F{s$TAPVB@{^qW8A(Y_wXYrdD${IXs2T5Q+JHb{~(Ri~XXOHPenN_kA?c76Np2wVU<#6&<$GI4JEwcTm8AZ1X zfr$xXdMtA80LhCF1#OvVqX$6Ijgl6a92X-aW7~K^q4!5TY@9}q2Zl%|L>uMz=O!L$ z?!ThXk4-}INvesB>|wEweY$%;OKRJc=-eP)JBR~{%KsHj0ruMgxz6u6*_K4PwT$i= z7=3g$ESD@a?s_9i@ewGoUd5;e!IRK{{U{b|$Yo?y*0KE9_Sw%GLP9J84e)S|I)Jg; zNJBPxKGPbHEOg%&itKRv@cfO?SCwINl*QpOk2h90-N66nJ^D#+lz5uJ8Ho7)8IDMz zAeQA7$;)LYVtX<0^W>i>-@>^lMs3KoYx{lU**Q5J3t0@3Z1DcDJrQe(AFFt{Zf^-H zDyBX9*wXOAKcbsz5#C54Ktl-h%)Kg%0EpsHcx8m59jjW{K!3c`OQ6xvmK8`t(q-UIW=NGS z738@+(|@{*mz@q#X(;sh0OaY12+(D0QScFIInQ#(QJc5dJo^v809ELi_^% zSQa8XLf6-1-Rr3M&3G*5*Wx<`YBs-uNtMIpxMtdvB;ehy@S4phOg^Ri%IoQknXNO^ zY8CtDY0cUEuV6DAA@U*B5k-@5FF!7L8}SR~Qu^q0G~OJ0uv@JaYHYeDz!`-J!Q4pc zZJ=c0@?E3ntQw|-*d9wUhDjs(eE^+eBHJ;3C|<>z?QF}?9HbhxJE}Kg^5+%+>dMJg z&99d}Z0)C-Q~tG(#}3xG*)_~DI)iN*mkyae?<|Eixl=9~)%u(V2T0sMyBqKm!CO46 z2bF7y{Y<)4aq@8Vwa-e2NfCX7x3;q3*x)MyzBjJ0u(sdj=QSVINp=NqJB2jmwMKEF z!mjaQOPxYkHdpf;hJ{~EccRa$@&pq-U_f7>CL$X4tJY_=GViF7{}ImdISGT!@1!Q) zUPVd)IP@04p$GCs&L1ZlrZyUKP|M%^{&65p`bQ(`SG`U&0ots+3`8~$4c zAEU9oAEiSTwdBIdB!Qv31~Snp`JN(0d_>Raaw(M;GQ#v$kSc(M{ z{)!(+O4TAmXG&M&_RbLKw3z*foTUM;PJ`KV$VN5U(_6#9w{5L=&UHrBr)E+Q*k7b7 z&-9~?%!40)+|hdk-vLeY1p{QST5#Fhk6k|&9K_-F@7(n=GP#&6crOHGLpD2uHA$I% z5+@Ul5B3XsYlMd_ap0gfgrZ~F6eE+2{^<}tA{uE~Cy3ywq6{${$B^N%MPf)7Kp+?q zY7$mFf5z!nO65^l_(P)yVK3X*0=zJb72XEZr*OjFyBh-At$=O;-`fsHbhGmtplmCq z26Qn@=Pzeuk1?e{2w9a2$b{)f*-c7shu9M175hZJ!APk~b|`EOn;054+k}};@7qnK zoNYpjPNm4a4Z8fBJL2&1Fn)!wz*z-3=tDvj{~!SE(CEOFL<#{B8_OV9J-b-HF#2N_ zmI1i7&I``as7g3$uIFz6!zKFSrlGec$`>W+w7 zG*3KwB0Hxia8rM-7_|dSN*;(0)?_U{VcysZJ$u*mb~MxE~8+ z@q>)JZnBWB8Sgo07OGOL=_^F#JX|qlr)w6klrx4jT7fyu(XT1AUdiKA@vwQD!DzH) zamraobTsiR<8zM1us7+qDjUs!qHM>Zp-+F>tg%nqa)hH9J{L$O-*U44n$-G+Ohb^D z{k2O=?8Dm4O7DY3)2|Fe>kj;4a!8ZjRgqg>FxKPHE&5a+bIn)H>e`KUm&B;!-;bhx ztyRQC1JfUSi*F(x#m|5~>Tr>`in8F`xZT5`2e+n`8oY`zcS5$Sf%Pg!kEey$aq0Ti zGcJ!agO#^zf~#TALuqdX9-bEWjRL@?g`e5XFwV5ITMxdnRMZ-_m&C~FC`{n&UzpHI z8F3@hf8SChe`Hr%mlflzg>A@!XRGOg^zS`+%ZJ1|-l}0C*#^ii(vvp5L3{N6aMAz6 zax!^00CXTxu@^gg*#^Ktye{u0z3iIdBIiDGO52bUa(7MB#Ds&#*g|={$auKhOHiB+1t_KDkc`LfPJ(pOg^N#vhrMh)@u;vrj-6pEXsE5j= zX>DGAv0r<`$C%Ld+2cUQF!i4F@>O<=9lLMFo4XtLuC_$6xn}n+46$-YvoEHrce}gM z_kK;SSHA+j11l#&hdUI#YXn|Pg{Wh^qVtk_#vC1j^KdQbuuBxuvng(elr8}g%J0wMHb| zKJWA1d%r){kFob5jvLu~t}(|LbJ#b&vj9d#T)A9%oqQQDn_j@>*RE20G~R+)ZzO)` z1Xnb1IlpGQMJAO=l+0GfAm^>YSq-BCqt>R9{y#Z&yRB0#2|tmxzBS4+d{U zGZ5yK2EoW~xhj~C_MyV@nzM{T0;b_G{sGfWG(0Q9S|m3NMi!Yc4)%Vcso`iBfN}fA zW_JoFB00h1n|MiJoGK^@>~MGnfEilN`ww6kJ^wBF-X{x4`n~}<2%-m&^!*on8`bO{ z(G*a_9>Z4iZAA*=1z%^527&BD5PzylHaK(oRvY;z@S6OzgjwH{XP+HC0A%A(8IZ-r zR58;bpQbZ@{-;kI?3YXCIX(D|2a(WgBdLaBz}*hUwNgGO94bM8Gh^v49Hr!c0dg$`XG2?v=RB`_T=Hzg#y4=XA{?ebx`Io5d+Ux{a z@Mv3W;CZ4aj6Ucdls(el*{&wDNyAt`5d`&u>)+G^OBgH?xPH7%l{Jm8WF$D2kaPMM zL5PNREn9OZgqq3eAz= z85Rx?_(o*HK^83bWL zuJ)J7K!RPL9~!BbJv6(evp+NgL>OOy8DwK^e(3vixwveNDgv01M6vauG$#778a)AE zwC@)odbwZ&?w3>!4NU!sS1Lo3&MA9!DSs~WtTFRKCvG<@V3m}1(d|r^nt)ls4HR~5 z@Tg{JWWaZGA|Buuoqbf9?Xud+>D;TYlKta(u4RK>_sf)hHIt32a3Gw;Dz7b;`jgps z_q=H*J)z8k0Oz^0vi^Z@U!^M65Wz$hIUb-b5%R!w_y(@Siv*htc5_ahoVAq|L%%)`uA6{g9$bN3vGYC3=Au;d4+Bw>dL&u7rsZB?b}G z34(QMu`2=<;csM$vQ`XnJwF29Iyib5`?>K@Rvzt1M-yYW51WD5`K;+M^;Sy=y`}?n z;;gznunsakl%b9@e`J~I80H6Fxy?7G@wNG7nO<3?n@9OtyT|(si!8k0oL#S2{>d_N zKH@>bwOq}*OWhGpOnKQ<{}qWwk5ZbNDemRJEmf))nS)SgE;i54lEI|bz@$^kh%j&E zoOJH|!^1dq>Qu0GQ=RHPE{VMPTZ8~#jQ$}&hAje){x696o$|lT%x9d&ih1)gAv#A`wTr=B#VW2)@!CBAMvp>VFmzO`oT~&xHK+%d5mZ|f1 zp-7^?|7O5%pp@oXaD`~AK578Sze5?q*_w|CE2*a$)@fsp`O=sYL*@(JnbNw3RDKyi zu%BtOhxhsj5NvNy1U>iw!G=uQmNuqY9G=pZu1Ws}<-5DwkV?^y_3-8IxxsQyIb33} zA2#w&jI^ay+CKdvoW1AJ4Se7BiT6_|n}6{6QfRXgn-=cq_&07?kgD+vaMsE(2s5q5 z9}$TI3o-*r+b1rpi2@4%m+F=Znj{7AgrsvOF!ailauf}$Vm!KZw+BU@@7_|#sWuMY zaVFL2MkmzPGe8CJjN8l=x?#3F+(DT(fEFO2HF2HC61|NnqU~K-o(oHgMF?C|`p!fW zMDL#Z=!rkU2)%QVbLqIo_90~6$!%>7&UJ-}&Y}B&Qc!HtkSSwdhB?_4pZAF0xwNT} zEC`R`fcQ=dVTOhW!5#qYq|((ti`OqJx4ZM19b_r|C}n-^X#ZI;TSUVV;$=_*ErBjv zY06_sDQ##K4qS^ibPD4g~FKMXk0zxctJ5t&PIMby~%)TG1g>#XTX#3s^l<#w~`P zb%IAT1AHOo2bxa$b8>8p1+4eAQ;#mrdMt*v)3#+R%w$nm;kEli9@Re8UfR*+#dp=~ z%1?GVG8^>LTX2Zx>#E9+RJfaUMg8}N)@#3te zeQw}bmEE!^HREjJXZ|&Y{2?8p&VQPd$F&1>s3)NZ7%i-CxWv}V3o-=VLmy_WHTiVN zYs{Y6aKz5j(k80xBFqpJI`q#!IYWOtG_Ht}&7@69-_PQC88+KJ(z**_amMRpezVw$ z4YOm_(rJu%Nid#2j?8J3)u6Q;RwVE1_$TViLeJ2t&7DpBp=iWWgZUnTa&O>F2VDH; zSrKn$B(dtk`*>xE*3aKu9#8iV_L#{z|D2wbmC>K0C0*?oFI;ZjF5M0ZE<~oBUEYw{K|h#96;ul%OY0@413L7QO#a zfNiUF(QDy3qVjxnf$~*`XAS-OtB34&>h$3(>r}kXTzwp4u6F{EbK=$O_j@Vph(Tu) z=Q#SJ4E}dXle9P?msNa^gXO}J*n-Qq6kd%jx`*ZWIPRAf24p`$=Qo)e$AI&&KU?v7 zuHEEUYdrD$=EZW%g`Niop1`dhti~z6j**9@+oLoAP zypRk%VKW?hH10^pu$^Di^W4bUctqV)aon0-_q3Eo`JfmciPqc|-7WLbeoAB~@F}w7 zxYBsjLR-evt)F*_rZD#>@`P%AN6X{+G5PzX0anha(9M?IQX=|$%2}aPvE03|ceig& z4Bu&q&Ndz}K^GKmW$Bh)tXbC0Zzi5lopK30Qpqe`IP)N!bktjXo%*5PYZu$FZXCHS z1V_g2aZjF8+u_hWe;veMx#wzqJbSK!<*Ifkq0-({HTR}npaSxT94T0(M1bAXc*t); zhOeHlvZ?=;mHAHojNuCb7JqaY1(sZHlZv!d{~ZN8vo7$e%~@cg4`>39sDcs9e*YaV zxAhGkdT<^taDe6k$r>XfCc34qyIqz|RNu_&Fu0sI%ENi>lp^5;Kj!e zP=dD`Ko3VnjvS14177d#@sq)UH%>W*WuBe~SxA~-M{0p6-kSYi5ZsUeDFRO%lv$NZKQ-68JGHv_tMnVg3qFV4(=n%d!yQ!eliKuv{Ple$E% z(0s0`1pkhbknoEHV4u+8kb_OJi(+nu*{UBC#{BZHZ_gw6j&A`~vX2GawPGM_=P_YB z!~efi�eo`tWiT%OnYVtNdi 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

  • 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­a)60(CPt6~%@usHTCK-rQ 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 N-}zcgR*;78`X6W2dg=fG diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121227.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121227.log.gz deleted file mode 100644 index 07df6074a5a8a570286d28f08954364684c4457a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131302 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≺loiVL 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 AgHYYAX-yS zjK$eUzz^k%ap8R(uni8uM{>N_hzq(AGy=R%toX7u<9lMH)b;UcO2JEKkZnKfcw{q| zYZ5C$I}2aGtmdnlru6eHks?GBQg2u$A}DA`=Bg!ZA`Is03fs*z;H%^#jC$WcAyF~c z-bR!tX6$T{rAD4aN6OY{p_INE_8bOSCUz^ATPQW6ZnR31+6~yZ2U5rma$mE^4sJku zVopV=n}jg#;W!Vt+IGLnfa$d!kWE^q0URkDfH{`F_w#Js){@GTNMLp?{k{%7Z-J;G zVzR9Jvq#4<)y`8lmYit#Q0%uH4>3}$8Xm=*Ejl&(|5h1EfZGQ1^E&#fO)c57IA}Vy^F&KjE_u_MXsyuN{Dg8Tn>zN~uL z9p;)`<5-2^LT8QOsQP5DaG!c2jg<%U#5yCgZ`fYHc_OrO$%9!J^MLGSW#w|)*f^63 zM%G4cdPOVGYRoYar#n_|q39qi&H^WZe6m{vhLi2fcDC*VYF|%=@1i>uEWA=htT^z4zmLUn4nx@GgLPS3L#U zX1DrAy3X0=*y5>Vh0?y2^qkJ{=6*jC>RsM0F;H3=J+@rp(a&l*cgDK?vvzsuSX)=k zYh!{pM&Rf*van&h%@eLXZkisbe2GPr*uKWTFS@65nGVY`+qkM+xS;xWcC@ZnJ4n zdXVgr|8w-ToX2)mICZ)w?jz5UT;xnoT#hY-i#_Zgs0AZ3&OT?9d%?Y9yK+BPP94KFJ$W$}*i#~b7iERP#KC{D2jy6&7IeQaO11lV4(R*J zK`iPcq(tM^Sqj)Vql6`m0%~^W#hqp3peJvjlh|9`VhlA$Z@;7n-$~u@ik2lyJn%kA zn-M5`t�@_bOlJC>9rYe-d-1!T~8V%vXWVIquhmrca{+6{4{HduCr$4Ii>Ui}pUo z2)t`~gJ2Qax|iF>)2yb^EU!tI>5*#3N&gd*L4!GXXXCO3H0Y1w;QJ^$W*tcpEANmw zd?2$e7Qv?BTBZY>gxrD3zmi}q*+!)RmcljBkVqsGV7x&UiVCy<`X2HY?QeOssY{1@ ziO3)EP0X?0xghw|C-ikphXW?$1JkNxKf#gBrD9g%qlC`R-4??`Bljt&5$G% zv#HHYt7Yz2kZQyqc;I-Er6~T3b$u}CJ-z^RhuXSbTLtmVJr!GzC{rj)MYXkX0=k>2 z5ENkT3#7yJ_65Z9;0B-tKbNdT4d#Q8cbZ5ErEf7o$bnKp`G7SvRHzM@v3V;rxaHs+d@w*O)~2eR|)Uj6jhTQL`PU9bQI7@8ZF2di=WR2!3EU0 z@_{q?{{yO&*2$Rm#so9S<#2X1VAG3E%E^uzny3qGdU0he>`EprB+b`OVA5VaNDuCk zAAtyEcg81-I)+#c&1Edt3mL;{Lb#-gB{__gBTH@5wtD0&Q8SxAf3j$HksIuNRcF!A zT6~jnj?5DOwf-Zk?)V#J{}&uvm}7=cQ~T#g!^@k{-ykbY5pH`=ceF>NHt4et($z2C z1|TNx2>hWuq36~z3Ej+&uHRL>F%;oHGl9w=R7s~ecbeN*|MTzm|H*Fom5DyfUi`>CfrWO z-+6cVc?>4y&0ZBOp`duxV3Vb3k>-1kqUyQh5<*UN_B^e7-9x8=3jKINR`pyC4Kb3I zwBuYzQP-NN;jRdT@29~@_w}wpm9Y1`h}FnADrK|a!Chn}?{ACu?~a!(Weo7xMjr^)GSW$qzQfis^`a8X_lrk1hkG}xnNgBWAj8{{k#YkpkFs(9tlux1qYiDj=aTs+F8-_Yj_vOJbBbqN0az1N2)1w0{i z@_QEjzyjlc0PlEpp?BJ&MpToX$=5uI=#}-nBV=ZY&>yyr=0d5VvqnN%P^tQ0E~GL~ zVu8Z=1&g=z&{QKKyLb81l}<`r=4j7{Bpl_fvD1W16^(442lSFiqX67l+mF_vD|Gg& zcjD|?YZ0uh9QX)(3xa2wi{Ns4dgk4I^p-UBFV|)cyqa0&qhapxneBqa`q^y7&rI>` z&^%cAOGPy)pEfnRqERr^5b~mT@uE9qAFMryF|B^Q^KQHxqYU1CJDBPnKhg#N069*L z;pk_NAFRZR&E$O%>SoWhN4odd%%D1sT#ic2t)lpxIPaPrjN0 z`LxRXaD2Gzvbgo<_O4>qEU6^4X43G>&Ys#L)2HR-QiEwXzdBk5K#6~3*#h}9%KI;x zUzSYXoL^SfOxh!)oM+aopzXu-;SpkdPmPm(Y?i252CzczAqlMek#+AVVCwtRP`XrN zrKpCv+u_VEq?Bus3zWUIT8Onl^7m;9Lkz?$iBV^?cy!7Q?tqk$E(%+=slFTH?0c9I zq9|Zi2Vq_rDgO0op*Cnfa-l47un`B1nFdwKHwWMs}EQ7`YI~8U_DnYarA5Odc%8+p)MCm%vm}ablhzu!#^c3 zeQ=E~{V;tNdlgIeEh~?_Y}r1wlxv|BQ5Y31Cc3hA@9F1feR|MsLt>sR~IU8v%sc43J5rndTkXz$&YAvox_K%CD@wTi!P_QxhcZDR>XMQ)ww;7rx^D zD+B~ZNrWetd-FhQ#-j)9Zs4j^c9}dItUhwhR(o4zw19qVZDNx8wawI2J+rke!r7d( zz^GpQY}|O|irUQ~Y$oM~=AF}B%3Ww}yiJs;B)pW!cdgfMmBje^PGc_wyr><>IH6Sm z#UzN7U3UrM;~b`hVBQ59))l(HZt=m9Q#Y=v%2=uyO}w=m`RjDQZdT`~Wk{tPyQqQE zss(>>9I#lgKG*9SS&yvHBAglheC#(k`|_jv`Lz1XuldyqUAtF(&XEkCAwSK~wqk_- zS}qW0P*Q#!yG!$a9Pz9-grrJ7M@ZM{EiyDMoZpq>juM^t%qzv4;Qv diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121228.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121228.log.gz deleted file mode 100644 index 41c95cda32e85f228975d4b892ff45e97f7b9771..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128106 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
    oqs)XWE*{N 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%0D3v8-NHCjIjQ-lv%)Hr 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 diff --git a/chef/cookbooks/python/src/12/storing_thread_specific_state/example1.py b/chef/cookbooks/python/src/12/storing_thread_specific_state/example1.py deleted file mode 100644 index 42523b5..0000000 --- a/chef/cookbooks/python/src/12/storing_thread_specific_state/example1.py +++ /dev/null @@ -1,45 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM -import threading - -class LazyConnection: - def __init__(self, address, family=AF_INET, type=SOCK_STREAM): - self.address = address - self.family = AF_INET - self.type = SOCK_STREAM - self.local = threading.local() - - def __enter__(self): - if hasattr(self.local, 'sock'): - raise RuntimeError('Already connected') - self.local.sock = socket(self.family, self.type) - self.local.sock.connect(self.address) - return self.local.sock - - def __exit__(self, exc_ty, exc_val, tb): - self.local.sock.close() - del self.local.sock - -def test(conn): - from functools import partial - - # Connection closed - with conn as s: - # conn.__enter__() executes: connection open - s.send(b'GET /index.html HTTP/1.0\r\n') - s.send(b'Host: www.python.org\r\n') - s.send(b'\r\n') - resp = b''.join(iter(partial(s.recv, 8192), b'')) - # conn.__exit__() executes: connection closed - - print('Got {} bytes'.format(len(resp))) - -if __name__ == '__main__': - conn = LazyConnection(('www.python.org', 80)) - - t1 = threading.Thread(target=test, args=(conn,)) - t2 = threading.Thread(target=test, args=(conn,)) - t1.start() - t2.start() - t1.join() - t2.join() - diff --git a/chef/cookbooks/python/src/12/storing_thread_specific_state/example2.py b/chef/cookbooks/python/src/12/storing_thread_specific_state/example2.py deleted file mode 100644 index 12e0640..0000000 --- a/chef/cookbooks/python/src/12/storing_thread_specific_state/example2.py +++ /dev/null @@ -1,60 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM -import threading - -class LazyConnection: - def __init__(self, address, family=AF_INET, type=SOCK_STREAM): - self.address = address - self.family = AF_INET - self.type = SOCK_STREAM - self.local = threading.local() - - def __enter__(self): - sock = socket(self.family, self.type) - sock.connect(self.address) - if not hasattr(self.local, 'connections'): - self.local.connections = [] - self.local.connections.append(sock) - return sock - - def __exit__(self, exc_ty, exc_val, tb): - self.local.connections.pop().close() - -def test(conn): - # Example use - from functools import partial - - with conn as s: - s.send(b'GET /index.html HTTP/1.0\r\n') - s.send(b'Host: www.python.org\r\n') - s.send(b'\r\n') - resp = b''.join(iter(partial(s.recv, 8192), b'')) - - print('Got {} bytes'.format(len(resp))) - - with conn as s1, conn as s2: - s1.send(b'GET /downloads HTTP/1.0\r\n') - s2.send(b'GET /index.html HTTP/1.0\r\n') - s1.send(b'Host: www.python.org\r\n') - s2.send(b'Host: www.python.org\r\n') - s1.send(b'\r\n') - s2.send(b'\r\n') - resp1 = b''.join(iter(partial(s1.recv, 8192), b'')) - resp2 = b''.join(iter(partial(s2.recv, 8192), b'')) - - print('resp1 got {} bytes'.format(len(resp1))) - print('resp2 got {} bytes'.format(len(resp2))) - -if __name__ == '__main__': - - conn = LazyConnection(('www.python.org', 80)) - t1 = threading.Thread(target=test, args=(conn,)) - t2 = threading.Thread(target=test, args=(conn,)) - t3 = threading.Thread(target=test, args=(conn,)) - t1.start() - t2.start() - t3.start() - t1.join() - t2.join() - t3.join() - - diff --git a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/actorsched.py b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/actorsched.py deleted file mode 100644 index 8f2e57f..0000000 --- a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/actorsched.py +++ /dev/null @@ -1,59 +0,0 @@ -from collections import deque - -class ActorScheduler: - def __init__(self): - self._actors = { } # Mapping of names to actors - self._msg_queue = deque() # Message queue - - def new_actor(self, name, actor): - ''' - Admit a newly started actor to the scheduler and give it a name - ''' - self._msg_queue.append((actor,None)) - self._actors[name] = actor - - def send(self, name, msg): - ''' - Send a message to a named actor - ''' - actor = self._actors.get(name) - if actor: - self._msg_queue.append((actor,msg)) - - def run(self): - ''' - Run as long as there are pending messages. - ''' - while self._msg_queue: - actor, msg = self._msg_queue.popleft() - try: - actor.send(msg) - except StopIteration: - pass - -# Example use -if __name__ == '__main__': - def printer(): - while True: - msg = yield - print('Got:', msg) - - def counter(sched): - while True: - # Receive the current count - n = yield - if n == 0: - break - # Send to the printer task - sched.send('printer', n) - # Send the next count to the counter task (recursive) - sched.send('counter', n-1) - - sched = ActorScheduler() - # Create the initial actors - sched.new_actor('printer', printer()) - sched.new_actor('counter', counter(sched)) - - # Send an initial message to the counter to initiate - sched.send('counter', 10000) - sched.run() diff --git a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/netsched.py b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/netsched.py deleted file mode 100644 index b6dba71..0000000 --- a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/netsched.py +++ /dev/null @@ -1,159 +0,0 @@ -from collections import deque -from select import select - -# This class represents a generic yield event in the scheduler -class YieldEvent: - def handle_yield(self, sched, task): - pass - def handle_resume(self, sched, task): - pass - -# Task Scheduler -class Scheduler: - def __init__(self): - self._numtasks = 0 # Total num of tasks - self._ready = deque() # Tasks ready to run - self._read_waiting = {} # Tasks waiting to read - self._write_waiting = {} # Tasks waiting to write - - # Poll for I/O events and restart waiting tasks - def _iopoll(self): - rset,wset,eset = select(self._read_waiting, - self._write_waiting,[]) - for r in rset: - evt, task = self._read_waiting.pop(r) - evt.handle_resume(self, task) - for w in wset: - evt, task = self._write_waiting.pop(w) - evt.handle_resume(self, task) - - def new(self,task): - ''' - Add a newly started task to the scheduler - ''' - self._ready.append((task, None)) - self._numtasks += 1 - - def add_ready(self, task, msg=None): - ''' - Append an already started task to the ready queue. - msg is what to send into the task when it resumes. - ''' - self._ready.append((task, msg)) - - # Add a task to the reading set - def _read_wait(self, fileno, evt, task): - self._read_waiting[fileno] = (evt, task) - - # Add a task to the write set - def _write_wait(self, fileno, evt, task): - self._write_waiting[fileno] = (evt, task) - - def run(self): - ''' - Run the task scheduler until there are no tasks - ''' - while self._numtasks: - if not self._ready: - self._iopoll() - task, msg = self._ready.popleft() - try: - # Run the coroutine to the next yield - r = task.send(msg) - if isinstance(r, YieldEvent): - r.handle_yield(self, task) - else: - raise RuntimeError('unrecognized yield event') - except StopIteration: - self._numtasks -= 1 - -# Example implementation of coroutine based socket I/O -class ReadSocket(YieldEvent): - def __init__(self, sock, nbytes): - self.sock = sock - self.nbytes = nbytes - def handle_yield(self, sched, task): - sched._read_wait(self.sock.fileno(), self, task) - def handle_resume(self, sched, task): - data = self.sock.recv(self.nbytes) - sched.add_ready(task, data) - -class WriteSocket(YieldEvent): - def __init__(self, sock, data): - self.sock = sock - self.data = data - def handle_yield(self, sched, task): - sched._write_wait(self.sock.fileno(), self, task) - def handle_resume(self, sched, task): - nsent = self.sock.send(self.data) - sched.add_ready(task, nsent) - -class AcceptSocket(YieldEvent): - def __init__(self, sock): - self.sock = sock - def handle_yield(self, sched, task): - sched._read_wait(self.sock.fileno(), self, task) - def handle_resume(self, sched, task): - r = self.sock.accept() - sched.add_ready(task, r) - -# Wrapper around a socket object for use with yield -class Socket(object): - def __init__(self, sock): - self._sock = sock - def recv(self, maxbytes): - return ReadSocket(self._sock, maxbytes) - def send(self, data): - return WriteSocket(self._sock, data) - def accept(self): - return AcceptSocket(self._sock) - def __getattr__(self, name): - return getattr(self._sock, name) - -if __name__ == '__main__': - from socket import socket, AF_INET, SOCK_STREAM - import time - - # Example of a function involving generators. This should - # be called using line = yield from readline(sock) - def readline(sock): - chars = [] - while True: - c = yield sock.recv(1) - if not c: - break - chars.append(c) - if c == b'\n': - break - return b''.join(chars) - - # Echo server using generators - class EchoServer: - def __init__(self,addr,sched): - self.sched = sched - sched.new(self.server_loop(addr)) - - def server_loop(self,addr): - s = Socket(socket(AF_INET,SOCK_STREAM)) - s.bind(addr) - s.listen(5) - while True: - c,a = yield s.accept() - print('Got connection from ', a) - self.sched.new(self.client_handler(Socket(c))) - - def client_handler(self,client): - while True: - line = yield from readline(client) - if not line: - break - line = b'GOT:' + line - while line: - nsent = yield client.send(line) - line = line[nsent:] - client.close() - print('Client closed') - - sched = Scheduler() - EchoServer(('',16000),sched) - sched.run() diff --git a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/simple.py b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/simple.py deleted file mode 100644 index ed178c1..0000000 --- a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/simple.py +++ /dev/null @@ -1,49 +0,0 @@ -# A very simple example of a coroutine/generator scheduler - -# Two simple generator functions -def countdown(n): - while n > 0: - print("T-minus", n) - yield - n -= 1 - print("Blastoff!") - -def countup(n): - x = 0 - while x < n: - print("Counting up", x) - yield - x += 1 - -from collections import deque - -class TaskScheduler: - def __init__(self): - self._task_queue = deque() - - def new_task(self, task): - ''' - Admit a newly started task to the scheduler - ''' - self._task_queue.append(task) - - def run(self): - ''' - Run until there are no more tasks - ''' - while self._task_queue: - task = self._task_queue.popleft() - try: - # Run until the next yield statement - next(task) - self._task_queue.append(task) - except StopIteration: - # Generator is no longer executing - pass - -# Example use -sched = TaskScheduler() -sched.new_task(countdown(10)) -sched.new_task(countdown(5)) -sched.new_task(countup(15)) -sched.run() diff --git a/chef/cookbooks/python/src/13/adding_logging_to_libraries/somelib.py b/chef/cookbooks/python/src/13/adding_logging_to_libraries/somelib.py deleted file mode 100644 index 96f754e..0000000 --- a/chef/cookbooks/python/src/13/adding_logging_to_libraries/somelib.py +++ /dev/null @@ -1,10 +0,0 @@ -# somelib.py - -import logging -log = logging.getLogger(__name__) -log.addHandler(logging.NullHandler()) - -# Example function (for testing) -def func(): - log.critical("A Critical Error!") - log.debug("A debug message") diff --git a/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example1.py b/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example1.py deleted file mode 100644 index 781843d..0000000 --- a/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example1.py +++ /dev/null @@ -1,9 +0,0 @@ -import subprocess -try: - out_bytes = subprocess.check_output(['netstat', '-a']) - out_text = out_bytes.decode('utf-8') - print(out_text) -except subprocess.CalledProcessError as e: - print('It did not work. Reason:', e) - print('Exitcode:', e.returncode) - diff --git a/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example2.py b/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example2.py deleted file mode 100644 index 5ced9ad..0000000 --- a/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example2.py +++ /dev/null @@ -1,20 +0,0 @@ -import subprocess - -# Some text to send -text = b''' -hello world -this is a test -goodbye -''' - -# Launch a command with pipes -p = subprocess.Popen(['wc'], - stdout = subprocess.PIPE, - stdin = subprocess.PIPE) - -# Send the data and get the output -stdout, stderr = p.communicate(text) - -text = stdout.decode('utf-8') -print(text) - diff --git a/chef/cookbooks/python/src/13/finding_files/modified_within.py b/chef/cookbooks/python/src/13/finding_files/modified_within.py deleted file mode 100644 index de0ff24..0000000 --- a/chef/cookbooks/python/src/13/finding_files/modified_within.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3.3 - -import os -import time - -def modified_within(top, seconds): - now = time.time() - for path, dirs, files in os.walk(top): - for name in files: - fullpath = os.path.join(path, name) - if os.path.exists(fullpath): - mtime = os.path.getmtime(fullpath) - if mtime > (now - seconds): - print(fullpath) - - -if __name__ == '__main__': - import sys - if len(sys.argv) != 3: - print('Usage: {} dir seconds'.format(sys.argv[0])) - raise SystemExit(1) - - modified_within(sys.argv[1], float(sys.argv[2])) - - - - diff --git a/chef/cookbooks/python/src/13/generating_a_range_of_ip_addresses_from_a_cidr_address/example.py b/chef/cookbooks/python/src/13/generating_a_range_of_ip_addresses_from_a_cidr_address/example.py deleted file mode 100644 index 4966063..0000000 --- a/chef/cookbooks/python/src/13/generating_a_range_of_ip_addresses_from_a_cidr_address/example.py +++ /dev/null @@ -1,26 +0,0 @@ -from socket import AF_INET, AF_INET6, inet_pton, inet_ntop - -def cidr_range(cidr_address): - family = AF_INET6 if ':' in cidr_address else AF_INET - address, maskstr = cidr_address.split('/') - maskbits = int(maskstr) - - # Parse the supplied address into bytes - addr_bytes = inet_pton(family, address) - - # Calculate number of address bytes and mask bits - addr_len = len(addr_bytes) - numaddrs = 2**(addr_len*8 - maskbits) - mask = -numaddrs - - # Generate addresses - addr = int.from_bytes(addr_bytes, 'big') & mask - for n in range(numaddrs): - yield inet_ntop(family, (addr+n).to_bytes(addr_len, 'big')) - -if __name__ == '__main__': - for a in cidr_range('123.45.67.89/27'): - print(a) - - for a in cidr_range('12:3456:78:90ab:cd:ef01:23:34/125'): - print(a) diff --git a/chef/cookbooks/python/src/13/getting_the_terminal_size/example.py b/chef/cookbooks/python/src/13/getting_the_terminal_size/example.py deleted file mode 100644 index a6e78e9..0000000 --- a/chef/cookbooks/python/src/13/getting_the_terminal_size/example.py +++ /dev/null @@ -1,4 +0,0 @@ -import os -sz = os.get_terminal_size() -print(sz.columns, 'columns') -print(sz.lines, 'lines') diff --git a/chef/cookbooks/python/src/13/making_a_stopwatch/stopwatch.py b/chef/cookbooks/python/src/13/making_a_stopwatch/stopwatch.py deleted file mode 100644 index 756f473..0000000 --- a/chef/cookbooks/python/src/13/making_a_stopwatch/stopwatch.py +++ /dev/null @@ -1,50 +0,0 @@ -import time - -class Timer: - def __init__(self, func=time.perf_counter): - self.elapsed = 0.0 - self._func = func - self._start = None - - def start(self): - if self._start is not None: - raise RuntimeError('Already started') - self._start = self._func() - - def end(self): - if self._start is None: - raise RuntimeError('Not started') - end = self._func() - self.elapsed += end - self._start - self._start = None - - def reset(self): - self.elapsed = 0.0 - - @property - def running(self): - return self._start is not None - - def __enter__(self): - self.start() - return self - - def __exit__(self, *args): - self.end() - - -if __name__ == '__main__': - def countdown(n): - while n > 0: - n -= 1 - - t = Timer() - t.start() - countdown(1000000) - t.end() - print(t.elapsed) - - with t: - countdown(1000000) - print(t.elapsed) - diff --git a/chef/cookbooks/python/src/13/parsing_command_line_options/search.py b/chef/cookbooks/python/src/13/parsing_command_line_options/search.py deleted file mode 100644 index 891fd46..0000000 --- a/chef/cookbooks/python/src/13/parsing_command_line_options/search.py +++ /dev/null @@ -1,32 +0,0 @@ -# search.py -''' -Hypothetical command line tool for searching a collection of -files for one or more text patterns. -''' -import argparse -parser = argparse.ArgumentParser(description='Search some files') - -parser.add_argument(dest='filenames',metavar='filename', nargs='*') - -parser.add_argument('-p', '--pat',metavar='pattern', required=True, - dest='patterns', action='append', - help='text pattern to search for') - -parser.add_argument('-v', dest='verbose', action='store_true', - help='verbose mode') - -parser.add_argument('-o', dest='outfile', action='store', - help='output file') - -parser.add_argument('--speed', dest='speed', action='store', - choices={'slow','fast'}, default='slow', - help='search speed') - -args = parser.parse_args() - -# Output the collected arguments -print(args.filenames) -print(args.patterns) -print(args.verbose) -print(args.outfile) -print(args.speed) diff --git a/chef/cookbooks/python/src/13/prompting_for_a_password_at_runtime/example.py b/chef/cookbooks/python/src/13/prompting_for_a_password_at_runtime/example.py deleted file mode 100644 index 66f572e..0000000 --- a/chef/cookbooks/python/src/13/prompting_for_a_password_at_runtime/example.py +++ /dev/null @@ -1,7 +0,0 @@ -import getpass - -user = getpass.getuser() -passwd = getpass.getpass() - -print('User:', user) -print('Passwd:', passwd) diff --git a/chef/cookbooks/python/src/13/putting_limits_on_memory_and_cpu_usage/example.py b/chef/cookbooks/python/src/13/putting_limits_on_memory_and_cpu_usage/example.py deleted file mode 100644 index 1dd347e..0000000 --- a/chef/cookbooks/python/src/13/putting_limits_on_memory_and_cpu_usage/example.py +++ /dev/null @@ -1,18 +0,0 @@ -import signal -import resource -import os - -def time_exceeded(signo, frame): - print("Time's up!") - raise SystemExit(1) - -def set_max_runtime(seconds): - # Install the signal handler and set a resource limit - soft, hard = resource.getrlimit(resource.RLIMIT_CPU) - resource.setrlimit(resource.RLIMIT_CPU, (seconds, hard)) - signal.signal(signal.SIGXCPU, time_exceeded) - -if __name__ == '__main__': - set_max_runtime(15) - while True: - pass diff --git a/chef/cookbooks/python/src/13/reading_configuration_files/config.ini b/chef/cookbooks/python/src/13/reading_configuration_files/config.ini deleted file mode 100644 index f588545..0000000 --- a/chef/cookbooks/python/src/13/reading_configuration_files/config.ini +++ /dev/null @@ -1,24 +0,0 @@ - - ; config.ini - ; Sample configuration file - - [installation] - library=%(prefix)s/lib - include=%(prefix)s/include - bin=%(prefix)s/bin - prefix=/usr/local - - # Setting related to debug configuration - [debug] - log_errors=true - show_warnings=False - - [server] - port: 8080 - nworkers: 32 - pid-file=/tmp/spam.pid - root=/www/root - signature: - ================================= - Brought to by the Python Cookbook - ================================= diff --git a/chef/cookbooks/python/src/13/reading_configuration_files/example1.py b/chef/cookbooks/python/src/13/reading_configuration_files/example1.py deleted file mode 100644 index 8922073..0000000 --- a/chef/cookbooks/python/src/13/reading_configuration_files/example1.py +++ /dev/null @@ -1,9 +0,0 @@ -from configparser import ConfigParser -cfg = ConfigParser() -cfg.read('config.ini') -print('sections:', cfg.sections()) -print('installation:library', cfg.get('installation','library')) -print('debug:log_errors', cfg.getboolean('debug','log_errors')) -print('server:port', cfg.getint('server','port')) -print('server:nworkers', cfg.getint('server','nworkers')) -print('server:signature', cfg.get('server','signature')) diff --git a/chef/cookbooks/python/src/13/simple_logging_for_scripts/example1.py b/chef/cookbooks/python/src/13/simple_logging_for_scripts/example1.py deleted file mode 100644 index b584eab..0000000 --- a/chef/cookbooks/python/src/13/simple_logging_for_scripts/example1.py +++ /dev/null @@ -1,24 +0,0 @@ -import logging - -def main(): - # Configure the logging system - logging.basicConfig( - filename='app.log', - level=logging.ERROR - ) - - # Variables (to make the calls that follow work) - hostname = 'www.python.org' - item = 'spam' - filename = 'data.csv' - mode = 'r' - - # Example logging calls (insert into your program) - logging.critical('Host %s unknown', hostname) - logging.error("Couldn't find %r", item) - logging.warning('Feature is deprecated') - logging.info('Opening file %r, mode=%r', filename, mode) - logging.debug('Got here') - -if __name__ == '__main__': - main() diff --git a/chef/cookbooks/python/src/13/simple_logging_for_scripts/example2.py b/chef/cookbooks/python/src/13/simple_logging_for_scripts/example2.py deleted file mode 100644 index 61059dc..0000000 --- a/chef/cookbooks/python/src/13/simple_logging_for_scripts/example2.py +++ /dev/null @@ -1,22 +0,0 @@ -import logging -import logging.config - -def main(): - # Configure the logging system - logging.config.fileConfig('logconfig.ini') - - # Variables (to make the calls that follow work) - hostname = 'www.python.org' - item = 'spam' - filename = 'data.csv' - mode = 'r' - - # Example logging calls (insert into your program) - logging.critical('Host %s unknown', hostname) - logging.error("Couldn't find %r", item) - logging.warning('Feature is deprecated') - logging.info('Opening file %r, mode=%r', filename, mode) - logging.debug('Got here') - -if __name__ == '__main__': - main() diff --git a/chef/cookbooks/python/src/13/simple_logging_for_scripts/logconfig.ini b/chef/cookbooks/python/src/13/simple_logging_for_scripts/logconfig.ini deleted file mode 100644 index 37b7d36..0000000 --- a/chef/cookbooks/python/src/13/simple_logging_for_scripts/logconfig.ini +++ /dev/null @@ -1,21 +0,0 @@ -[loggers] -keys=root - -[handlers] -keys=defaultHandler - -[formatters] -keys=defaultFormatter - -[logger_root] -level=INFO -handlers=defaultHandler -qualname=root - -[handler_defaultHandler] -class=FileHandler -formatter=defaultFormatter -args=('app.log', 'a') - -[formatter_defaultFormatter] -format=%(levelname)s:%(name)s:%(message)s diff --git a/chef/cookbooks/python/src/14/logging_test_output_to_a_file/test.py b/chef/cookbooks/python/src/14/logging_test_output_to_a_file/test.py deleted file mode 100644 index e05221f..0000000 --- a/chef/cookbooks/python/src/14/logging_test_output_to_a_file/test.py +++ /dev/null @@ -1,36 +0,0 @@ -import unittest - -# A simple function to illustrate -def parse_int(s): - return int(s) - -class TestConversion(unittest.TestCase): - # Testing that an exception gets raised - def test_bad_int(self): - self.assertRaises(ValueError, parse_int, "N/A") - - # Testing an exception plus regex on exception message - def test_bad_int_msg(self): - self.assertRaisesRegex(ValueError, 'invalid literal .*', parse_int, 'N/A') - -# Example of testing an exception along with inspection of exception instance -import errno - -class TestIO(unittest.TestCase): - def test_file_not_found(self): - try: - f = open('/file/not/found') - except IOError as e: - self.assertEqual(e.errno, errno.ENOENT) - else: - self.fail("IOError not raised") - -import sys -def main(out=sys.stderr, verbosity=2): - loader = unittest.TestLoader() - suite = loader.loadTestsFromModule(sys.modules[__name__]) - unittest.TextTestRunner(out, verbosity=verbosity).run(suite) - -if __name__ == '__main__': - with open('testing.out', 'w') as f: - main(f) diff --git a/chef/cookbooks/python/src/14/make_your_programs_run_faster/example.py b/chef/cookbooks/python/src/14/make_your_programs_run_faster/example.py deleted file mode 100644 index 045aeac..0000000 --- a/chef/cookbooks/python/src/14/make_your_programs_run_faster/example.py +++ /dev/null @@ -1,35 +0,0 @@ -import time -def test(func): - start = time.time() - nums = range(1000000) - for n in range(100): - r = func(nums) - end = time.time() - print(func.__name__, ':', end-start) - -import math -def compute_roots_1(nums): - result = [] - for n in nums: - result.append(math.sqrt(n)) - return result - -from math import sqrt -def compute_roots_2(nums): - result = [] - result_append = result.append - for n in nums: - result_append(sqrt(n)) - return result - -def compute_roots_3(nums): - sqrt = math.sqrt - result = [] - result_append = result.append - for n in nums: - result_append(sqrt(n)) - return result - -tests = [compute_roots_1, compute_roots_2, compute_roots_3] -for func in tests: - test(func) diff --git a/chef/cookbooks/python/src/14/profiling_and_timing_your_program/timethis.py b/chef/cookbooks/python/src/14/profiling_and_timing_your_program/timethis.py deleted file mode 100644 index 5e82448..0000000 --- a/chef/cookbooks/python/src/14/profiling_and_timing_your_program/timethis.py +++ /dev/null @@ -1,23 +0,0 @@ -# timethis.py - -import time -from functools import wraps - -def timethis(func): - @wraps(func) - def wrapper(*args, **kwargs): - start = time.perf_counter() - r = func(*args, **kwargs) - end = time.perf_counter() - print('{}.{} : {}'.format(func.__module__, func.__name__, end-start)) - return r - return wrapper - -if __name__ == '__main__': - @timethis - def countdown(n): - while n > 0: - n -= 1 - - - countdown(10000000) diff --git a/chef/cookbooks/python/src/14/raising_an_exception_in_response_to_another_exception/example.py b/chef/cookbooks/python/src/14/raising_an_exception_in_response_to_another_exception/example.py deleted file mode 100644 index 3e8e6a6..0000000 --- a/chef/cookbooks/python/src/14/raising_an_exception_in_response_to_another_exception/example.py +++ /dev/null @@ -1,50 +0,0 @@ -# Different styles of raising chained exceptions - -# Example 1: Explicit chaining. Use this whenever your -# intent is to raise a new exception in response to another - -def example1(): - try: - int('N/A') - except ValueError as e: - raise RuntimeError('A parsing error occurred') from e - -# Example 2: Implicit chaining. This occurs if there's an -# unexpected exception in the except block. - -def example2(): - try: - int('N/A') - except ValueError as e: - print('It failed. Reason:', err) # Intentional error - -# Example 3: Discarding the previous exception -def example3(): - try: - int('N/A') - except ValueError as e: - raise RuntimeError('A parsing error occurred') from None - -if __name__ == '__main__': - import traceback - print('****** EXPLICIT EXCEPTION CHAINING ******') - try: - example1() - except Exception: - traceback.print_exc() - - print() - print('****** IMPLICIT EXCEPTION CHAINING ******') - try: - example2() - except Exception: - traceback.print_exc() - - print() - print('****** DISCARDED CHAINING *******') - try: - example3() - except Exception: - traceback.print_exc() - - diff --git a/chef/cookbooks/python/src/14/skipping_or_anticipating_test_failures/test.py b/chef/cookbooks/python/src/14/skipping_or_anticipating_test_failures/test.py deleted file mode 100644 index aa1cf61..0000000 --- a/chef/cookbooks/python/src/14/skipping_or_anticipating_test_failures/test.py +++ /dev/null @@ -1,26 +0,0 @@ -import unittest -import os -import platform - -class Tests(unittest.TestCase): - def test_0(self): - self.assertTrue(True) - - @unittest.skip('skipped test') - def test_1(self): - self.fail("should have failed!") - - @unittest.skipIf(os.name=='posix', 'Not supported on Unix') - def test_2(self): - import winreg - - @unittest.skipUnless(platform.system() == 'Darwin', 'Mac specific test') - def test_3(self): - self.assertTrue(True) - - @unittest.expectedFailure - def test_4(self): - self.assertEqual(2+2, 5) - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/chef/cookbooks/python/src/14/testing_for_exceptional_conditions_in_unit_tests/test.py b/chef/cookbooks/python/src/14/testing_for_exceptional_conditions_in_unit_tests/test.py deleted file mode 100644 index 53ab362..0000000 --- a/chef/cookbooks/python/src/14/testing_for_exceptional_conditions_in_unit_tests/test.py +++ /dev/null @@ -1,29 +0,0 @@ -import unittest - -# A simple function to illustrate -def parse_int(s): - return int(s) - -class TestConversion(unittest.TestCase): - # Testing that an exception gets raised - def test_bad_int(self): - self.assertRaises(ValueError, parse_int, "N/A") - - # Testing an exception plus regex on exception message - def test_bad_int_msg(self): - self.assertRaisesRegex(ValueError, 'invalid literal .*', parse_int, 'N/A') - -# Example of testing an exception along with inspection of exception instance -import errno - -class TestIO(unittest.TestCase): - def test_file_not_found(self): - try: - f = open('/file/not/found') - except IOError as e: - self.assertEqual(e.errno, errno.ENOENT) - else: - self.fail("IOError not raised") - -if __name__ == '__main__': - unittest.main() diff --git a/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/mymodule.py b/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/mymodule.py deleted file mode 100644 index f0e2547..0000000 --- a/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/mymodule.py +++ /dev/null @@ -1,5 +0,0 @@ -# mymodule.py - -def urlprint(protocol, host, domain): - url = '{}://{}.{}'.format(protocol, host, domain) - print(url) diff --git a/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/testmymodule.py b/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/testmymodule.py deleted file mode 100644 index ca64b2a..0000000 --- a/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/testmymodule.py +++ /dev/null @@ -1,19 +0,0 @@ -from io import StringIO -from unittest import TestCase -from unittest.mock import patch -import mymodule - -class TestURLPrint(TestCase): - def test_url_gets_to_stdout(self): - protocol = 'http' - host = 'www' - domain = 'example.com' - expected_url = '{}://{}.{}\n'.format(protocol, host, domain) - - with patch('sys.stdout', new=StringIO()) as fake_out: - mymodule.urlprint(protocol, host, domain) - self.assertEqual(fake_out.getvalue(), expected_url) - -if __name__ == '__main__': - import unittest - unittest.main() diff --git a/chef/cookbooks/python/src/15/Makefile b/chef/cookbooks/python/src/15/Makefile deleted file mode 100644 index b0ec491..0000000 --- a/chef/cookbooks/python/src/15/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -osx:: - gcc -shared -undefined dynamic_lookup sample.c -o libsample.so diff --git a/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/example.py b/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/example.py deleted file mode 100644 index a15af47..0000000 --- a/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/example.py +++ /dev/null @@ -1,9 +0,0 @@ -import sample -print(sample.gcd(35,42)) -print(sample.in_mandel(0,0,500)) -print(sample.in_mandel(2.0,1.0,500)) -print(sample.divide(42,8)) -print(sample.avg([1,2,3])) -p1 = sample.Point(1,2) -p2 = sample.Point(4,5) -print(sample.distance(p1,p2)) diff --git a/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/sample.py b/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/sample.py deleted file mode 100644 index 86fd9c2..0000000 --- a/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/sample.py +++ /dev/null @@ -1,77 +0,0 @@ -# sample.py -import ctypes -import os - -# .so file is located in the directory above. See Makefile for -# build instructions -_path = '../libsample.so' -_mod = ctypes.cdll.LoadLibrary(_path) - -# int gcd(int, int) -gcd = _mod.gcd -gcd.argtypes = (ctypes.c_int, ctypes.c_int) -gcd.restype = ctypes.c_int - -# int in_mandel(double, double, int) -in_mandel = _mod.in_mandel -in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int) -in_mandel.restype = ctypes.c_int - -# int divide(int, int, int *) -_divide = _mod.divide -_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int)) -_divide.restype = ctypes.c_int - -def divide(x, y): - rem = ctypes.c_int() - quot = _divide(x,y,rem) - return quot,rem.value - -# void avg(double *, int n) - -# Define a special type for the 'double *' argument -class DoubleArrayType: - def from_param(self, param): - typename = type(param).__name__ - if hasattr(self, 'from_'+typename): - return getattr(self, 'from_'+typename)(param) - elif isinstance(param, ctypes.Array): - return param - else: - raise TypeError("Can't convert %s" % typename) - - # Cast from array.array objects - def from_array(self, param): - if param.typecode != 'd': - raise TypeError('must be an array of doubles') - ptr, _ = param.buffer_info() - return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double)) - - # Cast from lists/tuples - def from_list(self, param): - val = ((ctypes.c_double)*len(param))(*param) - return val - - from_tuple = from_list - - # Cast from a numpy array - def from_ndarray(self, param): - return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) - -DoubleArray = DoubleArrayType() -_avg = _mod.avg -_avg.argtypes = (DoubleArray, ctypes.c_int) -_avg.restype = ctypes.c_double - -def avg(values): - return _avg(values, len(values)) - -# struct Point { } -class Point(ctypes.Structure): - _fields_ = [('x', ctypes.c_double), - ('y', ctypes.c_double)] - -# double distance(Point *, Point *) -distance = _mod.distance -distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point)) -distance.restype = ctypes.c_double diff --git a/chef/cookbooks/python/src/15/calling_python_from_c/Makefile b/chef/cookbooks/python/src/15/calling_python_from_c/Makefile deleted file mode 100644 index 5599aa9..0000000 --- a/chef/cookbooks/python/src/15/calling_python_from_c/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -all:: - cc -g embed.c -I/usr/local/include/python3.3m \ - -L/usr/local/lib/python3.3/config-3.3m -lpython3.3m diff --git a/chef/cookbooks/python/src/15/calling_python_from_c/embed.c b/chef/cookbooks/python/src/15/calling_python_from_c/embed.c deleted file mode 100644 index 2523a95..0000000 --- a/chef/cookbooks/python/src/15/calling_python_from_c/embed.c +++ /dev/null @@ -1,84 +0,0 @@ -#include - -/* Execute func(x,y) in the Python interpreter. The - arguments and return result of the function must - be Python floats */ - -double call_func(PyObject *func, double x, double y) { - PyObject *args; - PyObject *kwargs; - PyObject *result = 0; - double retval; - - /* Make sure we own the GIL */ - PyGILState_STATE state = PyGILState_Ensure(); - - /* Verify that func is a proper callable */ - if (!PyCallable_Check(func)) { - fprintf(stderr,"call_func: expected a callable\n"); - goto fail; - } - /* Build arguments */ - args = Py_BuildValue("(dd)", x, y); - kwargs = NULL; - - /* Call the function */ - result = PyObject_Call(func, args, kwargs); - Py_DECREF(args); - Py_XDECREF(kwargs); - - /* Check for Python exceptions (if any) */ - if (PyErr_Occurred()) { - PyErr_Print(); - goto fail; - } - - /* Verify the result is a float object */ - if (!PyFloat_Check(result)) { - fprintf(stderr,"call_func: callable didn't return a float\n"); - goto fail; - } - - /* Create the return value */ - retval = PyFloat_AsDouble(result); - Py_DECREF(result); - - /* Restore previous GIL state and return */ - PyGILState_Release(state); - return retval; - -fail: - Py_XDECREF(result); - PyGILState_Release(state); - abort(); -} - - -/* Load a symbol from a module */ -PyObject *import_name(const char *modname, const char *symbol) { - PyObject *u_name, *module; - u_name = PyUnicode_FromString(modname); - module = PyImport_Import(u_name); - Py_DECREF(u_name); - return PyObject_GetAttrString(module, symbol); -} - -/* Simple embedding example */ -int main() { - PyObject *pow_func; - double x; - - Py_Initialize(); - /* Get a reference to the math.pow function */ - pow_func = import_name("math","pow"); - - /* Call it using our call_func() code */ - for (x = 0.0; x < 10.0; x += 0.1) { - printf("%0.2f %0.2f\n", x, call_func(pow_func,x,2.0)); - } - /* Done */ - Py_DECREF(pow_func); - Py_Finalize(); - return 0; -} - diff --git a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/example.py b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/example.py deleted file mode 100644 index 2954533..0000000 --- a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/example.py +++ /dev/null @@ -1,10 +0,0 @@ -import sample - -sample.consume_iterable([1,2,3,4]) - -def countdown(n): - while n > 0: - yield n - n -= 1 - -sample.consume_iterable(countdown(10)) diff --git a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/sample.c b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/sample.c deleted file mode 100644 index ebdba94..0000000 --- a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/sample.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "Python.h" - -static PyObject *py_consume_iterable(PyObject *self, PyObject *args) { - PyObject *obj; - PyObject *iter; - PyObject *item; - - if (!PyArg_ParseTuple(args, "O", &obj)) { - return NULL; - } - if ((iter = PyObject_GetIter(obj)) == NULL) { - return NULL; - } - while ((item = PyIter_Next(iter)) != NULL) { - /* Use item */ - PyObject_Print(item, stdout, 0); - printf("\n"); - Py_DECREF(item); - } - Py_DECREF(iter); - return Py_BuildValue(""); -} - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"consume_iterable", py_consume_iterable, METH_VARARGS}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/setup.py b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/setup.py deleted file mode 100644 index 1113893..0000000 --- a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["sample.c"], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/README.txt b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/README.txt deleted file mode 100644 index ea993e9..0000000 --- a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -To build, you need to perform these two steps: - -% python3 setup.py build_ext --inplace -% python3 ptsetup.py build_ext --inplace diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/example.py b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/example.py deleted file mode 100644 index 150772b..0000000 --- a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/example.py +++ /dev/null @@ -1,4 +0,0 @@ -import sample -import ptexample -p1 = sample.Point(2,3) -ptexample.print_point(p1) diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptexample.c b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptexample.c deleted file mode 100644 index c8b3bb0..0000000 --- a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptexample.c +++ /dev/null @@ -1,50 +0,0 @@ -/* ptexample.c */ - -/* Include the header associated with the other module */ -#include "pysample.h" - -/* An extension function that uses the exported API */ -static PyObject *print_point(PyObject *self, PyObject *args) { - PyObject *obj; - Point *p; - if (!PyArg_ParseTuple(args,"O", &obj)) { - return NULL; - } - - /* Note: This is defined in a different module */ - p = PyPoint_AsPoint(obj); - if (!p) { - return NULL; - } - printf("%f %f\n", p->x, p->y); - return Py_BuildValue(""); -} - -static PyMethodDef PtExampleMethods[] = { - {"print_point", print_point, METH_VARARGS, "output a point"}, - { NULL, NULL, 0, NULL} -}; - -static struct PyModuleDef ptexamplemodule = { - PyModuleDef_HEAD_INIT, - "ptexample", /* name of module */ - "A module that imports an API", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - PtExampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_ptexample(void) { - PyObject *m; - - m = PyModule_Create(&ptexamplemodule); - if (m == NULL) - return NULL; - - /* Import sample, loading its API functions */ - if (!import_sample()) { - return NULL; - } - return m; -} diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptsetup.py b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptsetup.py deleted file mode 100644 index 5339d09..0000000 --- a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptsetup.py +++ /dev/null @@ -1,11 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="ptexample", - ext_modules=[ - Extension("ptexample", - ["ptexample.c"], - include_dirs = ['..','.'], # May need pysample.h directory - ) - ] -) diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.c b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.c deleted file mode 100644 index 0bf1cef..0000000 --- a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "Python.h" -#define PYSAMPLE_MODULE -#include "pysample.h" - -/* Destructor function for points */ -static void del_Point(PyObject *obj) { - free(PyCapsule_GetPointer(obj,"Point")); -} - -/* Utility functions */ -static Point *PyPoint_AsPoint(PyObject *obj) { - return (Point *) PyCapsule_GetPointer(obj, "Point"); -} - -static PyObject *PyPoint_FromPoint(Point *p, int must_free) { - return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); -} - -/* Create a new Point object */ -static PyObject *py_Point(PyObject *self, PyObject *args) { - Point *p; - double x,y; - if (!PyArg_ParseTuple(args,"dd",&x,&y)) { - return NULL; - } - p = (Point *) malloc(sizeof(Point)); - p->x = x; - p->y = y; - return PyPoint_FromPoint(p, 1); -} - -static PyObject *py_distance(PyObject *self, PyObject *args) { - Point *p1, *p2; - PyObject *py_p1, *py_p2; - double result; - - if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) { - return NULL; - } - if (!(p1 = PyPoint_AsPoint(py_p1))) { - return NULL; - } - if (!(p2 = PyPoint_AsPoint(py_p2))) { - return NULL; - } - result = distance(p1,p2); - return Py_BuildValue("d", result); -} - -static _PointAPIMethods _point_api = { - PyPoint_AsPoint, - PyPoint_FromPoint -}; - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"Point", py_Point, METH_VARARGS, "Make a point"}, - {"distance", py_distance, METH_VARARGS, "Distance between points"}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - PyObject *m; - PyObject *py_point_api; - - m = PyModule_Create(&samplemodule); - if (m == NULL) - return NULL; - - /* Add the Point C API functions */ - py_point_api = PyCapsule_New((void *) &_point_api, "sample._point_api", NULL); - if (py_point_api) { - PyModule_AddObject(m, "_point_api", py_point_api); - } - return m; -} diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.h b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.h deleted file mode 100644 index 6d00e46..0000000 --- a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.h +++ /dev/null @@ -1,31 +0,0 @@ -/* pysample.h */ -#include "Python.h" -#include "sample.h" -#ifdef __cplusplus -extern "C" { -#endif - -/* Public API Table */ -typedef struct { - Point *(*aspoint)(PyObject *); - PyObject *(*frompoint)(Point *, int); -} _PointAPIMethods; - -#ifndef PYSAMPLE_MODULE -/* Method table in external module */ -static _PointAPIMethods *_point_api = 0; - -/* Import the API table from sample */ -static int import_sample(void) { - _point_api = (_PointAPIMethods *) PyCapsule_Import("sample._point_api",0); - return (_point_api != NULL) ? 1 : 0; -} - -/* Macros to implement the programming interface */ -#define PyPoint_AsPoint(obj) (_point_api->aspoint)(obj) -#define PyPoint_FromPoint(obj) (_point_api->frompoint)(obj) -#endif - -#ifdef __cplusplus -} -#endif diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/setup.py b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/setup.py deleted file mode 100644 index 5baf554..0000000 --- a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["../sample.c", "pysample.c"], - include_dirs = ['..'], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/example.py b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/example.py deleted file mode 100644 index 6a8e3a6..0000000 --- a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/example.py +++ /dev/null @@ -1,20 +0,0 @@ -# example.py -import sample - -def foo(): - print('About to die') - sample.die() - -def bar(): - print('About to call the function that dies') - foo() - -def spam(): - print('About to call the function that calls the function that dies') - bar() - -if __name__ == '__main__': - import faulthandler - faulthandler.enable() - spam() - diff --git a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/sample.c b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/sample.c deleted file mode 100644 index 24bf6c5..0000000 --- a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/sample.c +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include - -static PyObject *py_die(PyObject *self, PyObject *args) { - char *s = 0; - - *s = 'x'; - Py_RETURN_NONE; -} - - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"die", py_die, METH_VARARGS}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/setup.py b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/setup.py deleted file mode 100644 index 1113893..0000000 --- a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["sample.c"], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/example.py b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/example.py deleted file mode 100644 index d6dcad7..0000000 --- a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/example.py +++ /dev/null @@ -1,9 +0,0 @@ -import sample -p1 = sample.Point(2, 3) -p2 = sample.Point(4, 5) -print(p1) -print(p2) -print(sample.distance(p1, p2)) -del p1 -del p2 -print('Done') diff --git a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/pysample.c b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/pysample.c deleted file mode 100644 index 4fad317..0000000 --- a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/pysample.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "Python.h" -#include "sample.h" - -/* Destructor function for points */ -static void del_Point(PyObject *obj) { - free(PyCapsule_GetPointer(obj,"Point")); -} - -/* Utility functions */ -static Point *PyPoint_AsPoint(PyObject *obj) { - return (Point *) PyCapsule_GetPointer(obj, "Point"); -} - -static PyObject *PyPoint_FromPoint(Point *p, int must_free) { - return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); -} - -/* Create a new Point object */ -static PyObject *py_Point(PyObject *self, PyObject *args) { - Point *p; - double x,y; - if (!PyArg_ParseTuple(args,"dd",&x,&y)) { - return NULL; - } - p = (Point *) malloc(sizeof(Point)); - p->x = x; - p->y = y; - return PyPoint_FromPoint(p, 1); -} - -static PyObject *py_distance(PyObject *self, PyObject *args) { - Point *p1, *p2; - PyObject *py_p1, *py_p2; - double result; - - if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) { - return NULL; - } - if (!(p1 = PyPoint_AsPoint(py_p1))) { - return NULL; - } - if (!(p2 = PyPoint_AsPoint(py_p2))) { - return NULL; - } - result = distance(p1,p2); - return Py_BuildValue("d", result); -} - - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"Point", py_Point, METH_VARARGS, "Make a point"}, - {"distance", py_distance, METH_VARARGS, "Distance between points"}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/setup.py b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/setup.py deleted file mode 100644 index 5baf554..0000000 --- a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["../sample.c", "pysample.c"], - include_dirs = ['..'], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/example.py b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/example.py deleted file mode 100644 index 106c785..0000000 --- a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/example.py +++ /dev/null @@ -1,15 +0,0 @@ -import sample -import sys - -sample.print_chars(b'hello world') - -s = 'Spicy Jalape\u00f1o' -print(sys.getsizeof(s)) -sample.print_chars_str(s) -print(sys.getsizeof(s)) -del s - -s = 'spicy Jalape\u00f1o' -print(sys.getsizeof(s)) -sample.print_chars_str_alt(s) -print(sys.getsizeof(s)) diff --git a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/sample.c b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/sample.c deleted file mode 100644 index e8e9caa..0000000 --- a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/sample.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include - -void print_chars(char *s) { - while (*s) { - printf("%2x ", (unsigned char) *s); - s++; - } - printf("\n"); -} - -static PyObject *py_print_chars(PyObject *self, PyObject *args) { - char *s; - - if (!PyArg_ParseTuple(args, "y", &s)) { - return NULL; - } - print_chars(s); - Py_RETURN_NONE; -} - -static PyObject *py_print_chars_str(PyObject *self, PyObject *args) { - char *s; - - if (!PyArg_ParseTuple(args, "s", &s)) { - return NULL; - } - print_chars(s); - Py_RETURN_NONE; -} - -static PyObject *py_print_chars_str_alt(PyObject *self, PyObject *args) { - PyObject *o, *bytes; - char *s; - - if (!PyArg_ParseTuple(args, "U", &o)) { - return NULL; - } - bytes = PyUnicode_AsUTF8String(o); - s = PyBytes_AsString(bytes); - print_chars(s); - Py_DECREF(bytes); - Py_RETURN_NONE; -} - - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"print_chars", py_print_chars, METH_VARARGS}, - {"print_chars_str", py_print_chars_str, METH_VARARGS}, - {"print_chars_str_alt", py_print_chars_str_alt, METH_VARARGS}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/setup.py b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/setup.py deleted file mode 100644 index 1113893..0000000 --- a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["sample.c"], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/example.py b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/example.py deleted file mode 100644 index 6253f7e..0000000 --- a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/example.py +++ /dev/null @@ -1,4 +0,0 @@ -import sample -s = "Spicy Jalape\u00f1o" -sample.print_chars(s) -sample.print_wchars(s) diff --git a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/sample.c b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/sample.c deleted file mode 100644 index 43ea23b..0000000 --- a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/sample.c +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -void print_chars(char *s, int len) { - int n = 0; - while (n < len) { - printf("%2x ", (unsigned char) s[n]); - n++; - } - printf("\n"); -} - -void print_wchars(wchar_t *s, int len) { - int n = 0; - while (n < len) { - printf("%x ", s[n]); - n++; - } - printf("\n"); -} - -static PyObject *py_print_chars(PyObject *self, PyObject *args) { - char *s; - Py_ssize_t len; - - if (!PyArg_ParseTuple(args, "s#", &s, &len)) { - return NULL; - } - print_chars(s, len); - Py_RETURN_NONE; -} - -static PyObject *py_print_wchars(PyObject *self, PyObject *args) { - wchar_t *s; - Py_ssize_t len; - - if (!PyArg_ParseTuple(args, "u#", &s, &len)) { - return NULL; - } - print_wchars(s,len); - Py_RETURN_NONE; -} - - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"print_chars", py_print_chars, METH_VARARGS}, - {"print_wchars", py_print_wchars, METH_VARARGS}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/setup.py b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/setup.py deleted file mode 100644 index 1113893..0000000 --- a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["sample.c"], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/example.py b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/example.py deleted file mode 100644 index 318c4cc..0000000 --- a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/example.py +++ /dev/null @@ -1,6 +0,0 @@ -f = open('sample.c') -import sample -sample.consume_file(f) -f.close() - -print('**** DONE') diff --git a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/sample.c b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/sample.c deleted file mode 100644 index ae2b8e4..0000000 --- a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/sample.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "Python.h" - -#define CHUNK_SIZE 8192 - -/* Consume a "file-like" object and write bytes to stdout */ -static PyObject *py_consume_file(PyObject *self, PyObject *args) { - PyObject *obj; - PyObject *read_meth; - PyObject *result = NULL; - PyObject *read_args; - - if (!PyArg_ParseTuple(args,"O", &obj)) { - return NULL; - } - - /* Get the read method of the passed object */ - if ((read_meth = PyObject_GetAttrString(obj, "read")) == NULL) { - return NULL; - } - - /* Build the argument list to read() */ - read_args = Py_BuildValue("(i)", CHUNK_SIZE); - while (1) { - PyObject *data; - PyObject *enc_data; - char *buf; - Py_ssize_t len; - - /* Call read() */ - if ((data = PyObject_Call(read_meth, read_args, NULL)) == NULL) { - goto final; - } - - /* Check for EOF */ - if (PySequence_Length(data) == 0) { - Py_DECREF(data); - break; - } - - /* Encode Unicode as Bytes for C */ - if ((enc_data = PyUnicode_AsEncodedString(data, "utf-8", "strict")) == NULL) { - Py_DECREF(data); - goto final; - } - - /* Extract underlying buffer data */ - PyBytes_AsStringAndSize(enc_data, &buf, &len); - - /* Write to stdout (replace with something more useful) */ - write(1, buf, len); - - /* Cleanup */ - Py_DECREF(enc_data); - Py_DECREF(data); - } - result = Py_BuildValue(""); - - final: - /* Cleanup */ - Py_DECREF(read_meth); - Py_DECREF(read_args); - return result; -} - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"consume_file", py_consume_file, METH_VARARGS}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/setup.py b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/setup.py deleted file mode 100644 index 1113893..0000000 --- a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["sample.c"], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/sample.c b/chef/cookbooks/python/src/15/sample.c deleted file mode 100644 index aff2306..0000000 --- a/chef/cookbooks/python/src/15/sample.c +++ /dev/null @@ -1,53 +0,0 @@ -/* sample.c */ -#include - -/* Compute the greatest common divisor */ -int gcd(int x, int y) { - int g = y; - while (x > 0) { - g = x; - x = y % x; - y = g; - } - return g; -} - -/* Test if (x0,y0) is in the Mandelbrot set or not */ -int in_mandel(double x0, double y0, int n) { - double x=0,y=0,xtemp; - while (n > 0) { - xtemp = x*x - y*y + x0; - y = 2*x*y + y0; - x = xtemp; - n -= 1; - if (x*x + y*y > 4) return 0; - } - return 1; -} - -/* Divide two numbers */ -int divide(int a, int b, int *remainder) { - int quot = a / b; - *remainder = a % b; - return quot; -} - -/* Average values in an array */ -double avg(double *a, int n) { - int i; - double total = 0.0; - for (i = 0; i < n; i++) { - total += a[i]; - } - return total / n; -} - -/* A C data structure */ -typedef struct Point { - double x,y; -} Point; - -/* Function involving a C data structure */ -double distance(Point *p1, Point *p2) { - return hypot(p1->x - p2->x, p1->y - p2->y); -} diff --git a/chef/cookbooks/python/src/15/sample.h b/chef/cookbooks/python/src/15/sample.h deleted file mode 100644 index dd42a62..0000000 --- a/chef/cookbooks/python/src/15/sample.h +++ /dev/null @@ -1,12 +0,0 @@ -/* sample.h */ - -extern int gcd(int x, int y); -extern int in_mandel(double x0, double y0, int n); -extern int divide(int a, int b, int *remainder); -extern double avg(double *a, int n); - -typedef struct Point { - double x,y; -} Point; - -extern double distance(Point *p1, Point *p2); diff --git a/chef/cookbooks/python/src/15/turning_a_function_pointer_into_a_callable/example.py b/chef/cookbooks/python/src/15/turning_a_function_pointer_into_a_callable/example.py deleted file mode 100644 index be2aa77..0000000 --- a/chef/cookbooks/python/src/15/turning_a_function_pointer_into_a_callable/example.py +++ /dev/null @@ -1,16 +0,0 @@ -import ctypes -lib = ctypes.cdll.LoadLibrary(None) - -# Get the address of sin() from the C math library -addr = ctypes.cast(lib.sin, ctypes.c_void_p).value -print(addr) -140735505915760 - -# Turn the address into a callable function -functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double) -func = functype(addr) -print(func) - -# Call the resulting function -print(func(2)) -print(func(0)) diff --git a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/example.py b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/example.py deleted file mode 100644 index a231e46..0000000 --- a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/example.py +++ /dev/null @@ -1,34 +0,0 @@ -# array module example -import sample -import array -a = array.array('d',[1,-3,4,7,2,0]) -print(a) -sample.clip(a,1,4,a) -print(a) - -# numpy example -import numpy -b = numpy.random.uniform(-10,10,size=1000000) -print(b) -c = numpy.zeros_like(b) -print(c) -sample.clip(b,-5,5,c) -print(c) -print(min(c)) -print(max(c)) - -# Timing test -from timeit import timeit -print('numpy.clip') -print(timeit('numpy.clip(b,-5,5,c)', 'from __main__ import b,c,numpy', number=1000)) -print('sample.clip') -print(timeit('sample.clip(b,-5,5,c)', 'from __main__ import b,c,sample', number=1000)) - -print('sample.clip_fast') -print(timeit('sample.clip_fast(b,-5,5,c)', 'from __main__ import b,c,sample', number=1000)) - -# 2D test -d = numpy.random.uniform(-10,10,size=(1000,1000)) -print(d) -sample.clip2d(d, -5, 5, d) -print(d) diff --git a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/sample.pyx b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/sample.pyx deleted file mode 100644 index 2bbbaaa..0000000 --- a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/sample.pyx +++ /dev/null @@ -1,46 +0,0 @@ -cimport cython - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef clip(double[:] a, double min, double max, double[:] out): - ''' - Clip the values in a to be between min and max. Result in out - ''' - if min > max: - raise ValueError("min must be <= max") - if a.shape[0] != out.shape[0]: - raise ValueError("input and output arrays must be the same size") - for i in range(a.shape[0]): - if a[i] < min: - out[i] = min - elif a[i] > max: - out[i] = max - else: - out[i] = a[i] - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef clip_fast(double[:] a, double min, double max, double[:] out): - if min > max: - raise ValueError("min must be <= max") - if a.shape[0] != out.shape[0]: - raise ValueError("input and output arrays must be the same size") - for i in range(a.shape[0]): - out[i] = (a[i] if a[i] < max else max) if a[i] > min else min - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef clip2d(double[:,:] a, double min, double max, double[:,:] out): - if min > max: - raise ValueError("min must be <= max") - for n in range(a.ndim): - if a.shape[n] != out.shape[n]: - raise TypeError("a and out have different shapes") - for i in range(a.shape[0]): - for j in range(a.shape[1]): - if a[i,j] < min: - out[i,j] = min - elif a[i,j] > max: - out[i,j] = max - else: - out[i,j] = a[i,j] diff --git a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/setup.py b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/setup.py deleted file mode 100644 index 01e1bae..0000000 --- a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/setup.py +++ /dev/null @@ -1,15 +0,0 @@ - -from distutils.core import setup -from distutils.extension import Extension -from Cython.Distutils import build_ext - -ext_modules = [ - Extension("sample", - ["sample.pyx"]) -] - -setup( - name = 'Sample app', - cmdclass = {'build_ext': build_ext}, - ext_modules = ext_modules -) diff --git a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/example.py b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/example.py deleted file mode 100644 index f021321..0000000 --- a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/example.py +++ /dev/null @@ -1,4 +0,0 @@ -import sample -s = sample.retstr() -print(repr(s)) -sample.print_chars(s) diff --git a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/sample.c b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/sample.c deleted file mode 100644 index ed0181b..0000000 --- a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/sample.c +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include -/* Some dubious string data (malformed UTF-8) */ -const char *sdata = "Spicy Jalape\xc3\xb1o\xae"; -int slen = 16; - -/* Output character data */ -void print_chars(char *s, int len) { - int n = 0; - while (n < len) { - printf("%2x ", (unsigned char) s[n]); - n++; - } - printf("\n"); -} - -/* Return the C string back to Python */ -static PyObject *py_retstr(PyObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) { - return NULL; - } - return PyUnicode_Decode(sdata, slen, "utf-8", "surrogateescape"); -} - -/* Wrapper for the print_chars() function */ -static PyObject *py_print_chars(PyObject *self, PyObject *args) { - PyObject *obj, *bytes; - char *s = 0; - Py_ssize_t len; - - if (!PyArg_ParseTuple(args, "U", &obj)) { - return NULL; - } - - if ((bytes = PyUnicode_AsEncodedString(obj,"utf-8","surrogateescape")) == NULL) { - return NULL; - } - PyBytes_AsStringAndSize(bytes, &s, &len); - print_chars(s, len); - Py_DECREF(bytes); - Py_RETURN_NONE; -} - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"print_chars", py_print_chars, METH_VARARGS}, - {"retstr", py_retstr, METH_VARARGS}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/setup.py b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/setup.py deleted file mode 100644 index 1113893..0000000 --- a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["sample.c"], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/example.py b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/example.py deleted file mode 100644 index 64b6190..0000000 --- a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/example.py +++ /dev/null @@ -1,12 +0,0 @@ -import sample -print(sample.gcd(42, 8)) -print(sample.divide(42, 8)) -p1 = sample.Point(2, 3) -p2 = sample.Point(4, 5) -print(sample.distance(p1, p2)) -print(p1.x) -print(p1.y) - -import array -a = array.array('d', [1, 2, 3]) -print(sample.avg(a)) diff --git a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/sample.i b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/sample.i deleted file mode 100644 index 0407dac..0000000 --- a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/sample.i +++ /dev/null @@ -1,53 +0,0 @@ -// sample.i - Swig interface -%module sample -%{ -#include "sample.h" -%} - -/* Customizations */ -%extend Point { - /* Constructor for Point objects */ - Point(double x, double y) { - Point *p = (Point *) malloc(sizeof(Point)); - p->x = x; - p->y = y; - return p; - }; -}; - -/* Map int *remainder as an output argument */ -%include typemaps.i -%apply int *OUTPUT { int * remainder }; - -/* Map the argument pattern (double *a, int n) to arrays */ -%typemap(in) (double *a, int n)(Py_buffer view) { - view.obj = NULL; - if (PyObject_GetBuffer($input, &view, PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) { - SWIG_fail; - } - if (strcmp(view.format,"d") != 0) { - PyErr_SetString(PyExc_TypeError, "Expected an array of doubles"); - SWIG_fail; - } - $1 = (double *) view.buf; - $2 = view.len / sizeof(double); -} - -%typemap(freearg) (double *a, int n) { - if (view$argnum.obj) { - PyBuffer_Release(&view$argnum); - } -} - -/* C declarations to be included in the extension module */ - -extern int gcd(int, int); -extern int in_mandel(double x0, double y0, int n); -extern int divide(int a, int b, int *remainder); -extern double avg(double *a, int n); - -typedef struct Point { - double x,y; -} Point; - -extern double distance(Point *p1, Point *p2); diff --git a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/setup.py b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/setup.py deleted file mode 100644 index 49b5c5e..0000000 --- a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name='sample', - py_modules=['sample.py'], - ext_modules=[ - Extension('_sample', - ['../sample.c', 'sample_wrap.c'], - include_dirs = ['..'], - define_macros = [], - undef_macros = [], - library_dirs = [], - libraries = [] - ) - ] -) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/csample.pxd b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/csample.pxd deleted file mode 100644 index 981c461..0000000 --- a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/csample.pxd +++ /dev/null @@ -1,15 +0,0 @@ -# csample.pxd -# -# Declarations of "external" C functions and structures - -cdef extern from "sample.h": - int gcd(int, int) - bint in_mandel(double, double, int) - int divide(int, int, int *) - double avg(double *, int) nogil - - ctypedef struct Point: - double x - double y - - double distance(Point *, Point *) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/example.py b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/example.py deleted file mode 100644 index 6db2ea6..0000000 --- a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/example.py +++ /dev/null @@ -1,12 +0,0 @@ -import sample -print(sample.gcd(42, 8)) -print(sample.divide(42, 8)) -p1 = sample.Point(2, 3) -p2 = sample.Point(4, 5) -print(p1) -print(p2) -print(sample.distance(p1, p2)) - -import array -a = array.array('d', [1, 2, 3]) -print(sample.avg(a)) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample.pyx b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample.pyx deleted file mode 100644 index c99a665..0000000 --- a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample.pyx +++ /dev/null @@ -1,50 +0,0 @@ -# sample.pyx - -# Import the low-level C declarations -cimport csample - -# Import some functionality from Python and the C stdlib -from cpython.pycapsule cimport * -from libc.stdlib cimport malloc, free - -# Wrappers -def gcd(unsigned int x, unsigned int y): - return csample.gcd(x,y) - -def in_mandel(x,y,unsigned int n): - return csample.in_mandel(x,y,n) - -def divide(x,y): - cdef int rem - quot = csample.divide(x,y,&rem) - return quot, rem - -def avg(double[:] a): - cdef: - int sz - double result - - sz = a.size - with nogil: - result = csample.avg( &a[0], sz) - return result - -# Destructor for cleaning up Point objects -cdef del_Point(object obj): - pt = PyCapsule_GetPointer(obj,"Point") - free( pt) - -# Create a Point object and return as a capsule -def Point(double x,double y): - cdef csample.Point *p - p = malloc(sizeof(csample.Point)) - if p == NULL: - raise MemoryError("No memory to make a Point") - p.x = x - p.y = y - return PyCapsule_New(p,"Point",del_Point) - -def distance(p1, p2): - pt1 = PyCapsule_GetPointer(p1,"Point") - pt2 = PyCapsule_GetPointer(p2,"Point") - return csample.distance(pt1,pt2) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample_alt.pyx b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample_alt.pyx deleted file mode 100644 index b4f394c..0000000 --- a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample_alt.pyx +++ /dev/null @@ -1,54 +0,0 @@ -# sample_alt.pyx - -# Import the low-level C declarations -cimport csample - -# Import some functionality from Python and the C stdlib -from libc.stdlib cimport malloc, free - -# Wrappers -def gcd(unsigned int x, unsigned int y): - return csample.gcd(x,y) - -def in_mandel(x,y,unsigned int n): - return csample.in_mandel(x,y,n) - -def divide(x,y): - cdef int rem - quot = csample.divide(x,y,&rem) - return quot, rem - -def avg(double[:] a): - cdef: - int sz - double result - - sz = a.size - with nogil: - result = csample.avg( &a[0], sz) - return result - -cdef class Point: - cdef csample.Point *_c_point - def __cinit__(self, double x, double y): - self._c_point = malloc(sizeof(csample.Point)) - self._c_point.x = x - self._c_point.y = y - - def __dealloc__(self): - free(self._c_point) - - property x: - def __get__(self): - return self._c_point.x - def __set__(self, value): - self._c_point.x = value - - property y: - def __get__(self): - return self._c_point.y - def __set__(self, value): - self._c_point.y = value - -def distance(Point p1, Point p2): - return csample.distance(p1._c_point, p2._c_point) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup.py b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup.py deleted file mode 100644 index 9c3369e..0000000 --- a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup.py +++ /dev/null @@ -1,15 +0,0 @@ -from distutils.core import setup -from distutils.extension import Extension -from Cython.Distutils import build_ext - -ext_modules = [ - Extension("sample", - ["sample.pyx"], - include_dirs=['..'], - libraries=['sample'], - library_dirs=['..'])] -setup( - name = 'Sample extension module', - cmdclass = {'build_ext': build_ext}, - ext_modules = ext_modules -) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup_alt.py b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup_alt.py deleted file mode 100644 index 7f22eb2..0000000 --- a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup_alt.py +++ /dev/null @@ -1,15 +0,0 @@ -from distutils.core import setup -from distutils.extension import Extension -from Cython.Distutils import build_ext - -ext_modules = [ - Extension("sample", - ["sample_alt.pyx"], - include_dirs=['..'], - libraries=['sample'], - library_dirs=['..'])] -setup( - name = 'Sample extension module', - cmdclass = {'build_ext': build_ext}, - ext_modules = ext_modules -) diff --git a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/example.py b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/example.py deleted file mode 100644 index da1f5b0..0000000 --- a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/example.py +++ /dev/null @@ -1,5 +0,0 @@ -import sample -print(sample.gcd(35,42)) -print(sample.in_mandel(0,0,500)) -print(sample.in_mandel(2.0,1.0,500)) -print(sample.divide(42,8)) diff --git a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/pysample.c b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/pysample.c deleted file mode 100644 index 02b01b6..0000000 --- a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/pysample.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "Python.h" -#include "sample.h" - -/* int gcd(int, int) */ -static PyObject *py_gcd(PyObject *self, PyObject *args) { - int x, y, result; - - if (!PyArg_ParseTuple(args,"ii", &x, &y)) { - return NULL; - } - result = gcd(x,y); - return Py_BuildValue("i", result); -} - -/* int in_mandel(double, double, int) */ -static PyObject *py_in_mandel(PyObject *self, PyObject *args) { - double x0, y0; - int n; - int result; - - if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) { - return NULL; - } - result = in_mandel(x0,y0,n); - return Py_BuildValue("i", result); -} - -/* int divide(int, int, int *) */ -static PyObject *py_divide(PyObject *self, PyObject *args) { - int a, b, quotient, remainder; - if (!PyArg_ParseTuple(args, "ii", &a, &b)) { - return NULL; - } - quotient = divide(a,b, &remainder); - return Py_BuildValue("(ii)", quotient, remainder); -} - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"}, - {"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"}, - {"divide", py_divide, METH_VARARGS, "Integer division"}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/setup.py b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/setup.py deleted file mode 100644 index 5baf554..0000000 --- a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["../sample.c", "pysample.c"], - include_dirs = ['..'], - ) - ] -) diff --git a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/example.py b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/example.py deleted file mode 100644 index 9f9ddc5..0000000 --- a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/example.py +++ /dev/null @@ -1,10 +0,0 @@ -import array -from sample import avg - -print(avg(array.array('d',[1,2,3]))) -try: - import numpy - print(avg(numpy.array([1., 2., 3.]))) -except ImportError: - pass - diff --git a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/pysample.c b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/pysample.c deleted file mode 100644 index 5f29b62..0000000 --- a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/pysample.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "Python.h" -#include "sample.h" - -/* Call double avg(double *, int) */ -static PyObject *py_avg(PyObject *self, PyObject *args) { - PyObject *bufobj; - Py_buffer view; - double result; - /* Get the passed Python object */ - if (!PyArg_ParseTuple(args, "O", &bufobj)) { - return NULL; - } - - /* Attempt to extract buffer information from it */ - if (PyObject_GetBuffer(bufobj, &view, PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) { - return NULL; - } - - if (view.ndim != 1) { - PyErr_SetString(PyExc_TypeError, "Expected a 1-dimensional array"); - PyBuffer_Release(&view); - return NULL; - } - - /* Check the type of items in the array */ - if (strcmp(view.format,"d") != 0) { - PyErr_SetString(PyExc_TypeError, "Expected an array of doubles"); - PyBuffer_Release(&view); - return NULL; - } - - /* Pass the raw buffer and size to the C function */ - result = avg(view.buf, view.shape[0]); - - /* Indicate we're done working with the buffer */ - PyBuffer_Release(&view); - return Py_BuildValue("d", result); -} - -/* Module method table */ -static PyMethodDef SampleMethods[] = { - {"avg", py_avg, METH_VARARGS, "Average"}, - { NULL, NULL, 0, NULL} -}; - -/* Module structure */ -static struct PyModuleDef samplemodule = { - PyModuleDef_HEAD_INIT, - "sample", /* name of module */ - "A sample module", /* Doc string (may be NULL) */ - -1, /* Size of per-interpreter state or -1 */ - SampleMethods /* Method table */ -}; - -/* Module initialization function */ -PyMODINIT_FUNC -PyInit_sample(void) { - return PyModule_Create(&samplemodule); -} diff --git a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/setup.py b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/setup.py deleted file mode 100644 index 5baf554..0000000 --- a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -# setup.py -from distutils.core import setup, Extension - -setup(name="sample", - ext_modules=[ - Extension("sample", - ["../sample.c", "pysample.c"], - include_dirs = ['..'], - ) - ] -) diff --git a/chef/cookbooks/python/src/2/combining_and_concatenating_strings/example.py b/chef/cookbooks/python/src/2/combining_and_concatenating_strings/example.py deleted file mode 100644 index dfd82e3..0000000 --- a/chef/cookbooks/python/src/2/combining_and_concatenating_strings/example.py +++ /dev/null @@ -1,38 +0,0 @@ -# example.py -# -# Example of combining text via generators - -def sample(): - yield "Is" - yield "Chicago" - yield "Not" - yield "Chicago?" - -# (a) Simple join operator -text = ''.join(sample()) -print(text) - -# (b) Redirection of parts to I/O -import sys -for part in sample(): - sys.stdout.write(part) -sys.stdout.write('\n') - -# (c) Combination of parts into buffers and larger I/O operations -def combine(source, maxsize): - parts = [] - size = 0 - for part in source: - parts.append(part) - size += len(part) - if size > maxsize: - yield ''.join(parts) - parts = [] - size = 0 - yield ''.join(parts) - -for part in combine(sample(), 32768): - sys.stdout.write(part) -sys.stdout.write('\n') - - diff --git a/chef/cookbooks/python/src/2/matching_and_searching_for_text_patterns_using_regular_expressions/example.py b/chef/cookbooks/python/src/2/matching_and_searching_for_text_patterns_using_regular_expressions/example.py deleted file mode 100644 index d351b84..0000000 --- a/chef/cookbooks/python/src/2/matching_and_searching_for_text_patterns_using_regular_expressions/example.py +++ /dev/null @@ -1,23 +0,0 @@ -# example.py -# -# Examples of simple regular expression matching - -import re - -# Some sample text -text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' - -# (a) Find all matching dates -datepat = re.compile(r'\d+/\d+/\d+') -print(datepat.findall(text)) - -# (b) Find all matching dates with capture groups -datepat = re.compile(r'(\d+)/(\d+)/(\d+)') -for month, day, year in datepat.findall(text): - print('{}-{}-{}'.format(year, month, day)) - -# (c) Iterative search -for m in datepat.finditer(text): - print(m.groups()) - - diff --git a/chef/cookbooks/python/src/2/matching_strings_using_shell_wildcard_patterns/example.py b/chef/cookbooks/python/src/2/matching_strings_using_shell_wildcard_patterns/example.py deleted file mode 100644 index 6e5d0e7..0000000 --- a/chef/cookbooks/python/src/2/matching_strings_using_shell_wildcard_patterns/example.py +++ /dev/null @@ -1,19 +0,0 @@ -# example.py -# -# Example of using shell-wildcard style matching in list comprehensions - -from fnmatch import fnmatchcase as match - -addresses = [ - '5412 N CLARK ST', - '1060 W ADDISON ST', - '1039 W GRANVILLE AVE', - '2122 N CLARK ST', - '4802 N BROADWAY', -] - -a = [addr for addr in addresses if match(addr, '* ST')] -print(a) - -b = [addr for addr in addresses if match(addr, '54[0-9][0-9] *CLARK*')] -print(b) diff --git a/chef/cookbooks/python/src/2/normalizing_unicode_text_to_a_standard_representation/example.py b/chef/cookbooks/python/src/2/normalizing_unicode_text_to_a_standard_representation/example.py deleted file mode 100644 index 49a61e9..0000000 --- a/chef/cookbooks/python/src/2/normalizing_unicode_text_to_a_standard_representation/example.py +++ /dev/null @@ -1,28 +0,0 @@ -# example.py -# -# Example of unicode normalization - -# Two strings -s1 = 'Spicy Jalape\u00f1o' -s2 = 'Spicy Jalapen\u0303o' - -# (a) Print them out (usually looks identical) -print(s1) -print(s2) - -# (b) Examine equality and length -print('s1 == s2', s1 == s2) -print(len(s1), len(s2)) - -# (c) Normalize and try the same experiment -import unicodedata - -n_s1 = unicodedata.normalize('NFC', s1) -n_s2 = unicodedata.normalize('NFC', s2) - -print('n_s1 == n_s2', n_s1 == n_s2) -print(len(n_s1), len(n_s2)) - -# (d) Example of normalizing to a decomposed form and stripping accents -t1 = unicodedata.normalize('NFD', s1) -print(''.join(c for c in t1 if not unicodedata.combining(c))) diff --git a/chef/cookbooks/python/src/2/reformatting_text_to_fixed_number_of_columns/example.py b/chef/cookbooks/python/src/2/reformatting_text_to_fixed_number_of_columns/example.py deleted file mode 100644 index 3cfcea8..0000000 --- a/chef/cookbooks/python/src/2/reformatting_text_to_fixed_number_of_columns/example.py +++ /dev/null @@ -1,23 +0,0 @@ -# example.py -# -# Examples of reformatting text to different column widths - -# A long string -s = "Look into my eyes, look into my eyes, the eyes, the eyes, \ -the eyes, not around the eyes, don't look around the eyes, \ -look into my eyes, you're under." - -import textwrap - -print(textwrap.fill(s, 70)) -print() - -print(textwrap.fill(s, 40)) -print() - -print(textwrap.fill(s, 40, initial_indent=' ')) -print() - -print(textwrap.fill(s, 40, subsequent_indent=' ')) -print() - diff --git a/chef/cookbooks/python/src/2/sanitizing_and_cleaning_up_text/example.py b/chef/cookbooks/python/src/2/sanitizing_and_cleaning_up_text/example.py deleted file mode 100644 index 905e5aa..0000000 --- a/chef/cookbooks/python/src/2/sanitizing_and_cleaning_up_text/example.py +++ /dev/null @@ -1,31 +0,0 @@ -# example.py -# -# Example of some tricky sanitization problems - -# A tricky string -s = 'p\xfdt\u0125\xf6\xf1\x0cis\tawesome\r\n' -print(s) - -# (a) Remapping whitespace -remap = { - ord('\t') : ' ', - ord('\f') : ' ', - ord('\r') : None # Deleted -} - -a = s.translate(remap) -print('whitespace remapped:', a) - -# (b) Remove all combining characters/marks -import unicodedata -import sys -cmb_chrs = dict.fromkeys(c for c in range(sys.maxunicode) - if unicodedata.combining(chr(c))) - -b = unicodedata.normalize('NFD', a) -c = b.translate(cmb_chrs) -print('accents removed:', c) - -# (c) Accent removal using I/O decoding -d = b.encode('ascii','ignore').decode('ascii') -print('accents removed via I/O:', d) diff --git a/chef/cookbooks/python/src/2/searching_and_replacing_text/example.py b/chef/cookbooks/python/src/2/searching_and_replacing_text/example.py deleted file mode 100644 index 304c575..0000000 --- a/chef/cookbooks/python/src/2/searching_and_replacing_text/example.py +++ /dev/null @@ -1,22 +0,0 @@ -# example.py -# -# Examples of simple regular expression substitution - -import re - -# Some sample text -text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' - -datepat = re.compile(r'(\d+)/(\d+)/(\d+)') - -# (a) Simple substitution -print(datepat.sub(r'\3-\1-\2', text)) - -# (b) Replacement function -from calendar import month_abbr - -def change_date(m): - mon_name = month_abbr[int(m.group(1))] - return '{} {} {}'.format(m.group(2), mon_name, m.group(3)) - -print(datepat.sub(change_date, text)) diff --git a/chef/cookbooks/python/src/2/specifying_a_regular_expression_for_the_shortest_match/example.py b/chef/cookbooks/python/src/2/specifying_a_regular_expression_for_the_shortest_match/example.py deleted file mode 100644 index 4077c6b..0000000 --- a/chef/cookbooks/python/src/2/specifying_a_regular_expression_for_the_shortest_match/example.py +++ /dev/null @@ -1,19 +0,0 @@ -# example.py -# -# Example of a regular expression that finds shortest matches - -import re - -# Sample text -text = 'Computer says "no." Phone says "yes."' - -# (a) Regex that finds quoted strings - longest match -str_pat = re.compile(r'\"(.*)\"') -print(str_pat.findall(text)) - -# (b) Regex that finds quoted strings - shortest match -str_pat = re.compile(r'\"(.*?)\"') -print(str_pat.findall(text)) - - - diff --git a/chef/cookbooks/python/src/2/splitting_strings_on_any_of_multiple_delimiters/example.py b/chef/cookbooks/python/src/2/splitting_strings_on_any_of_multiple_delimiters/example.py deleted file mode 100644 index d752aee..0000000 --- a/chef/cookbooks/python/src/2/splitting_strings_on_any_of_multiple_delimiters/example.py +++ /dev/null @@ -1,29 +0,0 @@ -# example.py -# -# Example of splitting a string on multiple delimiters using a regex - -import re - -line = 'asdf fjdk; afed, fjek,asdf, foo' - -# (a) Splitting on space, comma, and semicolon -parts = re.split(r'[;,\s]\s*', line) -print(parts) - -# (b) Splitting with a capture group -fields = re.split(r'(;|,|\s)\s*', line) -print(fields) - -# (c) Rebuilding a string using fields above -values = fields[::2] -delimiters = fields[1::2] -delimiters.append('') -print('value =', values) -print('delimiters =', delimiters) -newline = ''.join(v+d for v,d in zip(values, delimiters)) -print('newline =', newline) - -# (d) Splitting using a non-capture group -parts = re.split(r'(?:,|;|\s)\s*', line) -print(parts) - diff --git a/chef/cookbooks/python/src/2/tokenizing_text/example.py b/chef/cookbooks/python/src/2/tokenizing_text/example.py deleted file mode 100644 index db31008..0000000 --- a/chef/cookbooks/python/src/2/tokenizing_text/example.py +++ /dev/null @@ -1,26 +0,0 @@ -# example.py -# -# Example of a tokenizer - -import re -from collections import namedtuple - -NAME = r'(?P[a-zA-Z_][a-zA-Z_0-9]*)' -NUM = r'(?P\d+)' -PLUS = r'(?P\+)' -TIMES = r'(?P\*)' -EQ = r'(?P=)' -WS = r'(?P\s+)' - -master_pat = re.compile('|'.join([NAME, NUM, PLUS, TIMES, EQ, WS])) - -Token = namedtuple('Token', ['type','value']) - -def generate_tokens(pat, text): - scanner = pat.scanner(text) - for m in iter(scanner.match, None): - yield Token(m.lastgroup, m.group()) - -for tok in generate_tokens(master_pat, 'foo = 42'): - print(tok) - diff --git a/chef/cookbooks/python/src/2/variable_interpolation_in_strings/example.py b/chef/cookbooks/python/src/2/variable_interpolation_in_strings/example.py deleted file mode 100644 index 47c83e1..0000000 --- a/chef/cookbooks/python/src/2/variable_interpolation_in_strings/example.py +++ /dev/null @@ -1,30 +0,0 @@ -# example.py -# -# Examples of variable interpolation - -# Class for performing safe substitutions -class safesub(dict): - def __missing__(self, key): - return '{%s}' % key - -s = '{name} has {n} messages.' - -# (a) Simple substitution -name = 'Guido' -n = 37 - -print(s.format_map(vars())) - -# (b) Safe substitution with missing values -del n -print(s.format_map(safesub(vars()))) - -# (c) Safe substitution + frame hack -n = 37 -import sys -def sub(text): - return text.format_map(safesub(sys._getframe(1).f_locals)) - -print(sub('Hello {name}')) -print(sub('{name} has {n} messages')) -print(sub('Your favorite color is {color}')) diff --git a/chef/cookbooks/python/src/2/writing_a_regular_expression_for_multiline_patterns/example.py b/chef/cookbooks/python/src/2/writing_a_regular_expression_for_multiline_patterns/example.py deleted file mode 100644 index 237fe09..0000000 --- a/chef/cookbooks/python/src/2/writing_a_regular_expression_for_multiline_patterns/example.py +++ /dev/null @@ -1,12 +0,0 @@ -# example.py -# -# Regular expression that matches multiline patterns - -import re - -text = '''/* this is a - multiline comment */ -''' - -comment = re.compile(r'/\*((?:.|\n)*?)\*/') -print(comment.findall(text)) diff --git a/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/example.py b/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/example.py deleted file mode 100644 index d401566..0000000 --- a/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/example.py +++ /dev/null @@ -1,158 +0,0 @@ -# example.py -# -# An example of writing a simple recursive descent parser - -import re -import collections - -# Token specification -NUM = r'(?P\d+)' -PLUS = r'(?P\+)' -MINUS = r'(?P-)' -TIMES = r'(?P\*)' -DIVIDE = r'(?P/)' -LPAREN = r'(?P\()' -RPAREN = r'(?P\))' -WS = r'(?P\s+)' - -master_pat = re.compile('|'.join([NUM, PLUS, MINUS, TIMES, - DIVIDE, LPAREN, RPAREN, WS])) - -# Tokenizer -Token = collections.namedtuple('Token', ['type','value']) - -def generate_tokens(text): - scanner = master_pat.scanner(text) - for m in iter(scanner.match, None): - tok = Token(m.lastgroup, m.group()) - if tok.type != 'WS': - yield tok - -# Parser -class ExpressionEvaluator: - ''' - Implementation of a recursive descent parser. Each method - implements a single grammar rule. Use the ._accept() method - to test and accept the current lookahead token. Use the ._expect() - method to exactly match and discard the next token on on the input - (or raise a SyntaxError if it doesn't match). - ''' - - def parse(self,text): - self.tokens = generate_tokens(text) - self.tok = None # Last symbol consumed - self.nexttok = None # Next symbol tokenized - self._advance() # Load first lookahead token - return self.expr() - - def _advance(self): - 'Advance one token ahead' - self.tok, self.nexttok = self.nexttok, next(self.tokens, None) - - def _accept(self,toktype): - 'Test and consume the next token if it matches toktype' - if self.nexttok and self.nexttok.type == toktype: - self._advance() - return True - else: - return False - - def _expect(self,toktype): - 'Consume next token if it matches toktype or raise SyntaxError' - if not self._accept(toktype): - raise SyntaxError('Expected ' + toktype) - - # Grammar rules follow - - def expr(self): - "expression ::= term { ('+'|'-') term }*" - - exprval = self.term() - while self._accept('PLUS') or self._accept('MINUS'): - op = self.tok.type - right = self.term() - if op == 'PLUS': - exprval += right - elif op == 'MINUS': - exprval -= right - return exprval - - def term(self): - "term ::= factor { ('*'|'/') factor }*" - - termval = self.factor() - while self._accept('TIMES') or self._accept('DIVIDE'): - op = self.tok.type - right = self.factor() - if op == 'TIMES': - termval *= right - elif op == 'DIVIDE': - termval /= right - return termval - - def factor(self): - "factor ::= NUM | ( expr )" - - if self._accept('NUM'): - return int(self.tok.value) - elif self._accept('LPAREN'): - exprval = self.expr() - self._expect('RPAREN') - return exprval - else: - raise SyntaxError('Expected NUMBER or LPAREN') - -if __name__ == '__main__': - e = ExpressionEvaluator() - print(e.parse('2')) - print(e.parse('2 + 3')) - print(e.parse('2 + 3 * 4')) - print(e.parse('2 + (3 + 4) * 5')) - -# Example of building trees - -class ExpressionTreeBuilder(ExpressionEvaluator): - def expr(self): - "expression ::= term { ('+'|'-') term }" - - exprval = self.term() - while self._accept('PLUS') or self._accept('MINUS'): - op = self.tok.type - right = self.term() - if op == 'PLUS': - exprval = ('+', exprval, right) - elif op == 'MINUS': - exprval = ('-', exprval, right) - return exprval - - def term(self): - "term ::= factor { ('*'|'/') factor }" - - termval = self.factor() - while self._accept('TIMES') or self._accept('DIVIDE'): - op = self.tok.type - right = self.factor() - if op == 'TIMES': - termval = ('*', termval, right) - elif op == 'DIVIDE': - termval = ('/', termval, right) - return termval - - def factor(self): - 'factor ::= NUM | ( expr )' - - if self._accept('NUM'): - return int(self.tok.value) - elif self._accept('LPAREN'): - exprval = self.expr() - self._expect('RPAREN') - return exprval - else: - raise SyntaxError('Expected NUMBER or LPAREN') - -if __name__ == '__main__': - e = ExpressionTreeBuilder() - print(e.parse('2 + 3')) - print(e.parse('2 + 3 * 4')) - print(e.parse('2 + (3 + 4) * 5')) - print(e.parse('2 + 3 + 4')) diff --git a/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/plyexample.py b/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/plyexample.py deleted file mode 100644 index 6d1ee3b..0000000 --- a/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/plyexample.py +++ /dev/null @@ -1,90 +0,0 @@ -# plyexample.py -# -# Example of parsing with PLY - -from ply.lex import lex -from ply.yacc import yacc - -# Token list -tokens = [ 'NUM', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'LPAREN', 'RPAREN' ] - -# Ignored characters - -t_ignore = ' \t\n' - -# Token specifications (as regexs) -t_PLUS = r'\+' -t_MINUS = r'-' -t_TIMES = r'\*' -t_DIVIDE = r'/' -t_LPAREN = r'\(' -t_RPAREN = r'\)' - -# Token processing functions -def t_NUM(t): - r'\d+' - t.value = int(t.value) - return t - -# Error handler -def t_error(t): - print('Bad character: {!r}'.format(t.value[0])) - t.skip(1) - -# Build the lexer -lexer = lex() - -# Grammar rules and handler functions -def p_expr(p): - ''' - expr : expr PLUS term - | expr MINUS term - ''' - if p[2] == '+': - p[0] = p[1] + p[3] - elif p[2] == '-': - p[0] = p[1] - p[3] - -def p_expr_term(p): - ''' - expr : term - ''' - p[0] = p[1] - -def p_term(p): - ''' - term : term TIMES factor - | term DIVIDE factor - ''' - if p[2] == '*': - p[0] = p[1] * p[3] - elif p[2] == '/': - p[0] = p[1] / p[3] - -def p_term_factor(p): - ''' - term : factor - ''' - p[0] = p[1] - -def p_factor(p): - ''' - factor : NUM - ''' - p[0] = p[1] - -def p_factor_group(p): - ''' - factor : LPAREN expr RPAREN - ''' - p[0] = p[2] - -def p_error(p): - print('Syntax error') - -parser = yacc() - -if __name__ == '__main__': - print(parser.parse('2')) - print(parser.parse('2+3')) - print(parser.parse('2+(3+4)*5')) diff --git a/chef/cookbooks/python/src/3/determining_last_fridays_date/example.py b/chef/cookbooks/python/src/3/determining_last_fridays_date/example.py deleted file mode 100644 index b4d6ebc..0000000 --- a/chef/cookbooks/python/src/3/determining_last_fridays_date/example.py +++ /dev/null @@ -1,15 +0,0 @@ -from datetime import datetime, timedelta - -weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] - -def get_previous_byday(dayname, start_date=None): - if start_date is None: - start_date = datetime.today() - day_num = start_date.weekday() - day_num_target = weekdays.index(dayname) - days_ago = (7 + day_num - day_num_target) % 7 - if days_ago == 0: - days_ago = 7 - target_date = start_date - timedelta(days=days_ago) - return target_date - diff --git a/chef/cookbooks/python/src/3/finding_the_date_range_for_the_current_month/example.py b/chef/cookbooks/python/src/3/finding_the_date_range_for_the_current_month/example.py deleted file mode 100644 index 2927384..0000000 --- a/chef/cookbooks/python/src/3/finding_the_date_range_for_the_current_month/example.py +++ /dev/null @@ -1,27 +0,0 @@ -from datetime import datetime, date, timedelta -import calendar - -def get_month_range(start_date=None): - if start_date is None: - start_date = date.today().replace(day=1) - days_in_month = calendar.monthrange(start_date.year, start_date.month)[1] - end_date = start_date + timedelta(days=days_in_month) - return (start_date, end_date) - -first_day, last_day = get_month_range() -a_day = timedelta(days=1) -while first_day < last_day: - print(first_day) - first_day += a_day - -def daterange(start, stop, step): - while start < stop: - yield start - start += step - -for d in daterange(date(2012, 8, 1), date(2012, 8, 11), timedelta(days=1)): - print(d) - -for d in daterange(datetime(2012, 8, 1), datetime(2012, 8, 3), timedelta(minutes=30)): - print(d) - diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/example.py b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/example.py deleted file mode 100644 index a7ff39a..0000000 --- a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/example.py +++ /dev/null @@ -1,63 +0,0 @@ -import os -import fnmatch -import gzip -import bz2 -import re - -def gen_find(filepat, top): - ''' - Find all filenames in a directory tree that match a shell wildcard pattern - ''' - for path, dirlist, filelist in os.walk(top): - for name in fnmatch.filter(filelist, filepat): - yield os.path.join(path,name) - -def gen_opener(filenames): - ''' - Open a sequence of filenames one at a time producing a file object. - The file is closed immediately when proceeding to the next iteration. - ''' - for filename in filenames: - if filename.endswith('.gz'): - f = gzip.open(filename, 'rt') - elif filename.endswith('.bz2'): - f = bz2.open(filename, 'rt') - else: - f = open(filename, 'rt') - yield f - f.close() - -def gen_concatenate(iterators): - ''' - Chain a sequence of iterators together into a single sequence. - ''' - for it in iterators: - yield from it - -def gen_grep(pattern, lines): - ''' - Look for a regex pattern in a sequence of lines - ''' - pat = re.compile(pattern) - for line in lines: - if pat.search(line): - yield line - -if __name__ == '__main__': - - # Example 1 - lognames = gen_find('access-log*', 'www') - files = gen_opener(lognames) - lines = gen_concatenate(files) - pylines = gen_grep('(?i)python', lines) - for line in pylines: - print(line) - - # Example 2 - lognames = gen_find('access-log*', 'www') - files = gen_opener(lognames) - lines = gen_concatenate(files) - pylines = gen_grep('(?i)python', lines) - bytecolumn = (line.rsplit(None,1)[1] for line in pylines) - bytes = (int(x) for x in bytecolumn if x != '-') - print('Total', sum(bytes)) diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log deleted file mode 100644 index deeb937..0000000 --- a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log +++ /dev/null @@ -1,7298 +0,0 @@ -140.180.132.213 - - [24/Feb/2008:00:08:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -140.180.132.213 - - [24/Feb/2008:00:08:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.54.118.139 - - [24/Feb/2008:00:15:40 -0600] "GET / HTTP/1.1" 200 4447 -75.54.118.139 - - [24/Feb/2008:00:15:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -75.54.118.139 - - [24/Feb/2008:00:15:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.54.118.139 - - [24/Feb/2008:00:15:49 -0600] "GET /software.html HTTP/1.1" 200 3163 -75.54.118.139 - - [24/Feb/2008:00:16:10 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -75.54.118.139 - - [24/Feb/2008:00:16:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -213.145.165.82 - - [24/Feb/2008:00:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.38.83 - - [24/Feb/2008:00:31:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.83 - - [24/Feb/2008:00:31:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.132.71.214 - - [24/Feb/2008:00:37:55 -0600] "GET /python.html HTTP/1.1" 200 18870 -86.132.71.214 - - [24/Feb/2008:00:37:55 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -86.132.71.214 - - [24/Feb/2008:00:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.132.71.214 - - [24/Feb/2008:00:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.132.71.214 - - [24/Feb/2008:00:38:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.25.144 - - [24/Feb/2008:00:48:16 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 200 3110734 -74.6.7.122 - - [24/Feb/2008:00:56:36 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE113.HTM HTTP/1.0" 200 1095 -125.25.238.64 - - [24/Feb/2008:01:04:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -125.25.238.64 - - [24/Feb/2008:01:04:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12382 -116.94.207.182 - - [24/Feb/2008:01:09:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -116.94.207.182 - - [24/Feb/2008:01:10:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -116.94.207.182 - - [24/Feb/2008:01:10:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.136.157 - - [24/Feb/2008:01:13:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.230 - - [24/Feb/2008:01:33:09 -0600] "GET /photos/u505/pages/IMG_1508.htm HTTP/1.0" 404 133 -128.143.38.83 - - [24/Feb/2008:01:34:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /python.html HTTP/1.1" 200 18870 -198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.83 - - [24/Feb/2008:01:44:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.179.114.61 - - [24/Feb/2008:01:55:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 -124.179.114.61 - - [24/Feb/2008:01:56:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -74.6.8.73 - - [24/Feb/2008:02:04:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 -217.136.207.156 - - [24/Feb/2008:02:08:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -217.136.207.156 - - [24/Feb/2008:02:08:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -217.136.207.156 - - [24/Feb/2008:02:08:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.136.207.156 - - [24/Feb/2008:02:14:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.117.168.219 - - [24/Feb/2008:02:15:07 -0600] "GET /ply/ HTTP/1.1" 304 - -122.117.168.219 - - [24/Feb/2008:02:15:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -122.117.168.219 - - [24/Feb/2008:02:15:11 -0600] "GET /ply/example.html HTTP/1.1" 304 - -122.117.168.219 - - [24/Feb/2008:02:15:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -217.136.207.156 - - [24/Feb/2008:02:15:20 -0600] "HEAD /ply/PLYTalk.pdf HTTP/1.1" 200 0 -217.136.207.156 - - [24/Feb/2008:02:15:40 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -77.81.4.30 - - [24/Feb/2008:02:17:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 -77.81.4.30 - - [24/Feb/2008:02:17:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -77.81.4.30 - - [24/Feb/2008:02:17:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [24/Feb/2008:02:20:25 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 -24.1.247.118 - - [24/Feb/2008:02:20:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [24/Feb/2008:02:20:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.117.168.219 - - [24/Feb/2008:02:22:06 -0600] "GET /ply/ HTTP/1.1" 304 - -122.117.168.219 - - [24/Feb/2008:02:22:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -122.117.168.219 - - [24/Feb/2008:02:22:08 -0600] "GET /ply/example.html HTTP/1.1" 304 - -89.182.136.236 - - [24/Feb/2008:02:23:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.182.136.236 - - [24/Feb/2008:02:23:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.182.136.236 - - [24/Feb/2008:02:23:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [24/Feb/2008:02:23:29 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 -117.198.144.124 - - [24/Feb/2008:02:23:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -117.198.144.124 - - [24/Feb/2008:02:23:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [24/Feb/2008:02:24:41 -0600] "GET / HTTP/1.1" 304 - -66.249.65.37 - - [24/Feb/2008:02:26:21 -0600] "GET /index.html HTTP/1.1" 304 - -66.249.65.37 - - [24/Feb/2008:02:26:54 -0600] "GET /about.html HTTP/1.1" 200 7890 -66.232.113.62 - - [24/Feb/2008:02:29:09 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -200.133.15.2 - - [24/Feb/2008:02:29:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -75.165.49.150 - - [24/Feb/2008:02:29:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -75.165.49.150 - - [24/Feb/2008:02:29:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.165.49.150 - - [24/Feb/2008:02:30:00 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1374 -75.165.49.150 - - [24/Feb/2008:02:30:06 -0600] "GET /cgi-bin/wiki.pl?UninstantiatedTemplates HTTP/1.1" 200 2091 -74.6.19.156 - - [24/Feb/2008:02:30:21 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5105 -77.81.4.30 - - [24/Feb/2008:02:34:50 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -74.6.8.73 - - [24/Feb/2008:02:39:13 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -189.141.19.88 - - [24/Feb/2008:02:49:01 -0600] "GET /ply/ HTTP/1.1" 304 - -189.141.19.88 - - [24/Feb/2008:02:49:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -189.141.19.88 - - [24/Feb/2008:02:49:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.249.65.37 - - [24/Feb/2008:02:53:59 -0600] "GET /python.html HTTP/1.1" 304 - -131.107.0.112 - - [24/Feb/2008:03:02:22 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 -217.196.43.134 - - [24/Feb/2008:03:05:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -78.100.2.3 - - [24/Feb/2008:03:05:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 -78.100.2.3 - - [24/Feb/2008:03:05:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -74.6.8.73 - - [24/Feb/2008:03:09:17 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 -88.179.52.81 - - [24/Feb/2008:03:14:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 -88.179.52.81 - - [24/Feb/2008:03:14:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -88.179.52.81 - - [24/Feb/2008:03:14:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.179.52.81 - - [24/Feb/2008:03:14:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -74.6.8.73 - - [24/Feb/2008:03:34:07 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 -64.81.229.55 - - [24/Feb/2008:03:49:00 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -64.81.229.55 - - [24/Feb/2008:03:49:08 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -64.81.229.55 - - [24/Feb/2008:03:49:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -58.120.219.129 - - [24/Feb/2008:04:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -58.120.219.129 - - [24/Feb/2008:04:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.120.219.129 - - [24/Feb/2008:04:03:01 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -58.120.219.129 - - [24/Feb/2008:04:03:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -65.55.208.119 - - [24/Feb/2008:04:04:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.119 - - [24/Feb/2008:04:04:59 -0600] "GET /about.html HTTP/1.1" 200 7890 -74.6.25.20 - - [24/Feb/2008:04:19:14 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.24.37 - - [24/Feb/2008:04:19:14 -0600] "GET /photos/wind/pages/IMG_1255.htm HTTP/1.0" 404 133 -65.55.208.123 - - [24/Feb/2008:04:27:31 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.123 - - [24/Feb/2008:04:27:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE034.HTM HTTP/1.1" 304 - -86.129.156.19 - - [24/Feb/2008:04:31:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -83.8.193.46 - - [24/Feb/2008:04:31:58 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -83.8.193.46 - - [24/Feb/2008:04:32:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.53.254.51 - - [24/Feb/2008:04:37:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 -79.66.109.148 - - [24/Feb/2008:04:37:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -83.53.254.51 - - [24/Feb/2008:04:37:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -79.66.109.148 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.53.254.51 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.53.254.51 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -79.66.109.148 - - [24/Feb/2008:04:38:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -79.66.109.148 - - [24/Feb/2008:04:38:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -79.66.109.148 - - [24/Feb/2008:04:38:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -83.53.254.51 - - [24/Feb/2008:04:39:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.230.94.215 - - [24/Feb/2008:04:45:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -61.230.94.215 - - [24/Feb/2008:04:45:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -61.230.94.215 - - [24/Feb/2008:04:45:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.230.94.215 - - [24/Feb/2008:04:45:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.230.94.215 - - [24/Feb/2008:04:45:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.230.94.215 - - [24/Feb/2008:04:45:55 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 -61.230.94.215 - - [24/Feb/2008:04:47:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.230.94.215 - - [24/Feb/2008:04:47:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -61.230.94.215 - - [24/Feb/2008:04:48:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -61.230.94.215 - - [24/Feb/2008:04:51:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -81.241.141.161 - - [24/Feb/2008:05:01:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -81.241.141.161 - - [24/Feb/2008:05:01:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -81.241.141.161 - - [24/Feb/2008:05:01:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.241.141.161 - - [24/Feb/2008:05:01:44 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.232.113.194 - - [24/Feb/2008:05:01:44 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 -80.227.1.100 - - [24/Feb/2008:05:01:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -58.176.3.7 - - [24/Feb/2008:05:01:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -60.28.31.194 - - [24/Feb/2008:05:02:03 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -172.207.178.122 - - [24/Feb/2008:05:05:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 -172.207.178.122 - - [24/Feb/2008:05:05:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -172.207.178.122 - - [24/Feb/2008:05:05:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.99.116.176 - - [24/Feb/2008:05:07:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -85.99.116.176 - - [24/Feb/2008:05:07:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.174 - - [24/Feb/2008:05:28:58 -0600] "GET /ply/ HTTP/1.0" 304 - -86.157.119.197 - - [24/Feb/2008:05:35:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.202.49.172 - - [24/Feb/2008:05:37:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 -67.202.49.172 - - [24/Feb/2008:05:37:27 -0600] "GET / HTTP/1.1" 200 4447 -66.116.72.114 - - [24/Feb/2008:05:40:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.116.72.114 - - [24/Feb/2008:05:40:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [24/Feb/2008:05:42:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.1" 200 1466 -206.51.237.114 - - [24/Feb/2008:05:49:52 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -200.65.127.161 - - [24/Feb/2008:05:49:53 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -145.76.18.22 - - [24/Feb/2008:05:49:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -222.122.236.43 - - [24/Feb/2008:05:51:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 -137.138.64.218 - - [24/Feb/2008:05:54:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -137.138.64.218 - - [24/Feb/2008:05:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.44.107 - - [24/Feb/2008:06:00:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 -86.157.119.197 - - [24/Feb/2008:06:00:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.8.73 - - [24/Feb/2008:06:14:20 -0600] "GET /training.html HTTP/1.0" 200 6154 -74.6.27.115 - - [24/Feb/2008:06:15:19 -0600] "GET /photos/wind/pages/IMG_1327.htm HTTP/1.0" 404 133 -72.30.226.134 - - [24/Feb/2008:06:20:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 -84.110.188.195 - - [24/Feb/2008:06:23:38 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.188.195 - - [24/Feb/2008:06:23:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 -67.195.44.109 - - [24/Feb/2008:06:27:28 -0600] "GET /ply/ HTTP/1.0" 200 8018 -61.135.219.5 - - [24/Feb/2008:06:28:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 -74.6.8.73 - - [24/Feb/2008:06:32:05 -0600] "GET /photos/wind/pages/IMG_1277.htm HTTP/1.0" 404 133 -84.110.187.127 - - [24/Feb/2008:06:52:46 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.187.127 - - [24/Feb/2008:06:52:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 -66.249.65.37 - - [24/Feb/2008:06:56:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE009.HTM HTTP/1.1" 200 1279 -66.249.65.37 - - [24/Feb/2008:06:57:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE008.HTM HTTP/1.1" 200 1231 -66.249.65.37 - - [24/Feb/2008:07:01:40 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE092.HTM HTTP/1.1" 200 1329 -65.55.104.13 - - [24/Feb/2008:07:03:09 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.104.13 - - [24/Feb/2008:07:03:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.249.65.37 - - [24/Feb/2008:07:03:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE071.HTM HTTP/1.1" 200 1322 -89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12238 -89.49.130.55 - - [24/Feb/2008:07:04:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.49.130.55 - - [24/Feb/2008:07:04:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.49.130.55 - - [24/Feb/2008:07:04:15 -0600] "GET /ply/README HTTP/1.1" 200 8605 -125.99.164.76 - - [24/Feb/2008:07:11:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.249.65.37 - - [24/Feb/2008:07:14:19 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE050.HTM HTTP/1.1" 200 982 -84.110.206.219 - - [24/Feb/2008:07:15:16 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.206.219 - - [24/Feb/2008:07:15:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1047 -84.110.117.89 - - [24/Feb/2008:07:15:34 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.117.89 - - [24/Feb/2008:07:15:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1101 -84.110.189.175 - - [24/Feb/2008:07:15:55 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.189.175 - - [24/Feb/2008:07:15:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1158 -89.49.130.55 - - [24/Feb/2008:07:20:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -84.110.121.137 - - [24/Feb/2008:07:36:14 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.121.137 - - [24/Feb/2008:07:36:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1107 -80.93.56.177 - - [24/Feb/2008:07:57:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 -80.93.56.116 - - [24/Feb/2008:07:57:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.93.56.177 - - [24/Feb/2008:07:57:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.93.56.116 - - [24/Feb/2008:07:57:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.93.56.177 - - [24/Feb/2008:07:57:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.93.56.116 - - [24/Feb/2008:07:57:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.93.56.177 - - [24/Feb/2008:07:57:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.93.56.116 - - [24/Feb/2008:07:57:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.93.56.177 - - [24/Feb/2008:07:57:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.93.56.116 - - [24/Feb/2008:07:58:00 -0600] "GET / HTTP/1.1" 200 4447 -80.93.56.177 - - [24/Feb/2008:07:58:00 -0600] "GET / HTTP/1.1" 200 4447 -80.93.56.116 - - [24/Feb/2008:07:58:01 -0600] "GET / HTTP/1.1" 200 4447 -80.93.56.177 - - [24/Feb/2008:07:58:01 -0600] "GET / HTTP/1.1" 200 4447 -66.232.113.62 - - [24/Feb/2008:08:03:16 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2754 -83.229.21.4 - - [24/Feb/2008:08:03:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -219.136.206.29 - - [24/Feb/2008:08:03:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -83.8.193.46 - - [24/Feb/2008:08:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.196.97.157 - - [24/Feb/2008:08:20:04 -0600] "GET / HTTP/1.0" 200 4447 -66.196.97.157 - - [24/Feb/2008:08:20:05 -0600] "GET / HTTP/1.0" 200 4447 -66.196.97.157 - - [24/Feb/2008:08:20:07 -0600] "GET / HTTP/1.0" 200 4447 -66.196.97.157 - - [24/Feb/2008:08:20:07 -0600] "GET / HTTP/1.0" 200 4447 -79.182.112.231 - - [24/Feb/2008:08:27:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -79.182.112.231 - - [24/Feb/2008:08:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -79.182.112.231 - - [24/Feb/2008:08:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.226.58.44 - - [24/Feb/2008:08:28:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -69.121.132.53 - - [24/Feb/2008:08:28:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.226.58.44 - - [24/Feb/2008:08:28:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -82.226.58.44 - - [24/Feb/2008:08:28:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -82.226.58.44 - - [24/Feb/2008:08:28:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 -82.226.58.44 - - [24/Feb/2008:08:28:34 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Perl5Typemaps HTTP/1.1" 200 3613 -82.226.58.44 - - [24/Feb/2008:08:29:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -82.226.58.44 - - [24/Feb/2008:08:29:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -82.226.58.44 - - [24/Feb/2008:08:29:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -88.191.19.81 - - [24/Feb/2008:08:30:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.29.115.55 - - [24/Feb/2008:08:31:57 -0600] "GET / HTTP/1.1" 200 4447 -65.55.208.117 - - [24/Feb/2008:08:36:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.117 - - [24/Feb/2008:08:36:42 -0600] "GET /swill/exec.html HTTP/1.1" 304 - -59.92.203.99 - - [24/Feb/2008:08:42:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -59.92.203.99 - - [24/Feb/2008:08:42:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -59.92.203.99 - - [24/Feb/2008:08:42:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -59.92.203.99 - - [24/Feb/2008:08:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -79.182.112.231 - - [24/Feb/2008:08:47:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -79.182.112.231 - - [24/Feb/2008:08:47:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -79.182.112.231 - - [24/Feb/2008:08:47:29 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 -79.182.112.231 - - [24/Feb/2008:08:47:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -79.182.112.231 - - [24/Feb/2008:08:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -79.182.112.231 - - [24/Feb/2008:08:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -79.182.112.231 - - [24/Feb/2008:08:47:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 -79.182.112.231 - - [24/Feb/2008:08:47:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -79.182.112.231 - - [24/Feb/2008:08:47:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 -79.182.112.231 - - [24/Feb/2008:08:47:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -79.182.112.231 - - [24/Feb/2008:08:48:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -79.182.112.231 - - [24/Feb/2008:08:48:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 -189.70.147.197 - - [24/Feb/2008:09:08:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.129.99.56 - - [24/Feb/2008:09:08:05 -0600] "GET /ply/ HTTP/1.1" 304 - -189.70.147.197 - - [24/Feb/2008:09:08:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.70.147.197 - - [24/Feb/2008:09:08:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:08:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:08:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:12:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.70.147.197 - - [24/Feb/2008:09:17:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.70.147.197 - - [24/Feb/2008:09:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:17:55 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -189.70.147.197 - - [24/Feb/2008:09:18:01 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 -189.70.147.197 - - [24/Feb/2008:09:18:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:18:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:18:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.184.6.249 - - [24/Feb/2008:09:18:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -200.184.6.249 - - [24/Feb/2008:09:18:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -200.184.6.249 - - [24/Feb/2008:09:18:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.184.6.249 - - [24/Feb/2008:09:19:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:20:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.184.6.249 - - [24/Feb/2008:09:21:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -150.210.155.167 - - [24/Feb/2008:09:22:11 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /ply/ HTTP/1.0" 200 8018 -76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.55.208.119 - - [24/Feb/2008:09:32:37 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.119 - - [24/Feb/2008:09:32:37 -0600] "GET /photos/u505/pages/IMG_1484.htm HTTP/1.1" 404 133 -84.122.84.241 - - [24/Feb/2008:09:32:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -84.122.84.241 - - [24/Feb/2008:09:33:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -84.122.84.241 - - [24/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -118.83.147.252 - - [24/Feb/2008:09:35:15 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.1" 200 0 -201.21.24.169 - - [24/Feb/2008:09:35:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.21.24.169 - - [24/Feb/2008:09:36:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.21.24.169 - - [24/Feb/2008:09:37:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.28.142 - - [24/Feb/2008:09:37:32 -0600] "GET /dynamic/assign4.html HTTP/1.0" 200 8712 -189.13.155.147 - - [24/Feb/2008:09:38:31 -0600] "GET / HTTP/1.0" 200 4447 -189.13.155.147 - - [24/Feb/2008:09:38:32 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -189.13.155.147 - - [24/Feb/2008:09:38:52 -0600] "GET /ply/ HTTP/1.1" 304 - -189.13.155.147 - - [24/Feb/2008:09:38:52 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -67.176.147.11 - - [24/Feb/2008:09:40:25 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 -67.176.147.11 - - [24/Feb/2008:09:40:28 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -74.6.20.36 - - [24/Feb/2008:09:41:59 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.0" 200 399 -66.94.237.140 - - [24/Feb/2008:09:46:14 -0600] "HEAD /ply/index.html HTTP/1.0" 200 0 -66.94.237.140 - - [24/Feb/2008:09:46:14 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -220.133.118.213 - - [24/Feb/2008:09:46:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.21.24.169 - - [24/Feb/2008:09:48:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.21.24.169 - - [24/Feb/2008:09:48:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:48:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [24/Feb/2008:09:54:22 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [24/Feb/2008:09:54:24 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -201.236.226.90 - - [24/Feb/2008:09:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [24/Feb/2008:09:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [24/Feb/2008:09:54:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -80.68.93.199 - - [24/Feb/2008:09:55:52 -0600] "GET /ply/ HTTP/1.0" 200 8018 -80.68.93.199 - - [24/Feb/2008:09:55:53 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -80.68.93.199 - - [24/Feb/2008:09:55:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -80.68.93.199 - - [24/Feb/2008:09:56:26 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -74.6.23.14 - - [24/Feb/2008:09:57:00 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1310 -189.70.147.197 - - [24/Feb/2008:09:57:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.123 - - [24/Feb/2008:09:58:50 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -128.143.38.123 - - [24/Feb/2008:09:58:54 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 -128.143.38.123 - - [24/Feb/2008:09:58:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.123 - - [24/Feb/2008:09:58:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:09:59:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:10:00:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.70.147.197 - - [24/Feb/2008:10:00:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:10:00:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.70.147.197 - - [24/Feb/2008:10:00:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.123 - - [24/Feb/2008:10:01:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.123 - - [24/Feb/2008:10:01:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -74.6.8.73 - - [24/Feb/2008:10:02:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -84.110.186.118 - - [24/Feb/2008:10:03:14 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.186.118 - - [24/Feb/2008:10:03:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 -217.196.43.134 - - [24/Feb/2008:10:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.116.72.114 - - [24/Feb/2008:10:06:12 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.116.72.114 - - [24/Feb/2008:10:06:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -196.203.175.16 - - [24/Feb/2008:10:12:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -74.6.25.105 - - [24/Feb/2008:10:13:11 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 -128.143.38.123 - - [24/Feb/2008:10:31:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.206.180.32 - - [24/Feb/2008:10:31:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.206.180.32 - - [24/Feb/2008:10:31:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.206.180.32 - - [24/Feb/2008:10:31:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.8.73 - - [24/Feb/2008:10:34:02 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - -122.55.52.10 - - [24/Feb/2008:10:40:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -122.55.52.10 - - [24/Feb/2008:10:40:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -122.55.52.10 - - [24/Feb/2008:10:40:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -122.55.52.10 - - [24/Feb/2008:10:41:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 -189.13.184.120 - - [24/Feb/2008:10:42:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.13.184.120 - - [24/Feb/2008:10:42:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -122.55.52.10 - - [24/Feb/2008:10:42:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/PhP HTTP/1.0" 200 3594 -189.13.184.120 - - [24/Feb/2008:10:44:41 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -88.191.19.81 - - [24/Feb/2008:10:46:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.8.73 - - [24/Feb/2008:10:56:58 -0600] "GET /swill/Doc/ HTTP/1.0" 200 39052 -61.135.166.102 - - [24/Feb/2008:11:04:25 -0600] "GET / HTTP/1.1" 200 4447 -65.55.208.121 - - [24/Feb/2008:11:23:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.121 - - [24/Feb/2008:11:23:15 -0600] "GET /swig/SWIG_Doc1.pdf HTTP/1.1" 304 - -86.157.4.107 - - [24/Feb/2008:11:24:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -65.55.104.157 - - [24/Feb/2008:11:25:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 -189.29.245.80 - - [24/Feb/2008:11:25:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.29.245.80 - - [24/Feb/2008:11:25:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.29.245.80 - - [24/Feb/2008:11:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.29.245.80 - - [24/Feb/2008:11:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.169.3 - - [24/Feb/2008:11:26:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 15602 -80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -80.99.169.3 - - [24/Feb/2008:11:26:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.29.245.80 - - [24/Feb/2008:11:26:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.169.3 - - [24/Feb/2008:11:26:40 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -80.99.169.3 - - [24/Feb/2008:11:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.169.3 - - [24/Feb/2008:11:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.124.22.177 - - [24/Feb/2008:11:35:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.22.143 - - [24/Feb/2008:11:47:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.22.143 - - [24/Feb/2008:11:47:12 -0600] "GET /dynamic/ HTTP/1.0" 200 5105 -75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.199.195 - - [24/Feb/2008:11:54:45 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /ply/ HTTP/1.1" 200 8018 -71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.206.180.194 - - [24/Feb/2008:11:59:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -83.71.34.82 - - [24/Feb/2008:12:02:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -83.71.34.82 - - [24/Feb/2008:12:02:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -83.71.34.82 - - [24/Feb/2008:12:02:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Tcl HTTP/1.1" 200 1399 -67.195.58.174 - - [24/Feb/2008:12:09:31 -0600] "GET /ply/ HTTP/1.0" 304 - -72.240.122.140 - - [24/Feb/2008:12:18:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -72.240.122.140 - - [24/Feb/2008:12:18:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.240.122.140 - - [24/Feb/2008:12:18:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 -72.240.122.140 - - [24/Feb/2008:12:19:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.117.28.14 - - [24/Feb/2008:12:24:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.143 - - [24/Feb/2008:12:28:47 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 -24.1.247.118 - - [24/Feb/2008:12:29:32 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -24.1.247.118 - - [24/Feb/2008:12:29:37 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 200 731143 -98.206.164.173 - - [24/Feb/2008:12:33:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - -98.206.164.173 - - [24/Feb/2008:12:33:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.114.65.194 - - [24/Feb/2008:12:37:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.114.65.194 - - [24/Feb/2008:12:37:58 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1878 -76.114.65.194 - - [24/Feb/2008:12:38:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -76.114.65.194 - - [24/Feb/2008:12:38:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -85.242.185.241 - - [24/Feb/2008:12:41:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -85.242.185.241 - - [24/Feb/2008:12:41:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -85.242.185.241 - - [24/Feb/2008:12:41:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.242.185.241 - - [24/Feb/2008:12:41:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.195 - - [24/Feb/2008:12:41:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.231.195 - - [24/Feb/2008:12:41:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.195 - - [24/Feb/2008:12:41:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.242.185.241 - - [24/Feb/2008:12:41:58 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -85.242.185.241 - - [24/Feb/2008:12:41:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -83.10.100.10 - - [24/Feb/2008:12:42:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.10.100.10 - - [24/Feb/2008:12:42:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -83.10.100.10 - - [24/Feb/2008:12:42:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.169.3 - - [24/Feb/2008:12:44:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.231.195 - - [24/Feb/2008:12:46:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.20.137 - - [24/Feb/2008:12:50:05 -0600] "GET /photos/u505/pages/IMG_1500.htm HTTP/1.0" 404 133 -128.143.231.195 - - [24/Feb/2008:12:53:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.195 - - [24/Feb/2008:12:56:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.195 - - [24/Feb/2008:12:58:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.195 - - [24/Feb/2008:12:59:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.135.166.102 - - [24/Feb/2008:13:04:29 -0600] "GET / HTTP/1.1" 200 4447 -85.155.44.41 - - [24/Feb/2008:13:06:45 -0600] "GET /ply/ HTTP/1.1" 304 - -88.191.19.81 - - [24/Feb/2008:13:08:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -99.167.103.107 - - [24/Feb/2008:13:09:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -99.167.103.107 - - [24/Feb/2008:13:09:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.167.103.107 - - [24/Feb/2008:13:10:00 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -99.167.103.107 - - [24/Feb/2008:13:10:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -99.167.103.107 - - [24/Feb/2008:13:10:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -24.60.232.105 - - [24/Feb/2008:13:10:58 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -24.60.232.105 - - [24/Feb/2008:13:10:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.60.232.105 - - [24/Feb/2008:13:10:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.60.232.105 - - [24/Feb/2008:13:11:13 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -128.143.38.123 - - [24/Feb/2008:13:12:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.38.123 - - [24/Feb/2008:13:12:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.42.166.20 - - [24/Feb/2008:13:13:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -201.42.166.20 - - [24/Feb/2008:13:13:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.123 - - [24/Feb/2008:13:14:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.173.185.186 - - [24/Feb/2008:13:17:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.173.185.186 - - [24/Feb/2008:13:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.123 - - [24/Feb/2008:13:18:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.195 - - [24/Feb/2008:13:20:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 126926 -128.143.231.195 - - [24/Feb/2008:13:20:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 -67.195.58.177 - - [24/Feb/2008:13:20:46 -0600] "GET /python.html HTTP/1.0" 304 - -217.219.18.80 - - [24/Feb/2008:13:22:10 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -83.130.163.210 - - [24/Feb/2008:13:23:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -83.130.163.210 - - [24/Feb/2008:13:23:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.114.65.194 - - [24/Feb/2008:13:24:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 -128.143.38.123 - - [24/Feb/2008:13:24:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.26.28 - - [24/Feb/2008:13:24:50 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1299 -76.114.65.194 - - [24/Feb/2008:13:25:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -76.114.65.194 - - [24/Feb/2008:13:25:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 -76.114.65.194 - - [24/Feb/2008:13:26:19 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 -76.114.65.194 - - [24/Feb/2008:13:26:35 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 -76.114.65.194 - - [24/Feb/2008:13:27:21 -0600] "GET /cgi-bin/wiki.pl?CAsAHighLevelLanguage HTTP/1.1" 200 2235 -86.157.119.197 - - [24/Feb/2008:13:27:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.80.194.91 - - [24/Feb/2008:13:32:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.141.81.60 - - [24/Feb/2008:13:32:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.209.71.13 - - [24/Feb/2008:13:32:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -201.141.81.60 - - [24/Feb/2008:13:32:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.141.81.60 - - [24/Feb/2008:13:32:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.141.81.60 - - [24/Feb/2008:13:33:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -201.141.81.60 - - [24/Feb/2008:13:33:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.31.151 - - [24/Feb/2008:13:35:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -98.206.164.173 - - [24/Feb/2008:13:39:37 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -74.6.22.143 - - [24/Feb/2008:13:45:25 -0600] "GET /writing.html HTTP/1.0" 200 2871 -66.232.113.62 - - [24/Feb/2008:13:46:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -80.227.1.100 - - [24/Feb/2008:13:46:12 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -200.51.41.29 - - [24/Feb/2008:13:46:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -201.42.166.20 - - [24/Feb/2008:13:46:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.42.166.20 - - [24/Feb/2008:13:46:47 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -201.42.166.20 - - [24/Feb/2008:13:46:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -201.42.166.20 - - [24/Feb/2008:13:46:54 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -201.42.166.20 - - [24/Feb/2008:13:46:58 -0600] "GET /cgi-bin/wiki.pl?FormattingRules HTTP/1.1" 200 10503 -201.42.166.20 - - [24/Feb/2008:13:46:59 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -201.42.166.20 - - [24/Feb/2008:13:47:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -190.64.107.172 - - [24/Feb/2008:13:48:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -190.64.107.172 - - [24/Feb/2008:13:48:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -190.64.107.172 - - [24/Feb/2008:13:48:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 -190.64.107.172 - - [24/Feb/2008:13:48:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -80.58.205.41 - - [24/Feb/2008:13:53:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -80.58.205.41 - - [24/Feb/2008:13:53:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.58.205.41 - - [24/Feb/2008:13:53:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 -74.6.25.20 - - [24/Feb/2008:13:54:03 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.31.165 - - [24/Feb/2008:13:54:03 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.165 - - [24/Feb/2008:13:54:03 -0600] "GET /ply/ HTTP/1.0" 200 8018 -128.143.231.195 - - [24/Feb/2008:13:54:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.168.250.62 - - [24/Feb/2008:13:57:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -70.168.250.62 - - [24/Feb/2008:13:57:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 991 -70.168.250.62 - - [24/Feb/2008:13:57:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1237 -70.168.250.62 - - [24/Feb/2008:13:57:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -69.209.71.13 - - [24/Feb/2008:13:59:17 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -69.209.71.13 - - [24/Feb/2008:13:59:25 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -70.168.250.62 - - [24/Feb/2008:13:59:30 -0600] "GET /cgi-bin/wiki.pl?MakedefaultDirective HTTP/1.1" 200 2961 -69.209.71.13 - - [24/Feb/2008:13:59:46 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -69.209.71.13 - - [24/Feb/2008:14:00:02 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -58.24.206.144 - - [24/Feb/2008:14:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -58.24.206.144 - - [24/Feb/2008:14:02:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.24.206.144 - - [24/Feb/2008:14:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.168.250.62 - - [24/Feb/2008:14:04:39 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1564 -70.168.250.62 - - [24/Feb/2008:14:04:42 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 -74.6.19.115 - - [24/Feb/2008:14:04:49 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.31.170 - - [24/Feb/2008:14:04:49 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.170 - - [24/Feb/2008:14:04:49 -0600] "GET /ply/ HTTP/1.0" 200 8018 -58.24.206.144 - - [24/Feb/2008:14:09:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.24.206.144 - - [24/Feb/2008:14:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -58.24.206.144 - - [24/Feb/2008:14:09:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -24.1.247.118 - - [24/Feb/2008:14:09:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.173.185.186 - - [24/Feb/2008:14:15:48 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 -67.173.185.186 - - [24/Feb/2008:14:16:00 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -128.143.38.123 - - [24/Feb/2008:14:21:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.38.123 - - [24/Feb/2008:14:21:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.123 - - [24/Feb/2008:14:21:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.38.123 - - [24/Feb/2008:14:21:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -84.255.203.160 - - [24/Feb/2008:14:21:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 -84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.255.203.160 - - [24/Feb/2008:14:23:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.178 - - [24/Feb/2008:14:26:26 -0600] "GET /software.html HTTP/1.0" 304 - -67.195.58.164 - - [24/Feb/2008:14:27:22 -0600] "GET /dynamic/index.html HTTP/1.0" 304 - -98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [24/Feb/2008:14:30:27 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -199.111.229.93 - - [24/Feb/2008:14:34:21 -0600] "GET /ply/ HTTP/1.1" 304 - -199.111.229.93 - - [24/Feb/2008:14:34:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -199.111.229.93 - - [24/Feb/2008:14:34:51 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -189.141.19.88 - - [24/Feb/2008:14:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.172 - - [24/Feb/2008:14:40:05 -0600] "GET /index.html HTTP/1.0" 200 4447 -65.214.45.114 - - [24/Feb/2008:14:41:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.214.45.114 - - [24/Feb/2008:14:41:42 -0600] "GET /images/superboard.jpg HTTP/1.0" 200 71119 -204.111.252.233 - - [24/Feb/2008:14:42:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -204.111.252.233 - - [24/Feb/2008:14:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.111.252.233 - - [24/Feb/2008:14:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.168 - - [24/Feb/2008:14:46:46 -0600] "GET /consulting.html HTTP/1.0" 304 - -98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.143 - - [24/Feb/2008:14:57:30 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 -98.193.69.179 - - [24/Feb/2008:14:58:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -189.13.184.120 - - [24/Feb/2008:14:59:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -67.173.185.186 - - [24/Feb/2008:15:00:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.135.166.102 - - [24/Feb/2008:15:04:25 -0600] "GET / HTTP/1.1" 200 4447 -220.181.38.169 - - [24/Feb/2008:15:04:57 -0600] "GET / HTTP/1.1" 200 4447 -217.237.150.206 - - [24/Feb/2008:15:06:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -217.237.150.208 - - [24/Feb/2008:15:06:07 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -217.237.150.208 - - [24/Feb/2008:15:06:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 963 -217.237.150.207 - - [24/Feb/2008:15:06:21 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 -67.173.185.186 - - [24/Feb/2008:15:10:17 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 200 313896 -67.173.185.186 - - [24/Feb/2008:15:10:18 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 206 665908 -210.212.55.3 - - [24/Feb/2008:15:18:15 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -210.212.55.3 - - [24/Feb/2008:15:18:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -210.212.55.3 - - [24/Feb/2008:15:18:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -210.212.55.3 - - [24/Feb/2008:15:18:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -74.6.25.124 - - [24/Feb/2008:15:18:35 -0600] "GET /photos/u505/pages/IMG_1524.htm HTTP/1.0" 404 133 -210.212.55.3 - - [24/Feb/2008:15:18:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply HTTP/1.1" 301 242 -128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 -128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/ HTTP/1.1" 206 1042 -128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -210.212.55.3 - - [24/Feb/2008:15:25:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.54.165.36 - - [24/Feb/2008:15:37:16 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.54.165.36 - - [24/Feb/2008:15:37:17 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 304 - -69.46.29.140 - - [24/Feb/2008:15:40:50 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2732 -91.121.92.62 - - [24/Feb/2008:15:40:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -206.51.237.114 - - [24/Feb/2008:15:41:03 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 -88.255.192.42 - - [24/Feb/2008:15:41:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -83.229.21.4 - - [24/Feb/2008:15:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -85.214.114.155 - - [24/Feb/2008:15:41:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -210.22.158.132 - - [24/Feb/2008:15:41:25 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -66.232.113.194 - - [24/Feb/2008:15:43:12 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -60.234.20.98 - - [24/Feb/2008:15:43:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -200.216.186.35 - - [24/Feb/2008:15:43:18 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -74.6.22.143 - - [24/Feb/2008:15:50:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.22.143 - - [24/Feb/2008:15:50:37 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 -86.138.167.172 - - [24/Feb/2008:15:54:47 -0600] "GET /ply HTTP/1.1" 301 242 -86.138.167.172 - - [24/Feb/2008:15:54:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -86.138.167.172 - - [24/Feb/2008:15:54:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -86.138.167.172 - - [24/Feb/2008:15:54:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.191.19.81 - - [24/Feb/2008:15:56:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 -204.111.252.233 - - [24/Feb/2008:15:56:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.157.119.197 - - [24/Feb/2008:16:04:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.199.136 - - [24/Feb/2008:16:05:51 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.199.136 - - [24/Feb/2008:16:05:53 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1149 -89.229.25.165 - - [24/Feb/2008:16:06:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.229.25.165 - - [24/Feb/2008:16:06:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.229.25.165 - - [24/Feb/2008:16:06:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.229.25.165 - - [24/Feb/2008:16:06:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.229.25.165 - - [24/Feb/2008:16:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.229.25.165 - - [24/Feb/2008:16:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.229.25.165 - - [24/Feb/2008:16:07:00 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -75.32.37.138 - - [24/Feb/2008:16:12:29 -0600] "GET / HTTP/1.1" 200 4447 -75.32.37.138 - - [24/Feb/2008:16:12:30 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -75.32.37.138 - - [24/Feb/2008:16:12:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.32.37.138 - - [24/Feb/2008:16:13:05 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -75.32.37.138 - - [24/Feb/2008:16:13:14 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -204.111.252.233 - - [24/Feb/2008:16:14:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.232.113.62 - - [24/Feb/2008:16:15:32 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 -85.185.11.131 - - [24/Feb/2008:16:15:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -64.124.150.55 - - [24/Feb/2008:16:15:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [24/Feb/2008:16:20:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [24/Feb/2008:16:20:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [24/Feb/2008:16:21:14 -0600] "GET / HTTP/1.1" 200 4447 -128.135.11.245 - - [24/Feb/2008:16:21:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -172.159.46.8 - - [24/Feb/2008:16:21:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -172.159.46.8 - - [24/Feb/2008:16:21:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.135.11.245 - - [24/Feb/2008:16:21:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 -128.135.11.245 - - [24/Feb/2008:16:21:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -172.159.46.8 - - [24/Feb/2008:16:21:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [24/Feb/2008:16:21:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.135.11.245 - - [24/Feb/2008:16:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.39 - - [24/Feb/2008:16:23:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.7.210.64 - - [24/Feb/2008:16:25:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.7.210.64 - - [24/Feb/2008:16:25:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.176.42.43 - - [24/Feb/2008:16:30:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.176.42.43 - - [24/Feb/2008:16:30:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.176.42.43 - - [24/Feb/2008:16:30:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -207.176.224.242 - - [24/Feb/2008:16:30:34 -0600] "GET /robots.txt HTTP/1.0" 200 71 -89.229.25.165 - - [24/Feb/2008:16:33:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.89.137.229 - - [24/Feb/2008:16:33:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -91.89.137.229 - - [24/Feb/2008:16:33:33 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -71.6.151.80 - - [24/Feb/2008:16:39:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 -128.135.11.245 - - [24/Feb/2008:16:40:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [24/Feb/2008:16:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [24/Feb/2008:16:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [24/Feb/2008:16:40:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [24/Feb/2008:16:40:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [24/Feb/2008:16:40:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.4.230.43 - - [24/Feb/2008:16:40:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -64.4.230.43 - - [24/Feb/2008:16:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.4.230.43 - - [24/Feb/2008:16:40:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 -74.6.19.101 - - [24/Feb/2008:16:46:54 -0600] "GET /swill/Doc/ HTTP/1.0" 200 39052 -67.173.185.186 - - [24/Feb/2008:16:48:30 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -71.62.148.145 - - [24/Feb/2008:16:56:16 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.62.148.145 - - [24/Feb/2008:16:56:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.62.148.145 - - [24/Feb/2008:16:56:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.62.148.145 - - [24/Feb/2008:16:56:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.62.148.145 - - [24/Feb/2008:16:57:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.181.38.169 - - [24/Feb/2008:17:04:19 -0600] "GET / HTTP/1.1" 200 4447 -61.135.166.102 - - [24/Feb/2008:17:04:25 -0600] "GET / HTTP/1.1" 200 4447 -217.196.43.134 - - [24/Feb/2008:17:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -210.143.35.17 - - [24/Feb/2008:17:07:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 -210.143.35.17 - - [24/Feb/2008:17:07:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.10 - - [24/Feb/2008:17:09:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.205.10 - - [24/Feb/2008:17:09:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.205.10 - - [24/Feb/2008:17:09:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.10 - - [24/Feb/2008:17:09:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -199.111.205.10 - - [24/Feb/2008:17:10:10 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -80.202.87.9 - - [24/Feb/2008:17:12:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 -199.111.205.10 - - [24/Feb/2008:17:14:56 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -71.62.148.145 - - [24/Feb/2008:17:18:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.10 - - [24/Feb/2008:17:19:36 -0600] "GET /ply/README HTTP/1.1" 200 8605 -71.62.148.145 - - [24/Feb/2008:17:21:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.252.151.118 - - [24/Feb/2008:17:21:45 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -80.252.151.118 - - [24/Feb/2008:17:21:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -199.111.205.10 - - [24/Feb/2008:17:22:18 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -82.248.57.218 - - [24/Feb/2008:17:24:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.248.57.218 - - [24/Feb/2008:17:24:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.248.57.218 - - [24/Feb/2008:17:24:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.144.107.121 - - [24/Feb/2008:17:26:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -69.209.71.13 - - [24/Feb/2008:17:26:43 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -71.62.148.145 - - [24/Feb/2008:17:28:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.47.80.43 - - [24/Feb/2008:17:33:12 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.47.80.43 - - [24/Feb/2008:17:33:12 -0600] "GET / HTTP/1.1" 200 4447 -71.62.148.145 - - [24/Feb/2008:17:37:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.144.107.121 - - [24/Feb/2008:17:37:53 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -67.195.58.182 - - [24/Feb/2008:17:38:07 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 -67.195.58.186 - - [24/Feb/2008:17:38:09 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 -67.195.58.170 - - [24/Feb/2008:17:38:32 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 -67.195.58.181 - - [24/Feb/2008:17:38:48 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -67.173.185.186 - - [24/Feb/2008:17:38:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.151 - - [24/Feb/2008:17:39:02 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 -189.144.107.121 - - [24/Feb/2008:17:40:22 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -189.144.107.121 - - [24/Feb/2008:17:40:23 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 -76.198.207.51 - - [24/Feb/2008:17:44:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -76.198.207.51 - - [24/Feb/2008:17:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.198.207.51 - - [24/Feb/2008:17:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.33.39.177 - - [24/Feb/2008:17:47:25 -0600] "GET /python.html HTTP/1.1" 200 18870 -151.33.39.177 - - [24/Feb/2008:17:47:26 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -151.33.39.177 - - [24/Feb/2008:17:47:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.33.39.177 - - [24/Feb/2008:17:47:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.91.200.98 - - [24/Feb/2008:17:48:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -67.195.58.164 - - [24/Feb/2008:17:54:04 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 -76.10.149.199 - - [24/Feb/2008:17:54:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -76.10.149.199 - - [24/Feb/2008:17:54:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.10.149.199 - - [24/Feb/2008:17:54:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -72.244.56.83 - - [24/Feb/2008:17:54:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -72.244.56.83 - - [24/Feb/2008:17:54:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.244.56.83 - - [24/Feb/2008:17:55:22 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -65.247.230.214 - - [24/Feb/2008:17:56:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.10.149.199 - - [24/Feb/2008:17:56:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 -76.10.149.199 - - [24/Feb/2008:17:56:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 -76.10.149.199 - - [24/Feb/2008:17:56:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.1" 200 5005 -65.55.208.118 - - [24/Feb/2008:17:57:44 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.118 - - [24/Feb/2008:17:57:45 -0600] "GET /photos/u505/pages/IMG_1530.htm HTTP/1.1" 404 133 -74.6.19.115 - - [24/Feb/2008:17:59:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.31.145 - - [24/Feb/2008:17:59:40 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -88.191.19.81 - - [24/Feb/2008:18:01:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.22.143 - - [24/Feb/2008:18:02:06 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -71.230.189.170 - - [24/Feb/2008:18:06:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -151.33.39.177 - - [24/Feb/2008:18:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.33.39.177 - - [24/Feb/2008:18:10:37 -0600] "GET /software.html HTTP/1.1" 200 3163 -84.110.216.199 - - [24/Feb/2008:18:17:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.216.199 - - [24/Feb/2008:18:17:33 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 987 -67.195.58.168 - - [24/Feb/2008:18:17:51 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 -64.81.241.54 - - [24/Feb/2008:18:26:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.81.241.54 - - [24/Feb/2008:18:26:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.146.214.212 - - [24/Feb/2008:18:32:02 -0600] "GET /dynamic/ HTTP/1.1" 304 - -66.146.214.212 - - [24/Feb/2008:18:32:11 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -12.206.63.189 - - [24/Feb/2008:18:37:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -12.206.63.189 - - [24/Feb/2008:18:37:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -12.206.63.189 - - [24/Feb/2008:18:37:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -12.206.63.189 - - [24/Feb/2008:18:38:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.70.18 - - [24/Feb/2008:18:42:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.70.18 - - [24/Feb/2008:18:42:49 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -82.35.89.141 - - [24/Feb/2008:18:43:23 -0600] "GET /ply/ HTTP/1.1" 304 - -82.35.89.141 - - [24/Feb/2008:18:43:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -82.226.58.44 - - [24/Feb/2008:18:45:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.226.58.44 - - [24/Feb/2008:18:45:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -82.226.58.44 - - [24/Feb/2008:18:45:55 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -82.226.58.44 - - [24/Feb/2008:18:46:05 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 -128.143.70.18 - - [24/Feb/2008:18:48:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -84.110.209.197 - - [24/Feb/2008:18:56:42 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.209.197 - - [24/Feb/2008:18:56:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1071 -58.215.57.238 - - [24/Feb/2008:18:59:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 -58.215.57.238 - - [24/Feb/2008:18:59:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -58.215.57.238 - - [24/Feb/2008:18:59:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.145.11.94 - - [24/Feb/2008:19:02:32 -0600] "GET /robots.txt HTTP/1.0" 200 71 -216.145.11.94 - - [24/Feb/2008:19:02:32 -0600] "GET / HTTP/1.1" 206 4447 -218.94.136.173 - - [24/Feb/2008:19:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -218.94.136.173 - - [24/Feb/2008:19:05:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -218.94.136.173 - - [24/Feb/2008:19:05:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.125.38.188 - - [24/Feb/2008:19:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.125.38.188 - - [24/Feb/2008:19:12:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -74.6.22.143 - - [24/Feb/2008:19:21:03 -0600] "GET /swill/exec.html HTTP/1.0" 200 12540 -72.85.134.143 - - [24/Feb/2008:19:23:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 -72.85.134.143 - - [24/Feb/2008:19:23:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -72.85.134.143 - - [24/Feb/2008:19:23:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [24/Feb/2008:19:25:02 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -75.22.21.146 - - [24/Feb/2008:19:25:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.85.134.143 - - [24/Feb/2008:19:25:12 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -24.207.163.47 - - [24/Feb/2008:19:30:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.207.163.47 - - [24/Feb/2008:19:30:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -24.207.163.47 - - [24/Feb/2008:19:30:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.207.163.47 - - [24/Feb/2008:19:31:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.207.163.47 - - [24/Feb/2008:19:31:05 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -210.245.52.8 - - [24/Feb/2008:19:38:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 -210.245.52.8 - - [24/Feb/2008:19:38:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.141.19.88 - - [24/Feb/2008:19:42:42 -0600] "GET /ply/ HTTP/1.1" 304 - -189.141.19.88 - - [24/Feb/2008:19:42:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -189.141.19.88 - - [24/Feb/2008:19:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.141.19.88 - - [24/Feb/2008:19:42:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -71.57.91.136 - - [24/Feb/2008:19:46:56 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -24.84.190.11 - - [24/Feb/2008:19:48:40 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -24.84.190.11 - - [24/Feb/2008:19:48:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.179.180.52 - - [24/Feb/2008:19:49:28 -0600] "GET /robots.txt HTTP/1.1" 200 71 -202.179.180.52 - - [24/Feb/2008:19:49:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -24.84.190.11 - - [24/Feb/2008:19:50:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -74.6.28.151 - - [24/Feb/2008:19:53:29 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE076.HTM HTTP/1.0" 200 1335 -201.86.78.151 - - [24/Feb/2008:19:54:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.86.78.151 - - [24/Feb/2008:19:54:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.86.78.151 - - [24/Feb/2008:19:54:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.86.78.151 - - [24/Feb/2008:19:54:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.86.78.151 - - [24/Feb/2008:19:54:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.86.78.151 - - [24/Feb/2008:19:55:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.86.78.151 - - [24/Feb/2008:19:55:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.86.78.151 - - [24/Feb/2008:19:55:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.86.78.151 - - [24/Feb/2008:19:55:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.135.190.17 - - [24/Feb/2008:19:58:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.28.162 - - [24/Feb/2008:19:59:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE061.HTM HTTP/1.0" 200 1508 -99.140.184.199 - - [24/Feb/2008:20:00:38 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 -99.140.184.199 - - [24/Feb/2008:20:00:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.184.199 - - [24/Feb/2008:20:00:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [24/Feb/2008:20:00:49 -0600] "GET /dynamic/ HTTP/1.1" 304 - -75.22.21.146 - - [24/Feb/2008:20:01:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [24/Feb/2008:20:01:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -165.82.137.78 - - [24/Feb/2008:20:03:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -165.82.137.78 - - [24/Feb/2008:20:04:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -165.82.137.78 - - [24/Feb/2008:20:04:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -201.141.93.154 - - [24/Feb/2008:20:05:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.141.93.154 - - [24/Feb/2008:20:05:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.141.93.154 - - [24/Feb/2008:20:05:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.141.93.154 - - [24/Feb/2008:20:06:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.141.93.154 - - [24/Feb/2008:20:06:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -210.143.35.13 - - [24/Feb/2008:20:09:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -210.143.35.13 - - [24/Feb/2008:20:09:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -165.95.24.194 - - [24/Feb/2008:20:10:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -165.95.24.194 - - [24/Feb/2008:20:10:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.141.93.154 - - [24/Feb/2008:20:13:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.209.71.13 - - [24/Feb/2008:20:21:31 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -189.13.184.120 - - [24/Feb/2008:20:33:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.13.184.120 - - [24/Feb/2008:20:33:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.13.184.120 - - [24/Feb/2008:20:44:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.195.58.174 - - [24/Feb/2008:20:44:24 -0600] "GET /ply/ HTTP/1.0" 304 - -165.82.137.78 - - [24/Feb/2008:20:45:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -67.195.58.160 - - [24/Feb/2008:20:45:23 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 -67.195.58.174 - - [24/Feb/2008:20:45:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -67.195.58.188 - - [24/Feb/2008:20:46:00 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 -67.195.58.188 - - [24/Feb/2008:20:46:00 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -67.195.58.178 - - [24/Feb/2008:20:46:39 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 -67.186.98.20 - - [24/Feb/2008:20:48:18 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -67.186.98.20 - - [24/Feb/2008:20:48:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [24/Feb/2008:20:48:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -131.107.0.112 - - [24/Feb/2008:20:54:44 -0600] "GET /robots.txt HTTP/1.1" 304 - -131.107.0.112 - - [24/Feb/2008:20:55:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -220.237.12.253 - - [24/Feb/2008:20:56:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 -220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.237.12.253 - - [24/Feb/2008:20:58:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.237.12.253 - - [24/Feb/2008:20:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.237.12.253 - - [24/Feb/2008:20:59:08 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -67.176.147.11 - - [24/Feb/2008:20:59:21 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -189.13.184.120 - - [24/Feb/2008:21:01:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -165.82.137.78 - - [24/Feb/2008:21:02:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -165.82.137.78 - - [24/Feb/2008:21:02:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 -165.82.137.78 - - [24/Feb/2008:21:03:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -61.135.166.102 - - [24/Feb/2008:21:04:22 -0600] "GET / HTTP/1.1" 200 4447 -220.237.12.253 - - [24/Feb/2008:21:04:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.143 - - [24/Feb/2008:21:04:29 -0600] "GET /swill/writing.html HTTP/1.0" 404 133 -220.237.12.253 - - [24/Feb/2008:21:05:37 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 -68.72.123.219 - - [24/Feb/2008:21:06:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -125.16.133.35 - - [24/Feb/2008:21:08:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -125.16.133.35 - - [24/Feb/2008:21:08:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -165.82.137.78 - - [24/Feb/2008:21:10:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -165.82.137.78 - - [24/Feb/2008:21:10:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -165.82.137.78 - - [24/Feb/2008:21:10:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 -83.249.251.158 - - [24/Feb/2008:21:11:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -83.249.251.158 - - [24/Feb/2008:21:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -125.16.133.35 - - [24/Feb/2008:21:11:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.72.123.219 - - [24/Feb/2008:21:12:25 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -68.72.123.219 - - [24/Feb/2008:21:12:32 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -74.6.20.207 - - [24/Feb/2008:21:18:07 -0600] "GET /writing.html HTTP/1.0" 200 2871 -220.237.12.253 - - [24/Feb/2008:21:28:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [24/Feb/2008:21:30:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [24/Feb/2008:21:31:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -202.181.80.140 - - [24/Feb/2008:21:34:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -202.181.80.140 - - [24/Feb/2008:21:34:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -202.181.80.140 - - [24/Feb/2008:21:35:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -202.181.80.140 - - [24/Feb/2008:21:35:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 -139.175.68.252 - - [24/Feb/2008:21:40:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -88.191.19.81 - - [24/Feb/2008:21:44:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -86.219.203.174 - - [24/Feb/2008:21:45:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -86.219.203.174 - - [24/Feb/2008:21:45:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.219.203.174 - - [24/Feb/2008:21:45:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -86.219.203.174 - - [24/Feb/2008:21:48:00 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -86.219.203.174 - - [24/Feb/2008:21:48:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -67.175.229.192 - - [24/Feb/2008:21:50:17 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 -67.175.229.192 - - [24/Feb/2008:21:50:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.143 - - [24/Feb/2008:21:50:37 -0600] "GET /cv.html HTTP/1.0" 200 31798 -67.176.147.11 - - [24/Feb/2008:22:03:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -189.6.242.136 - - [24/Feb/2008:22:12:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.6.242.136 - - [24/Feb/2008:22:12:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.6.242.136 - - [24/Feb/2008:22:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.6.242.136 - - [24/Feb/2008:22:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.6.242.136 - - [24/Feb/2008:22:15:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -38.98.120.84 - - [24/Feb/2008:22:20:29 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [24/Feb/2008:22:20:29 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [24/Feb/2008:22:20:56 -0600] "GET / HTTP/1.1" 200 4447 -207.229.184.99 - - [24/Feb/2008:22:22:52 -0600] "GET /dynamic/ HTTP/1.1" 304 - -207.229.184.99 - - [24/Feb/2008:22:22:55 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -74.6.28.156 - - [24/Feb/2008:22:26:48 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 -74.6.24.162 - - [24/Feb/2008:22:29:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE088.HTM HTTP/1.0" 200 1723 -74.6.26.198 - - [24/Feb/2008:22:30:06 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 -139.175.68.252 - - [24/Feb/2008:22:33:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -139.175.68.252 - - [24/Feb/2008:22:33:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [24/Feb/2008:22:33:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.200 - - [24/Feb/2008:22:48:41 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 -220.181.38.169 - - [24/Feb/2008:23:04:32 -0600] "GET / HTTP/1.1" 200 4447 -61.135.166.102 - - [24/Feb/2008:23:04:32 -0600] "GET / HTTP/1.1" 200 4447 -68.83.161.100 - - [24/Feb/2008:23:06:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -68.83.161.100 - - [24/Feb/2008:23:06:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.83.161.100 - - [24/Feb/2008:23:06:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.83.161.100 - - [24/Feb/2008:23:06:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.83.161.100 - - [24/Feb/2008:23:06:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -68.34.230.143 - - [24/Feb/2008:23:11:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -68.34.230.143 - - [24/Feb/2008:23:11:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.34.230.143 - - [24/Feb/2008:23:11:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -58.211.255.253 - - [24/Feb/2008:23:11:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -58.211.255.253 - - [24/Feb/2008:23:11:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -66.116.72.114 - - [24/Feb/2008:23:13:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.116.72.114 - - [24/Feb/2008:23:13:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.23.120 - - [24/Feb/2008:23:14:09 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.0" 200 375 -207.229.184.99 - - [24/Feb/2008:23:22:11 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -207.229.184.99 - - [24/Feb/2008:23:22:22 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -74.6.22.143 - - [24/Feb/2008:23:35:43 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE001.HTM HTTP/1.0" 200 1210 -67.186.98.20 - - [24/Feb/2008:23:35:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [24/Feb/2008:23:39:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -139.175.68.252 - - [24/Feb/2008:23:39:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [24/Feb/2008:23:39:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.143 - - [24/Feb/2008:23:40:11 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 -139.175.68.252 - - [24/Feb/2008:23:42:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [24/Feb/2008:23:43:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -198.247.172.9 - - [24/Feb/2008:23:49:24 -0600] "GET / HTTP/1.1" 200 4447 -41.196.193.85 - - [24/Feb/2008:23:49:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 -41.196.193.85 - - [24/Feb/2008:23:49:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -41.196.193.85 - - [24/Feb/2008:23:49:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.84.154.13 - - [24/Feb/2008:23:50:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.25.106 - - [24/Feb/2008:23:55:14 -0600] "GET /photos/u505/ HTTP/1.0" 404 133 -217.196.43.134 - - [25/Feb/2008:00:05:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 -70.242.107.51 - - [25/Feb/2008:00:11:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.145.165.82 - - [25/Feb/2008:00:15:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 -138.206.161.230 - - [25/Feb/2008:00:16:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -138.206.161.230 - - [25/Feb/2008:00:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -138.206.161.230 - - [25/Feb/2008:00:16:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -148.243.90.225 - - [25/Feb/2008:00:17:55 -0600] "GET /ply/ HTTP/1.1" 304 - -74.6.22.143 - - [25/Feb/2008:00:18:25 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 -57.73.25.166 - - [25/Feb/2008:00:22:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -139.175.68.252 - - [25/Feb/2008:00:24:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.143 - - [25/Feb/2008:00:24:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -74.6.25.20 - - [25/Feb/2008:00:26:13 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.195.58.169 - - [25/Feb/2008:00:26:14 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 -211.127.232.14 - - [25/Feb/2008:00:26:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -211.127.232.14 - - [25/Feb/2008:00:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -211.127.232.14 - - [25/Feb/2008:00:26:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -211.127.232.14 - - [25/Feb/2008:00:26:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -211.127.232.14 - - [25/Feb/2008:00:28:47 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 -211.127.232.14 - - [25/Feb/2008:00:28:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 2813 -211.127.232.14 - - [25/Feb/2008:00:28:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -211.127.232.14 - - [25/Feb/2008:00:29:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Perl5Typemaps HTTP/1.1" 200 3613 -211.127.232.14 - - [25/Feb/2008:00:29:38 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 -71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 12705 -71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.192.28.26 - - [25/Feb/2008:00:35:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -71.192.28.26 - - [25/Feb/2008:00:35:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.192.28.26 - - [25/Feb/2008:00:37:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -71.192.28.26 - - [25/Feb/2008:00:37:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -67.195.58.175 - - [25/Feb/2008:00:46:01 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 -74.6.22.143 - - [25/Feb/2008:00:48:20 -0600] "GET /index.html HTTP/1.0" 200 4447 -88.191.19.81 - - [25/Feb/2008:00:48:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.200.69 - - [25/Feb/2008:00:49:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.200.69 - - [25/Feb/2008:00:49:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.126.89.51 - - [25/Feb/2008:00:50:17 -0600] "GET / HTTP/1.1" 200 4447 -86.15.171.18 - - [25/Feb/2008:00:58:36 -0600] "GET /ply HTTP/1.1" 301 242 -86.15.171.18 - - [25/Feb/2008:00:58:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -125.35.5.39 - - [25/Feb/2008:01:02:32 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -125.35.5.39 - - [25/Feb/2008:01:02:33 -0600] "GET /ply/ HTTP/1.0" 200 8018 -125.35.5.39 - - [25/Feb/2008:01:02:34 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -146.230.128.29 - - [25/Feb/2008:01:03:58 -0600] "GET /cv.html HTTP/1.0" 200 31798 -146.230.128.29 - - [25/Feb/2008:01:03:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -146.230.128.29 - - [25/Feb/2008:01:03:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -61.135.166.102 - - [25/Feb/2008:01:04:27 -0600] "GET / HTTP/1.1" 200 4447 -220.181.38.169 - - [25/Feb/2008:01:04:54 -0600] "GET / HTTP/1.1" 200 4447 -146.230.128.29 - - [25/Feb/2008:01:05:20 -0600] "GET / HTTP/1.0" 200 4447 -146.230.128.29 - - [25/Feb/2008:01:05:21 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 60025 -139.175.68.252 - - [25/Feb/2008:01:05:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -139.175.68.252 - - [25/Feb/2008:01:05:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -139.175.68.252 - - [25/Feb/2008:01:05:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 -146.230.128.29 - - [25/Feb/2008:01:06:15 -0600] "GET /python.html HTTP/1.0" 200 18870 -146.230.128.29 - - [25/Feb/2008:01:06:16 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -146.230.128.29 - - [25/Feb/2008:01:06:17 -0600] "GET /software.html HTTP/1.0" 200 3163 -146.230.128.29 - - [25/Feb/2008:01:06:33 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 -148.245.19.94 - - [25/Feb/2008:01:08:25 -0600] "GET /ply HTTP/1.1" 301 242 -148.245.19.94 - - [25/Feb/2008:01:08:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 -148.245.19.94 - - [25/Feb/2008:01:08:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -148.245.19.94 - - [25/Feb/2008:01:08:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.160 - - [25/Feb/2008:01:08:56 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 304 - -71.192.28.26 - - [25/Feb/2008:01:10:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.175.68.252 - - [25/Feb/2008:01:18:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -139.175.68.252 - - [25/Feb/2008:01:18:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -217.172.44.82 - - [25/Feb/2008:01:22:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 -208.80.193.48 - - [25/Feb/2008:01:22:20 -0600] "GET / HTTP/1.1" 200 4447 -198.54.202.210 - - [25/Feb/2008:01:25:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -198.54.202.194 - - [25/Feb/2008:01:25:54 -0600] "GET /favicon.gif HTTP/1.1" 404 133 -70.90.215.85 - - [25/Feb/2008:01:28:06 -0600] "GET /robots.txt HTTP/1.0" 200 71 -70.90.215.85 - - [25/Feb/2008:01:28:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 -70.90.215.85 - - [25/Feb/2008:01:28:54 -0600] "GET /ply HTTP/1.0" 301 230 -70.90.215.85 - - [25/Feb/2008:01:28:55 -0600] "GET /ply/ HTTP/1.0" 200 8018 -203.78.221.48 - - [25/Feb/2008:01:29:16 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -203.78.221.48 - - [25/Feb/2008:01:29:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -138.246.7.155 - - [25/Feb/2008:01:38:12 -0600] "GET /ply/ HTTP/1.1" 304 - -138.246.7.155 - - [25/Feb/2008:01:38:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -58.185.239.242 - - [25/Feb/2008:01:39:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -58.185.239.242 - - [25/Feb/2008:01:39:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.185.239.242 - - [25/Feb/2008:01:39:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -69.137.228.16 - - [25/Feb/2008:01:46:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.137.228.16 - - [25/Feb/2008:01:46:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -69.137.228.16 - - [25/Feb/2008:01:46:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.185.239.242 - - [25/Feb/2008:01:48:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 -58.185.239.242 - - [25/Feb/2008:01:48:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -58.185.239.242 - - [25/Feb/2008:01:48:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -84.177.46.231 - - [25/Feb/2008:01:50:08 -0600] "GET /python.html HTTP/1.1" 200 18870 -84.177.46.231 - - [25/Feb/2008:01:50:09 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -84.177.46.231 - - [25/Feb/2008:01:50:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.177.46.231 - - [25/Feb/2008:01:50:10 -0600] "GET /training.html HTTP/1.1" 200 6154 -206.51.237.114 - - [25/Feb/2008:01:51:23 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -88.255.192.42 - - [25/Feb/2008:01:51:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -212.247.11.155 - - [25/Feb/2008:01:51:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -200.65.127.161 - - [25/Feb/2008:01:51:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -71.38.14.119 - - [25/Feb/2008:01:54:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -71.38.14.119 - - [25/Feb/2008:01:54:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 -69.137.228.16 - - [25/Feb/2008:01:57:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -138.246.7.155 - - [25/Feb/2008:02:01:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -87.194.30.240 - - [25/Feb/2008:02:04:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 -87.194.30.240 - - [25/Feb/2008:02:04:30 -0600] "GET /ply HTTP/1.1" 301 242 -87.194.30.240 - - [25/Feb/2008:02:04:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.108.70.84 - - [25/Feb/2008:02:05:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.108.70.84 - - [25/Feb/2008:02:05:46 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -217.237.70.128 - - [25/Feb/2008:02:08:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 -217.237.70.128 - - [25/Feb/2008:02:08:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -75.6.228.64 - - [25/Feb/2008:02:18:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -75.6.228.64 - - [25/Feb/2008:02:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.6.228.64 - - [25/Feb/2008:02:18:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -75.6.228.64 - - [25/Feb/2008:02:18:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/PhP HTTP/1.1" 200 3606 -75.6.228.64 - - [25/Feb/2008:02:18:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -66.232.113.194 - - [25/Feb/2008:02:22:28 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 -200.133.15.2 - - [25/Feb/2008:02:22:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -219.93.178.162 - - [25/Feb/2008:02:22:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -217.255.38.100 - - [25/Feb/2008:02:28:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -217.255.38.100 - - [25/Feb/2008:02:28:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.255.38.100 - - [25/Feb/2008:02:28:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.255.38.100 - - [25/Feb/2008:02:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.196.6.232 - - [25/Feb/2008:02:30:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.137.228.16 - - [25/Feb/2008:02:33:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.137.228.16 - - [25/Feb/2008:02:33:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -69.137.228.16 - - [25/Feb/2008:02:34:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.137.228.16 - - [25/Feb/2008:02:34:18 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -69.137.228.16 - - [25/Feb/2008:02:34:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.137.228.16 - - [25/Feb/2008:02:36:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.137.228.16 - - [25/Feb/2008:02:37:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -130.79.100.39 - - [25/Feb/2008:02:41:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -130.79.100.39 - - [25/Feb/2008:02:41:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.79.100.39 - - [25/Feb/2008:02:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1554 -130.79.100.39 - - [25/Feb/2008:02:41:24 -0600] "GET /cgi-bin/wiki.pl?UninstantiatedTemplates HTTP/1.1" 200 2091 -130.79.100.39 - - [25/Feb/2008:02:41:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -130.79.100.39 - - [25/Feb/2008:02:41:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -74.6.22.143 - - [25/Feb/2008:02:45:47 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -65.55.208.122 - - [25/Feb/2008:02:49:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.122 - - [25/Feb/2008:02:49:52 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE076.HTM HTTP/1.1" 304 - -74.6.23.75 - - [25/Feb/2008:02:52:21 -0600] "GET /photos/u505/pages/IMG_1502.htm HTTP/1.0" 404 133 -217.255.38.100 - - [25/Feb/2008:02:54:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.255.38.100 - - [25/Feb/2008:02:54:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -81.255.106.203 - - [25/Feb/2008:02:56:49 -0600] "GET /ply/ HTTP/1.1" 304 - -74.6.22.143 - - [25/Feb/2008:02:58:08 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 -61.135.166.102 - - [25/Feb/2008:03:04:19 -0600] "GET / HTTP/1.1" 200 4447 -220.181.38.169 - - [25/Feb/2008:03:05:28 -0600] "GET / HTTP/1.1" 200 4447 -194.105.57.11 - - [25/Feb/2008:03:06:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -194.105.57.12 - - [25/Feb/2008:03:06:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -194.105.57.12 - - [25/Feb/2008:03:06:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.15 - - [25/Feb/2008:03:11:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.232.15 - - [25/Feb/2008:03:11:55 -0600] "GET /python.html HTTP/1.1" 200 18870 -74.6.7.107 - - [25/Feb/2008:03:13:02 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.0" 200 288790 -151.96.0.8 - - [25/Feb/2008:03:13:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -151.96.0.8 - - [25/Feb/2008:03:13:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -85.185.76.213 - - [25/Feb/2008:03:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -85.185.76.213 - - [25/Feb/2008:03:16:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -203.200.218.2 - - [25/Feb/2008:03:22:13 -0600] "GET /ply/ HTTP/1.0" 304 - -203.200.218.2 - - [25/Feb/2008:03:22:13 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - -193.190.210.85 - - [25/Feb/2008:03:23:06 -0600] "GET /ply/ HTTP/1.1" 304 - -203.200.35.12 - - [25/Feb/2008:03:28:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 -203.200.35.12 - - [25/Feb/2008:03:28:42 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -203.200.35.12 - - [25/Feb/2008:03:28:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -203.200.35.12 - - [25/Feb/2008:03:28:53 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -203.200.218.2 - - [25/Feb/2008:03:29:43 -0600] "GET /ply/ HTTP/1.0" 304 - -203.200.218.2 - - [25/Feb/2008:03:29:43 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - -203.200.218.2 - - [25/Feb/2008:03:29:55 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -203.200.218.2 - - [25/Feb/2008:03:30:17 -0600] "GET /python.html HTTP/1.0" 200 18870 -203.200.218.2 - - [25/Feb/2008:03:30:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -203.200.218.2 - - [25/Feb/2008:03:34:35 -0600] "GET /ply/ HTTP/1.0" 304 - -66.201.54.42 - - [25/Feb/2008:03:34:35 -0600] "GET /cv.html HTTP/1.1" 200 31798 -66.201.54.42 - - [25/Feb/2008:03:34:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.200.218.2 - - [25/Feb/2008:03:34:36 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - -66.201.54.42 - - [25/Feb/2008:03:34:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.201.54.42 - - [25/Feb/2008:03:34:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.201.54.42 - - [25/Feb/2008:03:34:40 -0600] "GET / HTTP/1.1" 200 4447 -66.201.54.42 - - [25/Feb/2008:03:34:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -203.200.218.2 - - [25/Feb/2008:03:34:43 -0600] "GET /ply/ply.html HTTP/1.0" 304 - -72.32.58.119 - - [25/Feb/2008:03:34:46 -0600] "GET /cv.html HTTP/1.1" 200 31798 -72.32.58.119 - - [25/Feb/2008:03:34:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [25/Feb/2008:03:35:14 -0600] "GET /robots.txt HTTP/1.1" 200 71 -66.249.65.37 - - [25/Feb/2008:03:35:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE014.HTM HTTP/1.1" 200 1232 -67.195.58.174 - - [25/Feb/2008:03:35:56 -0600] "GET /ply/ HTTP/1.0" 304 - -66.249.65.37 - - [25/Feb/2008:03:36:34 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.1" 200 1336 -66.249.65.37 - - [25/Feb/2008:03:39:15 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE063.HTM HTTP/1.1" 200 984 -124.30.116.190 - - [25/Feb/2008:03:42:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 -124.30.116.190 - - [25/Feb/2008:03:42:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -124.30.116.190 - - [25/Feb/2008:03:42:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.65.240.234 - - [25/Feb/2008:03:44:19 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -124.30.116.190 - - [25/Feb/2008:03:44:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -68.142.212.161 - - [25/Feb/2008:03:51:20 -0600] "GET /robots.txt HTTP/1.0" 200 71 -68.142.212.161 - - [25/Feb/2008:03:51:39 -0600] "GET /images/BadDave1.jpg HTTP/1.0" 304 - -139.175.68.252 - - [25/Feb/2008:03:53:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -139.175.68.252 - - [25/Feb/2008:03:53:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -139.175.68.252 - - [25/Feb/2008:03:53:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -68.142.212.161 - - [25/Feb/2008:03:55:08 -0600] "GET /images/davechina.jpg HTTP/1.0" 304 - -66.201.54.42 - - [25/Feb/2008:03:59:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.89.228.178 - - [25/Feb/2008:04:33:30 -0600] "GET / HTTP/1.0" 200 4447 -130.225.195.70 - - [25/Feb/2008:04:47:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 -130.225.195.70 - - [25/Feb/2008:04:47:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -130.225.195.70 - - [25/Feb/2008:04:47:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.225.195.70 - - [25/Feb/2008:04:47:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.126.185 - - [25/Feb/2008:04:48:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.126.185 - - [25/Feb/2008:04:48:18 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 -61.135.166.102 - - [25/Feb/2008:05:04:21 -0600] "GET / HTTP/1.1" 200 4447 -84.110.122.157 - - [25/Feb/2008:05:08:30 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.122.157 - - [25/Feb/2008:05:08:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 -130.225.195.70 - - [25/Feb/2008:05:12:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.154.251.108 - - [25/Feb/2008:05:13:16 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.154.251.108 - - [25/Feb/2008:05:13:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.154.251.108 - - [25/Feb/2008:05:13:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.154.251.108 - - [25/Feb/2008:05:14:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -82.154.251.108 - - [25/Feb/2008:05:14:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -82.154.251.108 - - [25/Feb/2008:05:14:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -74.6.20.35 - - [25/Feb/2008:05:24:35 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE084.HTM HTTP/1.0" 200 1474 -74.6.22.150 - - [25/Feb/2008:05:26:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.22.150 - - [25/Feb/2008:05:26:54 -0600] "GET /swill/swill-0.1.tar.gz HTTP/1.0" 200 119170 -58.107.212.3 - - [25/Feb/2008:05:34:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 -137.226.57.203 - - [25/Feb/2008:05:38:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -137.226.57.203 - - [25/Feb/2008:05:38:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [25/Feb/2008:05:38:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [25/Feb/2008:05:38:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -74.6.25.148 - - [25/Feb/2008:05:48:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 -82.107.147.45 - - [25/Feb/2008:05:48:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.107.147.45 - - [25/Feb/2008:05:48:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.107.147.45 - - [25/Feb/2008:05:49:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -82.107.147.45 - - [25/Feb/2008:05:49:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 -82.107.147.45 - - [25/Feb/2008:05:49:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -82.107.147.45 - - [25/Feb/2008:05:49:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -82.107.147.45 - - [25/Feb/2008:05:49:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -88.191.19.81 - - [25/Feb/2008:05:52:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 -58.24.206.144 - - [25/Feb/2008:05:53:28 -0600] "GET / HTTP/1.1" 200 4447 -58.24.206.144 - - [25/Feb/2008:05:53:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -58.24.206.144 - - [25/Feb/2008:05:54:13 -0600] "GET /python.html HTTP/1.1" 200 18870 -58.24.206.144 - - [25/Feb/2008:05:54:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -58.24.206.144 - - [25/Feb/2008:05:54:34 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -58.24.206.144 - - [25/Feb/2008:05:54:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -131.159.46.32 - - [25/Feb/2008:05:54:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -58.24.206.144 - - [25/Feb/2008:05:55:22 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -89.102.3.36 - - [25/Feb/2008:05:55:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -89.102.3.36 - - [25/Feb/2008:05:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [25/Feb/2008:05:57:56 -0600] "GET /dynamic/sd.html HTTP/1.1" 304 - -58.24.206.144 - - [25/Feb/2008:05:58:06 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -193.172.19.20 - - [25/Feb/2008:05:59:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 -193.172.19.20 - - [25/Feb/2008:05:59:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -193.172.19.20 - - [25/Feb/2008:05:59:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -88.166.41.113 - - [25/Feb/2008:06:01:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -88.166.41.113 - - [25/Feb/2008:06:02:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.110.220.12 - - [25/Feb/2008:06:15:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -194.110.220.12 - - [25/Feb/2008:06:15:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -194.110.220.12 - - [25/Feb/2008:06:15:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.110.220.12 - - [25/Feb/2008:06:24:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.110.220.12 - - [25/Feb/2008:06:24:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -92.112.194.106 - - [25/Feb/2008:06:35:05 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -38.98.120.84 - - [25/Feb/2008:06:38:49 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [25/Feb/2008:06:38:49 -0600] "GET / HTTP/1.1" 200 4447 -125.16.133.35 - - [25/Feb/2008:06:44:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -138.246.7.155 - - [25/Feb/2008:06:45:59 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -200.21.98.7 - - [25/Feb/2008:06:50:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -200.21.98.7 - - [25/Feb/2008:06:50:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -62.240.69.90 - - [25/Feb/2008:06:52:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -62.240.69.90 - - [25/Feb/2008:06:52:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.240.69.90 - - [25/Feb/2008:06:52:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.215.248.26 - - [25/Feb/2008:06:55:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -124.215.248.26 - - [25/Feb/2008:06:55:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.215.248.26 - - [25/Feb/2008:06:55:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.215.248.26 - - [25/Feb/2008:06:55:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.57.248.115 - - [25/Feb/2008:06:55:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.57.248.115 - - [25/Feb/2008:06:55:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -80.57.248.115 - - [25/Feb/2008:06:55:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -196.21.126.135 - - [25/Feb/2008:06:55:07 -0600] "GET /ply/ HTTP/1.0" 200 8018 -192.54.144.229 - - [25/Feb/2008:06:55:20 -0600] "GET /ply HTTP/1.1" 301 242 -192.54.144.229 - - [25/Feb/2008:06:55:21 -0600] "GET /ply/ HTTP/1.1" 304 - -80.57.248.115 - - [25/Feb/2008:06:55:43 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -196.21.126.135 - - [25/Feb/2008:06:55:55 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -124.215.248.26 - - [25/Feb/2008:06:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.215.248.26 - - [25/Feb/2008:06:57:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.215.248.26 - - [25/Feb/2008:06:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [25/Feb/2008:06:58:02 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [25/Feb/2008:06:58:04 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -201.236.226.90 - - [25/Feb/2008:06:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [25/Feb/2008:06:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [25/Feb/2008:06:58:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -124.215.248.26 - - [25/Feb/2008:07:00:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -220.181.38.169 - - [25/Feb/2008:07:04:30 -0600] "GET / HTTP/1.1" 200 4447 -84.89.249.77 - - [25/Feb/2008:07:04:44 -0600] "GET /ply/ HTTP/1.1" 304 - -84.89.249.77 - - [25/Feb/2008:07:04:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -217.196.43.134 - - [25/Feb/2008:07:05:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -124.215.248.26 - - [25/Feb/2008:07:05:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.215.248.26 - - [25/Feb/2008:07:07:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -140.94.82.18 - - [25/Feb/2008:07:10:52 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -140.94.82.18 - - [25/Feb/2008:07:11:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -200.255.103.130 - - [25/Feb/2008:07:11:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 2460 -200.255.103.130 - - [25/Feb/2008:07:12:07 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 408 -200.255.103.130 - - [25/Feb/2008:07:12:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 410 -200.255.103.130 - - [25/Feb/2008:07:12:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 912 -200.255.103.130 - - [25/Feb/2008:07:12:24 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 2460 -140.94.82.18 - - [25/Feb/2008:07:13:45 -0600] "GET /cv.html HTTP/1.0" 200 31798 -130.235.34.165 - - [25/Feb/2008:07:14:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 -130.235.34.165 - - [25/Feb/2008:07:14:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.235.34.165 - - [25/Feb/2008:07:15:21 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -71.62.75.201 - - [25/Feb/2008:07:16:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.255.103.130 - - [25/Feb/2008:07:18:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 -71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET / HTTP/1.1" 200 4447 -71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [25/Feb/2008:07:24:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -71.57.91.136 - - [25/Feb/2008:07:24:35 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -74.6.19.207 - - [25/Feb/2008:07:24:42 -0600] "GET /photos/wind/pages/IMG_1270.htm HTTP/1.0" 404 133 -82.135.63.177 - - [25/Feb/2008:07:29:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.135.63.177 - - [25/Feb/2008:07:29:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.8.219.19 - - [25/Feb/2008:07:32:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -194.8.219.19 - - [25/Feb/2008:07:32:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -194.8.219.19 - - [25/Feb/2008:07:32:04 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /ply/ HTTP/1.0" 200 8018 -84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -194.8.219.19 - - [25/Feb/2008:07:32:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 -82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.195.186.41 - - [25/Feb/2008:07:38:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.195.186.41 - - [25/Feb/2008:07:38:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -192.93.158.26 - - [25/Feb/2008:07:39:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -192.93.158.26 - - [25/Feb/2008:07:39:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -192.93.158.26 - - [25/Feb/2008:07:39:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -84.237.120.134 - - [25/Feb/2008:07:42:04 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.221.197.20 - - [25/Feb/2008:07:43:35 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -124.30.116.190 - - [25/Feb/2008:07:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -74.6.22.150 - - [25/Feb/2008:07:54:27 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5105 -74.6.26.75 - - [25/Feb/2008:07:55:55 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 200 64742 -84.237.120.134 - - [25/Feb/2008:07:56:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -82.195.186.41 - - [25/Feb/2008:07:56:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -82.195.186.41 - - [25/Feb/2008:07:56:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -194.237.142.6 - - [25/Feb/2008:08:01:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -194.237.142.6 - - [25/Feb/2008:08:01:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.191.19.81 - - [25/Feb/2008:08:02:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.98.120.84 - - [25/Feb/2008:08:04:55 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [25/Feb/2008:08:04:56 -0600] "GET / HTTP/1.1" 200 4447 -84.237.120.134 - - [25/Feb/2008:08:05:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -59.124.114.4 - - [25/Feb/2008:08:05:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -59.124.114.4 - - [25/Feb/2008:08:05:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -59.124.114.4 - - [25/Feb/2008:08:06:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 -71.57.91.136 - - [25/Feb/2008:08:07:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 -71.57.91.136 - - [25/Feb/2008:08:07:49 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 121887 -71.57.91.136 - - [25/Feb/2008:08:07:49 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246555 -203.109.126.184 - - [25/Feb/2008:08:08:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 -203.109.126.184 - - [25/Feb/2008:08:08:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -75.22.21.146 - - [25/Feb/2008:08:09:43 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 -75.22.21.146 - - [25/Feb/2008:08:09:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [25/Feb/2008:08:09:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -59.124.114.4 - - [25/Feb/2008:08:14:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 -72.14.220.136 - - [25/Feb/2008:08:15:44 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -200.195.66.68 - - [25/Feb/2008:08:16:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -72.14.220.136 - - [25/Feb/2008:08:16:35 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -72.14.220.136 - - [25/Feb/2008:08:16:37 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -72.14.220.136 - - [25/Feb/2008:08:17:01 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -200.195.66.68 - - [25/Feb/2008:08:17:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 -84.20.132.177 - - [25/Feb/2008:08:18:06 -0600] "GET /ply HTTP/1.1" 301 242 -84.20.132.177 - - [25/Feb/2008:08:18:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -84.20.132.177 - - [25/Feb/2008:08:18:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -84.20.132.177 - - [25/Feb/2008:08:18:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.20.132.177 - - [25/Feb/2008:08:18:19 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -84.20.132.177 - - [25/Feb/2008:08:18:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.23.158 - - [25/Feb/2008:08:18:19 -0600] "GET /dynamic/sd.html HTTP/1.0" 200 1873 -217.153.4.50 - - [25/Feb/2008:08:18:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -217.153.4.50 - - [25/Feb/2008:08:18:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.153.4.50 - - [25/Feb/2008:08:18:39 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 -217.153.4.50 - - [25/Feb/2008:08:18:40 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 -200.195.66.68 - - [25/Feb/2008:08:18:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -217.153.4.50 - - [25/Feb/2008:08:18:44 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -84.20.132.177 - - [25/Feb/2008:08:18:50 -0600] "GET /ply/README HTTP/1.1" 200 8605 -200.195.66.68 - - [25/Feb/2008:08:19:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -200.195.66.68 - - [25/Feb/2008:08:19:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 -200.195.66.68 - - [25/Feb/2008:08:19:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.0" 200 2290 -217.153.4.50 - - [25/Feb/2008:08:19:46 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4769 -200.195.66.68 - - [25/Feb/2008:08:19:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.0" 200 4413 -200.195.66.68 - - [25/Feb/2008:08:19:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.0" 200 1641 -200.195.66.68 - - [25/Feb/2008:08:20:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 -217.153.4.50 - - [25/Feb/2008:08:20:08 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/SwigInPython HTTP/1.1" 200 1704 -217.153.4.50 - - [25/Feb/2008:08:20:12 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 -66.212.158.132 - - [25/Feb/2008:08:20:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 -217.153.4.50 - - [25/Feb/2008:08:20:57 -0600] "GET /cgi-bin/wiki.pl?CAsAHighLevelLanguage HTTP/1.1" 200 2235 -200.195.66.68 - - [25/Feb/2008:08:21:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.0" 200 4986 -217.153.4.50 - - [25/Feb/2008:08:21:24 -0600] "GET /cgi-bin/wiki.pl?ConfigurationMemory HTTP/1.1" 200 3793 -217.153.4.50 - - [25/Feb/2008:08:21:40 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 -67.195.45.214 - - [25/Feb/2008:08:22:04 -0600] "GET / HTTP/1.0" 200 4447 -200.155.226.207 - - [25/Feb/2008:08:28:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -200.155.226.207 - - [25/Feb/2008:08:28:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -200.155.226.207 - - [25/Feb/2008:08:28:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.155.226.207 - - [25/Feb/2008:08:28:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.237.142.6 - - [25/Feb/2008:08:31:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.81.229.55 - - [25/Feb/2008:08:33:13 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 225488 -64.81.229.55 - - [25/Feb/2008:08:33:14 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246552 -82.195.186.41 - - [25/Feb/2008:08:33:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -82.195.186.41 - - [25/Feb/2008:08:33:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -217.65.240.234 - - [25/Feb/2008:08:33:44 -0600] "GET /ply/ HTTP/1.0" 200 8018 -217.65.240.234 - - [25/Feb/2008:08:33:45 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -216.72.46.162 - - [25/Feb/2008:08:34:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 -216.72.46.162 - - [25/Feb/2008:08:34:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -216.72.46.162 - - [25/Feb/2008:08:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.81.229.55 - - [25/Feb/2008:08:35:03 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213580 -128.221.197.20 - - [25/Feb/2008:08:37:40 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -192.35.17.30 - - [25/Feb/2008:08:37:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -192.35.17.30 - - [25/Feb/2008:08:38:00 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -220.227.29.99 - - [25/Feb/2008:08:39:52 -0600] "GET /python.html HTTP/1.0" 200 18870 -220.227.29.99 - - [25/Feb/2008:08:39:53 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -220.227.29.99 - - [25/Feb/2008:08:39:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -220.227.29.99 - - [25/Feb/2008:08:40:16 -0600] "GET /training.html HTTP/1.0" 200 6154 -220.227.29.99 - - [25/Feb/2008:08:40:24 -0600] "GET /software.html HTTP/1.0" 200 3163 -220.227.29.99 - - [25/Feb/2008:08:40:28 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -59.124.114.4 - - [25/Feb/2008:08:41:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -59.124.114.4 - - [25/Feb/2008:08:42:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -59.124.114.4 - - [25/Feb/2008:08:42:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -66.232.113.62 - - [25/Feb/2008:08:51:51 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -38.101.222.130 - - [25/Feb/2008:08:51:52 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -217.172.56.49 - - [25/Feb/2008:08:51:55 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -62.197.78.103 - - [25/Feb/2008:09:01:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -62.197.78.103 - - [25/Feb/2008:09:01:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.197.78.103 - - [25/Feb/2008:09:01:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 -61.135.166.102 - - [25/Feb/2008:09:04:23 -0600] "GET / HTTP/1.1" 200 4447 -220.181.38.169 - - [25/Feb/2008:09:07:34 -0600] "GET / HTTP/1.1" 200 4447 -132.207.44.190 - - [25/Feb/2008:09:13:55 -0600] "GET /ply/ HTTP/1.1" 304 - -132.207.44.190 - - [25/Feb/2008:09:14:13 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -71.183.55.2 - - [25/Feb/2008:09:14:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.183.55.2 - - [25/Feb/2008:09:14:31 -0600] "GET /favicon.gif HTTP/1.1" 404 133 -24.15.187.198 - - [25/Feb/2008:09:19:44 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 -24.15.187.198 - - [25/Feb/2008:09:19:53 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -213.186.249.190 - - [25/Feb/2008:09:20:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 -213.186.249.190 - - [25/Feb/2008:09:20:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -213.186.249.190 - - [25/Feb/2008:09:20:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.186.249.190 - - [25/Feb/2008:09:20:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.206.100.135 - - [25/Feb/2008:09:22:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -130.206.100.135 - - [25/Feb/2008:09:22:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -130.206.100.135 - - [25/Feb/2008:09:22:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.0" 200 9795 -190.24.202.82 - - [25/Feb/2008:09:25:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 -190.24.202.82 - - [25/Feb/2008:09:25:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -190.24.202.82 - - [25/Feb/2008:09:25:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.113.44.27 - - [25/Feb/2008:09:29:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -91.113.44.27 - - [25/Feb/2008:09:30:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.113.44.27 - - [25/Feb/2008:09:30:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -91.113.44.27 - - [25/Feb/2008:09:30:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 -150.210.155.167 - - [25/Feb/2008:09:30:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -91.113.44.27 - - [25/Feb/2008:09:30:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 -130.206.100.135 - - [25/Feb/2008:09:31:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 -91.113.44.27 - - [25/Feb/2008:09:31:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 -132.207.44.190 - - [25/Feb/2008:09:32:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -75.178.179.132 - - [25/Feb/2008:09:33:00 -0600] "GET /python.html HTTP/1.1" 200 18870 -75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.178.179.132 - - [25/Feb/2008:09:33:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.237.142.6 - - [25/Feb/2008:09:35:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -194.237.142.6 - - [25/Feb/2008:09:35:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -194.237.142.6 - - [25/Feb/2008:09:35:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -194.237.142.6 - - [25/Feb/2008:09:35:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -67.195.44.107 - - [25/Feb/2008:09:37:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.195.44.109 - - [25/Feb/2008:09:37:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 -128.221.197.20 - - [25/Feb/2008:09:38:54 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -82.245.141.168 - - [25/Feb/2008:09:39:06 -0600] "GET /cv.html HTTP/1.1" 200 31798 -209.85.136.136 - - [25/Feb/2008:09:39:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 -194.237.142.6 - - [25/Feb/2008:09:39:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -209.85.136.136 - - [25/Feb/2008:09:39:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -194.237.142.6 - - [25/Feb/2008:09:39:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 -81.48.212.152 - - [25/Feb/2008:09:39:34 -0600] "GET /ply/ HTTP/1.1" 304 - -74.6.26.119 - - [25/Feb/2008:09:39:43 -0600] "GET /ply/README HTTP/1.0" 200 8605 -132.207.44.190 - - [25/Feb/2008:09:42:33 -0600] "GET /ply/README HTTP/1.1" 200 8605 -83.103.98.38 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.237.142.6 - - [25/Feb/2008:09:43:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -137.226.57.203 - - [25/Feb/2008:09:43:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -194.237.142.6 - - [25/Feb/2008:09:43:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -137.226.57.203 - - [25/Feb/2008:09:43:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -137.226.57.203 - - [25/Feb/2008:09:43:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -81.48.212.152 - - [25/Feb/2008:09:45:17 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -81.48.212.152 - - [25/Feb/2008:09:46:03 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 173064 -81.48.212.152 - - [25/Feb/2008:09:46:08 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 173271 -128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET / HTTP/1.1" 200 4447 -128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.181.110 - - [25/Feb/2008:09:48:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.181.110 - - [25/Feb/2008:09:48:42 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -128.135.181.110 - - [25/Feb/2008:09:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.181.110 - - [25/Feb/2008:09:48:44 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.135.181.110 - - [25/Feb/2008:09:48:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.114.62.34 - - [25/Feb/2008:09:50:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 -194.114.62.34 - - [25/Feb/2008:09:50:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -194.114.62.34 - - [25/Feb/2008:09:50:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.114.62.34 - - [25/Feb/2008:09:50:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.114.62.34 - - [25/Feb/2008:09:50:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.114.62.34 - - [25/Feb/2008:09:51:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.48.71.175 - - [25/Feb/2008:09:54:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.48.71.175 - - [25/Feb/2008:09:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.48.71.175 - - [25/Feb/2008:09:54:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.20.132.177 - - [25/Feb/2008:09:54:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -84.20.132.177 - - [25/Feb/2008:09:54:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -84.20.132.177 - - [25/Feb/2008:09:54:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.20.132.177 - - [25/Feb/2008:09:54:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.25.82 - - [25/Feb/2008:09:56:09 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE077.HTM HTTP/1.0" 304 - -134.160.173.1 - - [25/Feb/2008:09:57:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 -134.160.173.1 - - [25/Feb/2008:09:57:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -134.160.173.1 - - [25/Feb/2008:09:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.160.173.1 - - [25/Feb/2008:09:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.160.173.1 - - [25/Feb/2008:09:57:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.160.173.1 - - [25/Feb/2008:09:58:21 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -137.226.57.203 - - [25/Feb/2008:09:58:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -137.226.57.203 - - [25/Feb/2008:09:58:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -137.226.57.203 - - [25/Feb/2008:09:58:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 -134.160.173.1 - - [25/Feb/2008:09:59:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.160.173.1 - - [25/Feb/2008:10:02:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.113.44.27 - - [25/Feb/2008:10:07:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -91.113.44.27 - - [25/Feb/2008:10:07:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -91.113.44.27 - - [25/Feb/2008:10:07:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -91.113.44.27 - - [25/Feb/2008:10:07:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -91.113.44.27 - - [25/Feb/2008:10:07:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCallbacks HTTP/1.1" 200 3411 -165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.113.44.27 - - [25/Feb/2008:10:08:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 -91.113.44.27 - - [25/Feb/2008:10:08:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -84.20.132.177 - - [25/Feb/2008:10:11:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [25/Feb/2008:10:11:21 -0600] "HEAD /video/MVI_1516.AVI HTTP/1.1" 404 0 -128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.218.61 - - [25/Feb/2008:10:12:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.26.231.162 - - [25/Feb/2008:10:13:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.26.231.162 - - [25/Feb/2008:10:13:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.26.231.162 - - [25/Feb/2008:10:13:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -83.26.231.162 - - [25/Feb/2008:10:13:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.26.231.162 - - [25/Feb/2008:10:13:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.26.231.162 - - [25/Feb/2008:10:15:15 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 -65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -83.26.231.162 - - [25/Feb/2008:10:17:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -131.44.121.252 - - [25/Feb/2008:10:18:30 -0600] "GET /python.html HTTP/1.1" 200 18870 -131.44.121.252 - - [25/Feb/2008:10:18:31 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -213.157.91.96 - - [25/Feb/2008:10:19:08 -0600] "GET /ply HTTP/1.1" 301 242 -213.157.91.96 - - [25/Feb/2008:10:19:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 -213.157.91.96 - - [25/Feb/2008:10:19:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.157.91.96 - - [25/Feb/2008:10:19:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -194.237.142.6 - - [25/Feb/2008:10:29:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -194.237.142.6 - - [25/Feb/2008:10:29:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -128.221.197.20 - - [25/Feb/2008:10:29:13 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -194.237.142.6 - - [25/Feb/2008:10:29:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -71.63.152.218 - - [25/Feb/2008:10:30:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -71.63.152.218 - - [25/Feb/2008:10:30:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.63.152.218 - - [25/Feb/2008:10:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -71.63.152.218 - - [25/Feb/2008:10:30:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -71.63.152.218 - - [25/Feb/2008:10:30:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -128.135.11.245 - - [25/Feb/2008:10:32:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -198.247.172.8 - - [25/Feb/2008:10:33:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -72.30.226.134 - - [25/Feb/2008:10:36:29 -0600] "GET /ply/ HTTP/1.0" 200 8018 -208.22.104.18 - - [25/Feb/2008:10:38:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.174 - - [25/Feb/2008:10:41:08 -0600] "GET /ply/ HTTP/1.0" 304 - -82.166.58.226 - - [25/Feb/2008:10:45:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.221.197.20 - - [25/Feb/2008:10:49:54 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -24.7.210.64 - - [25/Feb/2008:10:54:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.7.210.64 - - [25/Feb/2008:10:54:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.22.104.18 - - [25/Feb/2008:10:55:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -196.25.255.246 - - [25/Feb/2008:10:56:18 -0600] "GET /ply/ HTTP/1.1" 304 - -82.239.61.147 - - [25/Feb/2008:10:59:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.239.61.147 - - [25/Feb/2008:10:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.7.210.64 - - [25/Feb/2008:11:01:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.7.210.64 - - [25/Feb/2008:11:01:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.151.244.16 - - [25/Feb/2008:11:06:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -58.151.244.16 - - [25/Feb/2008:11:06:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [25/Feb/2008:11:06:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -190.128.56.113 - - [25/Feb/2008:11:07:51 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -190.128.56.113 - - [25/Feb/2008:11:07:53 -0600] "GET /ply/index.html HTTP/1.1" 304 - -190.128.56.113 - - [25/Feb/2008:11:07:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -190.128.56.113 - - [25/Feb/2008:11:08:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -74.6.7.113 - - [25/Feb/2008:11:15:25 -0600] "GET /photos/wind/pages/IMG_1298.htm HTTP/1.0" 404 133 -38.104.0.30 - - [25/Feb/2008:11:27:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.104.0.30 - - [25/Feb/2008:11:27:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -169.137.105.115 - - [25/Feb/2008:11:29:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 -169.137.105.115 - - [25/Feb/2008:11:29:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -169.137.105.115 - - [25/Feb/2008:11:29:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -169.137.105.115 - - [25/Feb/2008:11:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -169.137.105.115 - - [25/Feb/2008:11:29:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -169.137.105.115 - - [25/Feb/2008:11:29:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.240.156.251 - - [25/Feb/2008:11:31:22 -0600] "GET /ply/ HTTP/1.1" 304 - -212.240.156.251 - - [25/Feb/2008:11:31:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.240.156.251 - - [25/Feb/2008:11:31:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.239.61.147 - - [25/Feb/2008:11:33:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [25/Feb/2008:11:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.67.152.241 - - [25/Feb/2008:11:41:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.67.152.241 - - [25/Feb/2008:11:41:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -67.67.152.241 - - [25/Feb/2008:11:41:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.151.244.16 - - [25/Feb/2008:11:43:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -208.22.104.18 - - [25/Feb/2008:11:50:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -80.229.34.140 - - [25/Feb/2008:11:50:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -80.229.34.140 - - [25/Feb/2008:11:50:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [25/Feb/2008:11:54:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.164.194 - - [25/Feb/2008:12:02:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.135.164.194 - - [25/Feb/2008:12:03:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -170.252.64.1 - - [25/Feb/2008:12:04:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -170.252.64.1 - - [25/Feb/2008:12:04:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -128.135.164.194 - - [25/Feb/2008:12:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.164.194 - - [25/Feb/2008:12:04:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.164.194 - - [25/Feb/2008:12:05:01 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -170.252.64.1 - - [25/Feb/2008:12:06:16 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -170.252.64.1 - - [25/Feb/2008:12:06:27 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -170.252.64.1 - - [25/Feb/2008:12:06:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -170.252.64.1 - - [25/Feb/2008:12:06:41 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -128.135.11.245 - - [25/Feb/2008:12:07:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [25/Feb/2008:12:08:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -190.30.84.93 - - [25/Feb/2008:12:15:53 -0600] "GET /ply/ HTTP/1.0" 200 8018 -68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 -68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.72.97.51 - - [25/Feb/2008:12:35:47 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -68.72.97.51 - - [25/Feb/2008:12:36:27 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -68.72.97.51 - - [25/Feb/2008:12:36:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 -70.137.112.2 - - [25/Feb/2008:12:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -221.194.136.18 - - [25/Feb/2008:12:37:19 -0600] "GET /ply HTTP/1.1" 301 242 -128.135.139.146 - - [25/Feb/2008:12:40:56 -0600] "GET /dynamic/ HTTP/1.1" 304 - -128.135.139.146 - - [25/Feb/2008:12:41:00 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -64.46.248.1 - - [25/Feb/2008:12:43:00 -0600] "GET / HTTP/1.0" 200 4447 -64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 60025 -64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -64.46.248.1 - - [25/Feb/2008:12:43:04 -0600] "GET /python.html HTTP/1.0" 200 18870 -64.46.248.1 - - [25/Feb/2008:12:43:04 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -64.46.248.1 - - [25/Feb/2008:12:43:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -210.81.80.193 - - [25/Feb/2008:12:46:39 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -71.57.91.136 - - [25/Feb/2008:12:55:11 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 186488 -71.57.91.136 - - [25/Feb/2008:12:55:11 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -71.57.91.136 - - [25/Feb/2008:12:55:12 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246555 -71.57.91.136 - - [25/Feb/2008:12:55:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [25/Feb/2008:12:55:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [25/Feb/2008:12:57:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.157.119.197 - - [25/Feb/2008:12:57:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.221.197.20 - - [25/Feb/2008:13:01:04 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -71.170.119.34 - - [25/Feb/2008:13:01:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 -71.170.119.34 - - [25/Feb/2008:13:03:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 -209.234.185.130 - - [25/Feb/2008:13:04:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -209.234.185.130 - - [25/Feb/2008:13:04:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.25.20 - - [25/Feb/2008:13:05:09 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.23.48 - - [25/Feb/2008:13:05:09 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1331 -209.234.185.130 - - [25/Feb/2008:13:05:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -83.23.227.219 - - [25/Feb/2008:13:06:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.23.227.219 - - [25/Feb/2008:13:06:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -206.75.15.14 - - [25/Feb/2008:13:06:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -206.75.15.14 - - [25/Feb/2008:13:06:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -206.75.15.14 - - [25/Feb/2008:13:06:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1400 -206.75.15.14 - - [25/Feb/2008:13:06:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1026 -128.221.197.20 - - [25/Feb/2008:13:06:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -208.163.53.37 - - [25/Feb/2008:13:09:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 -208.163.53.37 - - [25/Feb/2008:13:09:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -208.163.53.37 - - [25/Feb/2008:13:09:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.171.251 - - [25/Feb/2008:13:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.171.251 - - [25/Feb/2008:13:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.122.155.30 - - [25/Feb/2008:13:11:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -200.122.155.30 - - [25/Feb/2008:13:11:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -200.122.155.30 - - [25/Feb/2008:13:11:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -200.122.155.30 - - [25/Feb/2008:13:12:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -200.122.155.30 - - [25/Feb/2008:13:12:12 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -208.163.53.37 - - [25/Feb/2008:13:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.163.53.37 - - [25/Feb/2008:13:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.163.53.37 - - [25/Feb/2008:13:17:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.188 - - [25/Feb/2008:13:22:37 -0600] "GET /training.html HTTP/1.0" 304 - -82.73.225.225 - - [25/Feb/2008:13:22:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.73.225.225 - - [25/Feb/2008:13:22:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.73.225.225 - - [25/Feb/2008:13:22:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -82.73.225.225 - - [25/Feb/2008:13:22:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -67.195.58.186 - - [25/Feb/2008:13:23:06 -0600] "GET /about.html HTTP/1.0" 304 - -202.160.174.4 - - [25/Feb/2008:13:23:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -202.160.174.4 - - [25/Feb/2008:13:23:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -202.160.174.4 - - [25/Feb/2008:13:23:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 -67.195.58.165 - - [25/Feb/2008:13:23:18 -0600] "GET /writing.html HTTP/1.0" 304 - -81.52.143.16 - - [25/Feb/2008:13:25:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 -82.239.61.147 - - [25/Feb/2008:13:25:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -81.52.143.16 - - [25/Feb/2008:13:25:58 -0600] "GET / HTTP/1.1" 200 4447 -82.239.61.147 - - [25/Feb/2008:13:26:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [25/Feb/2008:13:26:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.23.77 - - [25/Feb/2008:13:29:39 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE006.HTM HTTP/1.0" 200 1510 -82.239.61.147 - - [25/Feb/2008:13:30:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.22.104.18 - - [25/Feb/2008:13:32:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -41.222.70.189 - - [25/Feb/2008:13:36:05 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 200 21384 -41.222.70.189 - - [25/Feb/2008:13:36:55 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 206 3108482 -200.122.155.30 - - [25/Feb/2008:13:41:03 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -200.122.155.30 - - [25/Feb/2008:13:41:11 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -71.57.91.136 - - [25/Feb/2008:13:43:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.203.10.71 - - [25/Feb/2008:13:43:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -84.203.10.71 - - [25/Feb/2008:13:43:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.232.113.194 - - [25/Feb/2008:13:48:38 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -219.93.175.69 - - [25/Feb/2008:13:48:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -200.13.243.76 - - [25/Feb/2008:13:48:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -190.30.84.93 - - [25/Feb/2008:13:52:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 -128.221.197.20 - - [25/Feb/2008:13:52:35 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -62.121.64.81 - - [25/Feb/2008:13:52:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -62.121.64.81 - - [25/Feb/2008:13:52:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.121.64.81 - - [25/Feb/2008:13:52:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.121.64.81 - - [25/Feb/2008:13:53:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.121.64.81 - - [25/Feb/2008:13:53:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -62.121.64.81 - - [25/Feb/2008:13:53:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -62.121.64.81 - - [25/Feb/2008:13:53:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaqInstallSwigInDifferentDirectory HTTP/1.1" 200 2271 -62.121.64.81 - - [25/Feb/2008:13:54:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 -85.194.45.170 - - [25/Feb/2008:14:04:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 -85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.196.43.134 - - [25/Feb/2008:14:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.31.165 - - [25/Feb/2008:14:05:19 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.165 - - [25/Feb/2008:14:05:20 -0600] "GET /ply/ HTTP/1.0" 200 8018 -85.194.45.170 - - [25/Feb/2008:14:07:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.194.45.170 - - [25/Feb/2008:14:09:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -206.192.69.3 - - [25/Feb/2008:14:09:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 -206.192.69.3 - - [25/Feb/2008:14:10:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -204.154.183.65 - - [25/Feb/2008:14:10:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -204.154.183.65 - - [25/Feb/2008:14:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.154.183.65 - - [25/Feb/2008:14:10:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.154.183.65 - - [25/Feb/2008:14:10:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.150 - - [25/Feb/2008:14:11:32 -0600] "GET /publications.html HTTP/1.0" 200 7758 -204.154.183.65 - - [25/Feb/2008:14:11:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.20.171 - - [25/Feb/2008:14:11:57 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE082.HTM HTTP/1.0" 200 1526 -199.171.86.151 - - [25/Feb/2008:14:12:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -199.171.86.151 - - [25/Feb/2008:14:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -199.171.86.151 - - [25/Feb/2008:14:12:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -199.171.86.151 - - [25/Feb/2008:14:13:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 -199.171.86.151 - - [25/Feb/2008:14:13:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 -74.6.31.151 - - [25/Feb/2008:14:14:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -204.154.183.65 - - [25/Feb/2008:14:15:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.248.57.218 - - [25/Feb/2008:14:18:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.248.57.218 - - [25/Feb/2008:14:18:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -190.138.87.130 - - [25/Feb/2008:14:20:48 -0600] "GET /ply/ HTTP/1.0" 200 8018 -208.80.193.52 - - [25/Feb/2008:14:26:08 -0600] "GET /cgi-bin/wiki.pl?action=browse&diff=1&id=swigfaq/sharedlibraries HTTP/1.1" 200 1528 -85.194.45.170 - - [25/Feb/2008:14:27:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.132.99.224 - - [25/Feb/2008:14:28:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.132.99.224 - - [25/Feb/2008:14:29:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.132.99.224 - - [25/Feb/2008:14:29:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.132.99.224 - - [25/Feb/2008:14:29:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -80.132.99.224 - - [25/Feb/2008:14:29:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -80.132.99.224 - - [25/Feb/2008:14:29:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 -80.132.99.224 - - [25/Feb/2008:14:29:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -80.132.99.224 - - [25/Feb/2008:14:29:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -80.132.99.224 - - [25/Feb/2008:14:29:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -208.22.104.18 - - [25/Feb/2008:14:31:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -208.22.104.18 - - [25/Feb/2008:14:31:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -80.192.69.2 - - [25/Feb/2008:14:32:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.192.69.2 - - [25/Feb/2008:14:32:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.22.104.18 - - [25/Feb/2008:14:32:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -208.22.104.18 - - [25/Feb/2008:14:32:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 -88.114.145.139 - - [25/Feb/2008:14:34:38 -0600] "GET /python.html HTTP/1.1" 200 18870 -88.114.145.139 - - [25/Feb/2008:14:34:39 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -88.114.145.139 - - [25/Feb/2008:14:34:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.224.58.211 - - [25/Feb/2008:14:38:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12634 -62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.224.58.211 - - [25/Feb/2008:14:38:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.224.58.211 - - [25/Feb/2008:14:38:13 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -62.224.58.211 - - [25/Feb/2008:14:38:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.69.221.130 - - [25/Feb/2008:14:38:27 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -128.135.194.138 - - [25/Feb/2008:14:45:12 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -128.135.194.138 - - [25/Feb/2008:14:45:19 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -81.52.143.15 - - [25/Feb/2008:14:45:37 -0600] "GET /robots.txt HTTP/1.1" 200 71 -81.52.143.15 - - [25/Feb/2008:14:45:47 -0600] "GET /ply HTTP/1.1" 301 242 -128.135.194.138 - - [25/Feb/2008:14:45:48 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -91.110.251.215 - - [25/Feb/2008:14:45:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -91.110.251.215 - - [25/Feb/2008:14:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.110.251.215 - - [25/Feb/2008:14:45:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.241.68.226 - - [25/Feb/2008:14:49:22 -0600] "GET / HTTP/1.1" 200 4447 -66.241.68.226 - - [25/Feb/2008:14:49:23 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -66.241.68.226 - - [25/Feb/2008:14:49:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.241.68.226 - - [25/Feb/2008:14:50:07 -0600] "GET /python.html HTTP/1.1" 200 18870 -66.241.68.226 - - [25/Feb/2008:14:50:08 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -66.241.68.226 - - [25/Feb/2008:14:51:46 -0600] "GET /writing.html HTTP/1.1" 200 2871 -66.241.68.226 - - [25/Feb/2008:14:51:47 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 -66.241.68.226 - - [25/Feb/2008:14:52:14 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /about.html HTTP/1.1" 200 7890 -66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 -66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 -66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 -66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 -66.241.68.226 - - [25/Feb/2008:14:54:41 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 -66.241.68.226 - - [25/Feb/2008:14:54:44 -0600] "GET /diet.html HTTP/1.1" 404 133 -200.171.34.14 - - [25/Feb/2008:14:55:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -200.171.34.14 - - [25/Feb/2008:14:55:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.20.216 - - [25/Feb/2008:14:55:46 -0600] "GET / HTTP/1.0" 304 - -80.132.99.224 - - [25/Feb/2008:14:56:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 -128.221.197.20 - - [25/Feb/2008:15:01:43 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -70.137.112.2 - - [25/Feb/2008:15:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.171.34.14 - - [25/Feb/2008:15:04:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -200.171.34.14 - - [25/Feb/2008:15:04:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 -76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.223.13.234 - - [25/Feb/2008:15:09:02 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -129.106.32.126 - - [25/Feb/2008:15:10:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -129.106.32.126 - - [25/Feb/2008:15:10:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.106.32.126 - - [25/Feb/2008:15:10:41 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1827 -129.106.32.126 - - [25/Feb/2008:15:10:44 -0600] "GET /cgi-bin/wiki.pl?action=rc&from=1201142973 HTTP/1.1" 200 1887 -129.106.32.126 - - [25/Feb/2008:15:10:58 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -129.106.32.126 - - [25/Feb/2008:15:11:00 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -129.106.32.126 - - [25/Feb/2008:15:11:04 -0600] "GET /cgi-bin/wiki.pl?InlineDirective HTTP/1.1" 200 2103 -129.106.32.126 - - [25/Feb/2008:15:11:07 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 -129.106.32.126 - - [25/Feb/2008:15:11:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -129.106.32.126 - - [25/Feb/2008:15:11:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -129.106.32.126 - - [25/Feb/2008:15:11:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -129.106.32.126 - - [25/Feb/2008:15:11:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 -129.106.32.126 - - [25/Feb/2008:15:11:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -160.83.72.201 - - [25/Feb/2008:15:14:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -160.83.72.201 - - [25/Feb/2008:15:14:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -160.83.72.201 - - [25/Feb/2008:15:14:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -160.83.72.201 - - [25/Feb/2008:15:14:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -160.83.72.201 - - [25/Feb/2008:15:14:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 2455 -89.142.106.15 - - [25/Feb/2008:15:16:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -89.142.106.15 - - [25/Feb/2008:15:16:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.142.106.15 - - [25/Feb/2008:15:16:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -89.142.106.15 - - [25/Feb/2008:15:16:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 -89.142.106.15 - - [25/Feb/2008:15:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -160.83.72.201 - - [25/Feb/2008:15:17:13 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 1167 -74.6.19.115 - - [25/Feb/2008:15:17:56 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.31.170 - - [25/Feb/2008:15:17:56 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.170 - - [25/Feb/2008:15:17:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 -62.224.58.211 - - [25/Feb/2008:15:18:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.224.58.211 - - [25/Feb/2008:15:18:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -160.83.72.201 - - [25/Feb/2008:15:20:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -160.83.72.201 - - [25/Feb/2008:15:20:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -160.83.72.201 - - [25/Feb/2008:15:20:53 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -160.83.72.201 - - [25/Feb/2008:15:21:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -160.83.72.201 - - [25/Feb/2008:15:21:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -74.6.26.100 - - [25/Feb/2008:15:22:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE028.HTM HTTP/1.0" 200 2184 -160.83.72.201 - - [25/Feb/2008:15:22:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -208.22.104.18 - - [25/Feb/2008:15:24:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -86.205.155.128 - - [25/Feb/2008:15:24:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 -86.205.155.128 - - [25/Feb/2008:15:24:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -86.205.155.128 - - [25/Feb/2008:15:24:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.205.155.128 - - [25/Feb/2008:15:24:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.239.61.147 - - [25/Feb/2008:15:25:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [25/Feb/2008:15:39:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.239.61.147 - - [25/Feb/2008:15:39:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -82.239.61.147 - - [25/Feb/2008:15:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [25/Feb/2008:15:39:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.35.206.246 - - [25/Feb/2008:15:50:02 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -82.35.206.246 - - [25/Feb/2008:15:50:09 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -82.35.206.246 - - [25/Feb/2008:15:50:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -82.35.206.246 - - [25/Feb/2008:15:50:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -82.35.206.246 - - [25/Feb/2008:15:50:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -82.35.206.246 - - [25/Feb/2008:15:51:51 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.35.206.246 - - [25/Feb/2008:15:55:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -82.35.206.246 - - [25/Feb/2008:15:55:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -203.166.87.218 - - [25/Feb/2008:15:58:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -203.166.87.218 - - [25/Feb/2008:15:58:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -203.166.87.218 - - [25/Feb/2008:15:59:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=SwigFaqDLLForWindows/Msys HTTP/1.1" 200 1927 -203.166.87.218 - - [25/Feb/2008:15:59:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 -68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /dynamic HTTP/1.1" 301 246 -68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.252.247.8 - - [25/Feb/2008:16:03:09 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -68.252.247.8 - - [25/Feb/2008:16:03:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.223.13.234 - - [25/Feb/2008:16:09:19 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -76.223.13.234 - - [25/Feb/2008:16:09:25 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -88.191.19.81 - - [25/Feb/2008:16:09:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -86.157.119.197 - - [25/Feb/2008:16:11:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.91.134.210 - - [25/Feb/2008:16:23:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.91.134.210 - - [25/Feb/2008:16:23:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.91.134.210 - - [25/Feb/2008:16:23:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -83.204.240.53 - - [25/Feb/2008:16:23:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.204.240.53 - - [25/Feb/2008:16:23:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -69.91.134.210 - - [25/Feb/2008:16:23:44 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -140.160.129.28 - - [25/Feb/2008:16:25:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.28.166 - - [25/Feb/2008:16:27:33 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1346 -69.91.134.210 - - [25/Feb/2008:16:31:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -65.113.40.1 - - [25/Feb/2008:16:31:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.135.24.9 - - [25/Feb/2008:16:36:31 -0600] "GET / HTTP/1.1" 200 4447 -128.135.24.9 - - [25/Feb/2008:16:36:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -128.135.24.9 - - [25/Feb/2008:16:36:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -128.135.24.9 - - [25/Feb/2008:16:36:48 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -140.160.129.28 - - [25/Feb/2008:16:36:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -140.160.129.28 - - [25/Feb/2008:16:36:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.74.95.23 - - [25/Feb/2008:16:36:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -87.74.95.23 - - [25/Feb/2008:16:37:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -87.74.95.23 - - [25/Feb/2008:16:37:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.24.9 - - [25/Feb/2008:16:37:16 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -128.135.24.9 - - [25/Feb/2008:16:37:47 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -87.74.95.23 - - [25/Feb/2008:16:38:42 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.191.19.81 - - [25/Feb/2008:16:40:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -140.160.199.105 - - [25/Feb/2008:16:48:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -140.160.199.105 - - [25/Feb/2008:16:49:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -140.160.199.105 - - [25/Feb/2008:16:49:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -140.160.199.105 - - [25/Feb/2008:16:49:10 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -87.74.95.23 - - [25/Feb/2008:16:51:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 -87.74.95.23 - - [25/Feb/2008:16:51:03 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -200.21.98.7 - - [25/Feb/2008:16:58:30 -0600] "GET / HTTP/1.0" 304 - -200.21.98.7 - - [25/Feb/2008:16:58:30 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -200.21.98.7 - - [25/Feb/2008:16:58:42 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 -200.21.98.7 - - [25/Feb/2008:16:58:56 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 206 494 -210.143.35.13 - - [25/Feb/2008:16:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.21.98.7 - - [25/Feb/2008:16:59:21 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 206 133088 -204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /python.html HTTP/1.1" 200 18870 -204.246.129.196 - - [25/Feb/2008:17:01:39 -0600] "GET /python.html HTTP/1.1" 200 18870 -204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.253.252.20 - - [25/Feb/2008:17:01:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.189.175 - - [25/Feb/2008:17:03:50 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.189.175 - - [25/Feb/2008:17:03:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1002 -200.21.98.7 - - [25/Feb/2008:17:05:37 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 200 43106 -67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET / HTTP/1.1" 200 4447 -67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.191.75 - - [25/Feb/2008:17:10:57 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.191.75 - - [25/Feb/2008:17:10:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 987 -81.48.212.152 - - [25/Feb/2008:17:11:04 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -190.139.116.130 - - [25/Feb/2008:17:15:43 -0600] "GET /ply/ HTTP/1.0" 200 8018 -76.213.231.36 - - [25/Feb/2008:17:16:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 -76.213.231.36 - - [25/Feb/2008:17:16:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.213.231.36 - - [25/Feb/2008:17:16:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -76.213.231.36 - - [25/Feb/2008:17:16:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -210.143.35.13 - - [25/Feb/2008:17:18:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -210.143.35.13 - - [25/Feb/2008:17:18:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.172.156.57 - - [25/Feb/2008:17:23:23 -0600] "GET /python.html HTTP/1.1" 200 18870 -58.172.156.57 - - [25/Feb/2008:17:23:24 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -58.172.156.57 - - [25/Feb/2008:17:23:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.187.74 - - [25/Feb/2008:17:24:54 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.187.74 - - [25/Feb/2008:17:24:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 -210.143.35.13 - - [25/Feb/2008:17:25:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 20232 -88.191.19.81 - - [25/Feb/2008:17:27:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -208.49.99.11 - - [25/Feb/2008:17:29:55 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -208.49.99.11 - - [25/Feb/2008:17:29:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.49.99.11 - - [25/Feb/2008:17:30:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -208.49.99.11 - - [25/Feb/2008:17:30:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -41.221.16.105 - - [25/Feb/2008:17:34:09 -0600] "GET / HTTP/1.1" 200 4447 -41.221.16.105 - - [25/Feb/2008:17:34:14 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -193.47.80.43 - - [25/Feb/2008:17:35:53 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.47.80.43 - - [25/Feb/2008:17:35:53 -0600] "GET / HTTP/1.1" 200 4447 -220.194.55.45 - - [25/Feb/2008:17:42:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -128.135.24.9 - - [25/Feb/2008:17:48:48 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -75.58.86.1 - - [25/Feb/2008:17:48:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -201.236.226.90 - - [25/Feb/2008:17:50:45 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [25/Feb/2008:17:50:47 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -201.236.226.90 - - [25/Feb/2008:17:50:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [25/Feb/2008:17:50:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [25/Feb/2008:17:50:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -201.236.226.90 - - [25/Feb/2008:17:51:19 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -128.143.231.202 - - [25/Feb/2008:17:53:51 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.19.154 - - [25/Feb/2008:18:00:43 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.0" 200 3246437 -85.160.19.27 - - [25/Feb/2008:18:02:42 -0600] "GET /python.html HTTP/1.1" 200 18870 -85.160.19.27 - - [25/Feb/2008:18:02:47 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -85.160.19.27 - - [25/Feb/2008:18:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.160.19.27 - - [25/Feb/2008:18:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -209.17.146.129 - - [25/Feb/2008:18:03:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 -209.17.146.129 - - [25/Feb/2008:18:03:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -209.17.146.129 - - [25/Feb/2008:18:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -209.17.146.129 - - [25/Feb/2008:18:03:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.15.126.192 - - [25/Feb/2008:18:03:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -216.15.126.192 - - [25/Feb/2008:18:03:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.15.126.192 - - [25/Feb/2008:18:04:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.15.126.192 - - [25/Feb/2008:18:04:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1381 -216.15.126.192 - - [25/Feb/2008:18:04:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MzScheme HTTP/1.1" 200 2865 -121.139.76.33 - - [25/Feb/2008:18:09:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -121.139.76.33 - - [25/Feb/2008:18:09:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 4177 -121.139.76.33 - - [25/Feb/2008:18:09:58 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/SwigInPython HTTP/1.1" 200 1704 -75.58.86.1 - - [25/Feb/2008:18:13:56 -0600] "GET /dynamic/ HTTP/1.1" 304 - -75.58.86.1 - - [25/Feb/2008:18:13:59 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.195.58.174 - - [25/Feb/2008:18:14:23 -0600] "GET /ply/ HTTP/1.0" 304 - -128.135.24.238 - - [25/Feb/2008:18:14:25 -0600] "GET /dynamic/ffcache.zip HTTP/1.0" 200 4919642 -67.195.58.170 - - [25/Feb/2008:18:14:58 -0600] "GET / HTTP/1.0" 200 4447 -67.195.58.158 - - [25/Feb/2008:18:15:32 -0600] "GET /ply/README HTTP/1.0" 200 8605 -67.195.58.158 - - [25/Feb/2008:18:15:32 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 200 64742 -67.195.58.158 - - [25/Feb/2008:18:15:33 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -85.160.19.27 - - [25/Feb/2008:18:15:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [25/Feb/2008:18:21:58 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -74.6.31.145 - - [25/Feb/2008:18:23:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -203.144.143.13 - - [25/Feb/2008:18:29:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -203.144.143.13 - - [25/Feb/2008:18:29:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -203.144.143.13 - - [25/Feb/2008:18:29:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.60.229.125 - - [25/Feb/2008:18:44:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -68.60.229.125 - - [25/Feb/2008:18:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.60.229.125 - - [25/Feb/2008:18:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.60.229.125 - - [25/Feb/2008:18:44:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.60.229.125 - - [25/Feb/2008:18:44:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.134.126 - - [25/Feb/2008:18:46:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.134.126 - - [25/Feb/2008:18:46:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 -61.135.190.17 - - [25/Feb/2008:18:51:13 -0600] "GET /ply/ HTTP/1.1" 304 - -85.160.19.27 - - [25/Feb/2008:18:55:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.169 - - [25/Feb/2008:18:58:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.231.169 - - [25/Feb/2008:18:58:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.169 - - [25/Feb/2008:18:59:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -209.17.146.129 - - [25/Feb/2008:19:03:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.231.169 - - [25/Feb/2008:19:04:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.214.45.129 - - [25/Feb/2008:19:10:02 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.214.45.129 - - [25/Feb/2008:19:10:02 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE024.HTM HTTP/1.0" 200 1265 -130.240.203.74 - - [25/Feb/2008:19:10:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.231.169 - - [25/Feb/2008:19:10:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.240.203.74 - - [25/Feb/2008:19:10:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -130.240.203.74 - - [25/Feb/2008:19:10:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.240.203.74 - - [25/Feb/2008:19:10:42 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -70.245.137.240 - - [25/Feb/2008:19:15:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 -70.245.137.240 - - [25/Feb/2008:19:15:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -70.245.137.240 - - [25/Feb/2008:19:15:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.249.206.181 - - [25/Feb/2008:19:17:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -24.249.206.181 - - [25/Feb/2008:19:17:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.249.206.181 - - [25/Feb/2008:19:17:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -24.249.206.181 - - [25/Feb/2008:19:17:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -24.249.206.181 - - [25/Feb/2008:19:18:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 -128.143.231.169 - - [25/Feb/2008:19:18:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.249.206.181 - - [25/Feb/2008:19:18:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 -24.249.206.181 - - [25/Feb/2008:19:19:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 -24.249.206.181 - - [25/Feb/2008:19:20:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -202.179.180.52 - - [25/Feb/2008:19:20:29 -0600] "GET /robots.txt HTTP/1.1" 200 71 -202.179.180.52 - - [25/Feb/2008:19:20:30 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 135478 -128.143.231.169 - - [25/Feb/2008:19:28:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.3.94.70 - - [25/Feb/2008:19:30:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -71.3.94.70 - - [25/Feb/2008:19:30:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.169.3 - - [25/Feb/2008:19:31:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.169.3 - - [25/Feb/2008:19:31:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.11.31.114 - - [25/Feb/2008:19:31:48 -0600] "GET /ply HTTP/1.1" 301 242 -82.11.31.114 - - [25/Feb/2008:19:31:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.99.169.3 - - [25/Feb/2008:19:32:11 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -80.99.169.3 - - [25/Feb/2008:19:32:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -80.99.169.3 - - [25/Feb/2008:19:32:14 -0600] "GET /ply/README HTTP/1.1" 200 8605 -74.6.26.22 - - [25/Feb/2008:19:32:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE072.HTM HTTP/1.0" 200 1850 -98.224.230.125 - - [25/Feb/2008:19:34:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -98.224.230.125 - - [25/Feb/2008:19:34:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.224.230.125 - - [25/Feb/2008:19:35:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -98.224.230.125 - - [25/Feb/2008:19:35:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -98.224.230.125 - - [25/Feb/2008:19:36:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -98.224.230.125 - - [25/Feb/2008:19:36:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -74.6.19.237 - - [25/Feb/2008:19:38:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE091.HTM HTTP/1.0" 200 1772 -74.6.8.73 - - [25/Feb/2008:19:41:47 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.8.73 - - [25/Feb/2008:19:41:47 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -71.3.94.70 - - [25/Feb/2008:19:45:21 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -69.113.211.8 - - [25/Feb/2008:19:50:03 -0600] "GET /ply/ HTTP/1.0" 200 8018 -69.113.211.8 - - [25/Feb/2008:19:50:04 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -128.143.231.169 - - [25/Feb/2008:19:51:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.148.212.222 - - [25/Feb/2008:19:54:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -216.148.212.222 - - [25/Feb/2008:19:54:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.148.212.222 - - [25/Feb/2008:19:54:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -216.148.212.222 - - [25/Feb/2008:19:54:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -216.148.212.222 - - [25/Feb/2008:19:58:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -216.148.212.222 - - [25/Feb/2008:19:58:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -129.82.46.120 - - [25/Feb/2008:20:02:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -67.186.98.20 - - [25/Feb/2008:20:06:04 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 200 628284 -67.186.98.20 - - [25/Feb/2008:20:06:12 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 2935451 -67.186.98.20 - - [25/Feb/2008:20:06:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [25/Feb/2008:20:06:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.160.180.70 - - [25/Feb/2008:20:07:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.186.98.20 - - [25/Feb/2008:20:12:18 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 199044 -67.186.98.20 - - [25/Feb/2008:20:12:28 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 3181210 -216.148.212.222 - - [25/Feb/2008:20:12:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -148.241.82.6 - - [25/Feb/2008:20:12:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -148.241.82.6 - - [25/Feb/2008:20:12:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -148.241.82.6 - - [25/Feb/2008:20:12:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -148.241.82.6 - - [25/Feb/2008:20:13:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -129.82.46.120 - - [25/Feb/2008:20:13:22 -0600] "GET /ply/ HTTP/1.1" 304 - -129.82.46.120 - - [25/Feb/2008:20:13:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -193.202.115.177 - - [25/Feb/2008:20:16:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -74.6.8.73 - - [25/Feb/2008:20:20:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 -71.57.91.136 - - [25/Feb/2008:20:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.36.158.68 - - [25/Feb/2008:20:20:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 -69.36.158.68 - - [25/Feb/2008:20:20:35 -0600] "GET / HTTP/1.1" 200 4447 -69.36.158.68 - - [25/Feb/2008:20:20:36 -0600] "GET /index.html HTTP/1.1" 200 4447 -69.36.158.68 - - [25/Feb/2008:20:20:37 -0600] "GET /training.html HTTP/1.1" 200 6154 -69.36.158.68 - - [25/Feb/2008:20:20:38 -0600] "GET /software.html HTTP/1.1" 200 3163 -69.36.158.68 - - [25/Feb/2008:20:20:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -69.36.158.68 - - [25/Feb/2008:20:20:40 -0600] "GET /writing.html HTTP/1.1" 200 2871 -69.36.158.68 - - [25/Feb/2008:20:20:41 -0600] "GET /about.html HTTP/1.1" 200 7890 -69.36.158.68 - - [25/Feb/2008:20:20:42 -0600] "GET /python.html HTTP/1.1" 200 18870 -69.36.158.68 - - [25/Feb/2008:20:20:44 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -69.36.158.68 - - [25/Feb/2008:20:20:45 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -69.36.158.68 - - [25/Feb/2008:20:20:46 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -69.36.158.68 - - [25/Feb/2008:20:20:47 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -69.36.158.68 - - [25/Feb/2008:20:20:48 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -69.36.158.68 - - [25/Feb/2008:20:20:49 -0600] "GET /publications.html HTTP/1.1" 200 7758 -69.36.158.68 - - [25/Feb/2008:20:20:50 -0600] "GET /cv.html HTTP/1.1" 200 31798 -69.36.158.68 - - [25/Feb/2008:20:20:51 -0600] "GET /diet.html HTTP/1.1" 404 133 -69.36.158.68 - - [25/Feb/2008:20:20:52 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -69.36.158.68 - - [25/Feb/2008:20:20:53 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -69.36.158.68 - - [25/Feb/2008:20:20:54 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -69.36.158.68 - - [25/Feb/2008:20:20:55 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -69.36.158.68 - - [25/Feb/2008:20:20:56 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -148.241.82.6 - - [25/Feb/2008:20:21:15 -0600] "GET /ply/ HTTP/1.1" 304 - -148.241.82.6 - - [25/Feb/2008:20:21:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -201.213.27.208 - - [25/Feb/2008:20:22:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.213.27.208 - - [25/Feb/2008:20:22:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.213.27.208 - - [25/Feb/2008:20:22:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.213.27.208 - - [25/Feb/2008:20:22:33 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -67.186.98.20 - - [25/Feb/2008:20:28:07 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 250956 -67.186.98.20 - - [25/Feb/2008:20:28:18 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 3181210 -71.57.91.136 - - [25/Feb/2008:20:35:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -12.87.213.22 - - [25/Feb/2008:20:37:26 -0600] "GET / HTTP/1.1" 200 4447 -12.87.213.22 - - [25/Feb/2008:20:37:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -12.87.213.22 - - [25/Feb/2008:20:37:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /about.html HTTP/1.1" 200 7890 -12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 -12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 -12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 -12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 -129.82.46.120 - - [25/Feb/2008:20:38:25 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -12.87.213.22 - - [25/Feb/2008:20:39:06 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 -217.172.44.82 - - [25/Feb/2008:20:45:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -129.82.46.120 - - [25/Feb/2008:20:53:39 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [25/Feb/2008:20:55:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -72.85.134.143 - - [25/Feb/2008:20:57:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 -72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.28.160 - - [25/Feb/2008:21:03:47 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE045.HTM HTTP/1.0" 304 - -217.196.43.134 - - [25/Feb/2008:21:05:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 -190.47.57.29 - - [25/Feb/2008:21:09:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -190.47.57.29 - - [25/Feb/2008:21:09:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -190.47.57.29 - - [25/Feb/2008:21:09:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [25/Feb/2008:21:09:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -75.22.21.146 - - [25/Feb/2008:21:09:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [25/Feb/2008:21:09:41 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -75.22.21.146 - - [25/Feb/2008:21:09:59 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -65.55.208.122 - - [25/Feb/2008:21:14:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.122 - - [25/Feb/2008:21:14:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.1" 304 - -71.57.91.136 - - [25/Feb/2008:21:19:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [25/Feb/2008:21:19:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.7.149 - - [25/Feb/2008:21:23:01 -0600] "GET /about.html HTTP/1.0" 200 7890 -75.22.21.146 - - [25/Feb/2008:21:25:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - -65.214.45.101 - - [25/Feb/2008:21:29:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.214.45.101 - - [25/Feb/2008:21:29:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 -64.124.85.75 - - [25/Feb/2008:21:36:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 -64.124.85.75 - - [25/Feb/2008:21:39:27 -0600] "GET /ply HTTP/1.1" 301 242 -129.82.47.96 - - [25/Feb/2008:21:41:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -129.82.47.96 - - [25/Feb/2008:21:41:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -129.82.47.96 - - [25/Feb/2008:21:41:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -129.82.47.96 - - [25/Feb/2008:21:41:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.232.164.26 - - [25/Feb/2008:21:49:43 -0600] "GET /about.html HTTP/1.0" 200 7890 -38.98.120.84 - - [25/Feb/2008:22:04:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [25/Feb/2008:22:04:57 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [25/Feb/2008:22:05:01 -0600] "GET / HTTP/1.1" 200 4447 -202.118.7.10 - - [25/Feb/2008:22:08:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -202.118.7.10 - - [25/Feb/2008:22:08:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.118.7.10 - - [25/Feb/2008:22:08:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.118.7.10 - - [25/Feb/2008:22:09:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.191.64.124 - - [25/Feb/2008:22:15:33 -0600] "GET /ply/README HTTP/1.1" 200 8605 -38.104.0.30 - - [25/Feb/2008:22:15:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.104.0.30 - - [25/Feb/2008:22:15:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -202.118.7.10 - - [25/Feb/2008:22:17:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -68.72.123.219 - - [25/Feb/2008:22:18:25 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -74.6.28.76 - - [25/Feb/2008:22:21:28 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE053.HTM HTTP/1.0" 200 1409 -203.200.218.2 - - [25/Feb/2008:22:24:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 -203.200.218.2 - - [25/Feb/2008:22:24:15 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -203.200.218.2 - - [25/Feb/2008:22:24:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -67.135.15.12 - - [25/Feb/2008:22:24:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.135.15.12 - - [25/Feb/2008:22:24:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -67.135.15.12 - - [25/Feb/2008:22:24:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.7.72 - - [25/Feb/2008:22:25:31 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 -203.200.218.2 - - [25/Feb/2008:22:26:11 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -201.236.226.90 - - [25/Feb/2008:22:26:38 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [25/Feb/2008:22:26:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [25/Feb/2008:22:26:42 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 15733 -201.236.226.90 - - [25/Feb/2008:22:26:43 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -201.236.226.90 - - [25/Feb/2008:22:26:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [25/Feb/2008:22:26:52 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -129.82.47.96 - - [25/Feb/2008:22:29:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.146.214.212 - - [25/Feb/2008:22:31:13 -0600] "GET /dynamic HTTP/1.1" 301 246 -66.146.214.212 - - [25/Feb/2008:22:31:13 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -66.146.214.212 - - [25/Feb/2008:22:31:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -129.82.47.96 - - [25/Feb/2008:22:34:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.28.24 - - [25/Feb/2008:22:36:03 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -66.116.72.114 - - [25/Feb/2008:22:37:25 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.116.72.114 - - [25/Feb/2008:22:37:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.94.167.76 - - [25/Feb/2008:22:40:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -203.94.167.76 - - [25/Feb/2008:22:40:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.94.167.76 - - [25/Feb/2008:22:40:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 -76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -203.94.167.76 - - [25/Feb/2008:22:41:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.94.167.76 - - [25/Feb/2008:22:41:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -203.94.167.76 - - [25/Feb/2008:22:41:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -76.199.14.198 - - [25/Feb/2008:22:42:42 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -66.146.214.212 - - [25/Feb/2008:22:43:06 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -84.110.229.145 - - [25/Feb/2008:22:45:32 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.229.145 - - [25/Feb/2008:22:45:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 -202.179.180.45 - - [25/Feb/2008:22:49:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 -202.179.180.45 - - [25/Feb/2008:22:49:11 -0600] "GET / HTTP/1.1" 200 4447 -202.179.180.45 - - [25/Feb/2008:22:49:18 -0600] "GET /index.html HTTP/1.1" 200 4447 -202.179.180.45 - - [25/Feb/2008:22:49:24 -0600] "GET /training.html HTTP/1.1" 200 6154 -202.179.180.45 - - [25/Feb/2008:22:49:30 -0600] "GET /software.html HTTP/1.1" 200 3163 -202.179.180.45 - - [25/Feb/2008:22:49:37 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -202.179.180.45 - - [25/Feb/2008:22:49:43 -0600] "GET /writing.html HTTP/1.1" 200 2871 -202.179.180.45 - - [25/Feb/2008:22:49:50 -0600] "GET /about.html HTTP/1.1" 200 7890 -202.179.180.45 - - [25/Feb/2008:22:49:56 -0600] "GET /python.html HTTP/1.1" 200 18870 -202.179.180.45 - - [25/Feb/2008:22:50:03 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -202.179.180.45 - - [25/Feb/2008:22:50:09 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -202.179.180.45 - - [25/Feb/2008:22:50:15 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -202.179.180.45 - - [25/Feb/2008:22:50:21 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -202.179.180.45 - - [25/Feb/2008:22:50:28 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -202.179.180.45 - - [25/Feb/2008:22:50:32 -0600] "GET /publications.html HTTP/1.1" 200 7758 -202.179.180.45 - - [25/Feb/2008:22:50:39 -0600] "GET /cv.html HTTP/1.1" 200 31798 -68.226.202.243 - - [25/Feb/2008:22:50:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -68.226.202.243 - - [25/Feb/2008:22:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.226.202.243 - - [25/Feb/2008:22:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.226.202.243 - - [25/Feb/2008:22:51:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.226.202.243 - - [25/Feb/2008:22:51:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -68.226.202.243 - - [25/Feb/2008:22:51:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -68.226.202.243 - - [25/Feb/2008:22:51:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -58.71.35.197 - - [25/Feb/2008:22:58:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 -58.71.35.197 - - [25/Feb/2008:22:58:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -58.71.35.197 - - [25/Feb/2008:22:58:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.71.35.197 - - [25/Feb/2008:22:59:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/README HTTP/1.1" 200 8605 -128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.182.43.140 - - [25/Feb/2008:23:11:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 -76.182.43.140 - - [25/Feb/2008:23:11:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -76.182.43.140 - - [25/Feb/2008:23:11:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.160.180.110 - - [25/Feb/2008:23:11:24 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -74.6.20.44 - - [25/Feb/2008:23:15:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE104.HTM HTTP/1.0" 200 1140 -61.247.217.37 - - [25/Feb/2008:23:16:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 -58.71.35.197 - - [25/Feb/2008:23:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.67.135.113 - - [25/Feb/2008:23:24:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -61.67.135.113 - - [25/Feb/2008:23:24:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 -24.1.247.118 - - [25/Feb/2008:23:26:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -24.1.247.118 - - [25/Feb/2008:23:26:21 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -58.71.35.197 - - [25/Feb/2008:23:27:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.71.35.197 - - [25/Feb/2008:23:27:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.71.35.197 - - [25/Feb/2008:23:28:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.71.35.197 - - [25/Feb/2008:23:28:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [25/Feb/2008:23:28:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE043.HTM HTTP/1.1" 200 1178 -74.125.16.5 - - [25/Feb/2008:23:30:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -63.252.164.2 - - [25/Feb/2008:23:30:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -124.30.188.110 - - [25/Feb/2008:23:32:35 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -124.30.188.110 - - [25/Feb/2008:23:32:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -124.30.188.110 - - [25/Feb/2008:23:32:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.55.208.118 - - [25/Feb/2008:23:37:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.118 - - [25/Feb/2008:23:37:28 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 -124.30.188.110 - - [25/Feb/2008:23:39:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -74.6.8.73 - - [25/Feb/2008:23:42:11 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -74.6.8.73 - - [25/Feb/2008:23:42:13 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - -65.214.45.129 - - [25/Feb/2008:23:43:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE019.HTM HTTP/1.0" 200 1307 -58.71.35.197 - - [25/Feb/2008:23:53:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.36.114.239 - - [25/Feb/2008:23:55:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.55.208.118 - - [25/Feb/2008:23:55:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.1" 403 257 -58.71.35.197 - - [25/Feb/2008:23:57:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.115.4.222 - - [25/Feb/2008:23:57:58 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -74.6.26.171 - - [26/Feb/2008:00:00:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.25.81 - - [26/Feb/2008:00:00:44 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 -66.249.65.37 - - [26/Feb/2008:00:02:13 -0600] "GET /training.html HTTP/1.1" 304 - -58.71.35.197 - - [26/Feb/2008:00:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.214.45.129 - - [26/Feb/2008:00:04:45 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1310 -75.50.51.97 - - [26/Feb/2008:00:07:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.249.65.37 - - [26/Feb/2008:00:08:43 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 304 - -213.145.165.82 - - [26/Feb/2008:00:16:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.8.73 - - [26/Feb/2008:00:17:23 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 304 - -66.249.65.37 - - [26/Feb/2008:00:23:16 -0600] "GET /ply/example.html HTTP/1.1" 304 - -220.134.105.172 - - [26/Feb/2008:00:23:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -220.134.105.172 - - [26/Feb/2008:00:23:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -220.134.105.172 - - [26/Feb/2008:00:23:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.134.105.172 - - [26/Feb/2008:00:25:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.8.211.11 - - [26/Feb/2008:00:25:28 -0600] "GET /python.html HTTP/1.0" 200 18870 -220.134.105.172 - - [26/Feb/2008:00:25:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.8.211.11 - - [26/Feb/2008:00:25:59 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -192.8.211.11 - - [26/Feb/2008:00:26:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -192.8.211.11 - - [26/Feb/2008:00:26:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -220.134.105.172 - - [26/Feb/2008:00:26:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -84.110.232.251 - - [26/Feb/2008:00:34:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.232.251 - - [26/Feb/2008:00:34:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1017 -220.134.105.172 - - [26/Feb/2008:00:39:04 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -57.66.144.181 - - [26/Feb/2008:00:55:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -57.66.144.181 - - [26/Feb/2008:00:55:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -89.165.73.111 - - [26/Feb/2008:00:58:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.165.73.111 - - [26/Feb/2008:00:58:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -207.225.146.142 - - [26/Feb/2008:00:58:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -207.225.146.142 - - [26/Feb/2008:00:58:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.165.73.111 - - [26/Feb/2008:00:58:53 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -89.165.73.111 - - [26/Feb/2008:00:58:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.111 - - [26/Feb/2008:00:58:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.111 - - [26/Feb/2008:00:58:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -89.165.73.111 - - [26/Feb/2008:00:59:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.111 - - [26/Feb/2008:00:59:17 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 -89.165.73.111 - - [26/Feb/2008:01:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.111 - - [26/Feb/2008:01:01:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.111 - - [26/Feb/2008:01:02:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.111 - - [26/Feb/2008:01:02:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.111 - - [26/Feb/2008:01:03:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.111 - - [26/Feb/2008:01:04:18 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 107720 -99.140.232.220 - - [26/Feb/2008:01:04:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -99.140.232.220 - - [26/Feb/2008:01:05:03 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -66.249.65.37 - - [26/Feb/2008:01:06:05 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 304 - -74.6.8.73 - - [26/Feb/2008:01:06:49 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 -74.6.8.73 - - [26/Feb/2008:01:13:57 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 -66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.91.239.214 - - [26/Feb/2008:01:19:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -62.141.176.22 - - [26/Feb/2008:01:24:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -62.141.176.22 - - [26/Feb/2008:01:24:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.141.176.22 - - [26/Feb/2008:01:24:47 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -84.143.76.58 - - [26/Feb/2008:01:29:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 -84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.143.76.58 - - [26/Feb/2008:01:29:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.115.4.219 - - [26/Feb/2008:01:30:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -66.249.65.37 - - [26/Feb/2008:01:33:15 -0600] "GET /ply/support.html HTTP/1.1" 304 - -85.185.76.213 - - [26/Feb/2008:01:33:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -85.185.76.213 - - [26/Feb/2008:01:33:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -85.185.76.213 - - [26/Feb/2008:01:38:51 -0600] "GET /ply/ HTTP/1.1" 304 - -85.185.76.213 - - [26/Feb/2008:01:38:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -67.78.34.166 - - [26/Feb/2008:01:40:43 -0600] "GET /robots.txt HTTP/1.1" 200 71 -67.78.34.166 - - [26/Feb/2008:01:40:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 -71.216.4.6 - - [26/Feb/2008:01:47:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -71.216.4.6 - - [26/Feb/2008:01:47:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.216.4.6 - - [26/Feb/2008:01:47:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -71.216.4.6 - - [26/Feb/2008:01:47:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -71.216.4.6 - - [26/Feb/2008:01:48:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -71.216.4.6 - - [26/Feb/2008:01:48:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -71.216.4.6 - - [26/Feb/2008:01:49:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -82.98.238.29 - - [26/Feb/2008:02:02:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.98.238.29 - - [26/Feb/2008:02:02:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -82.98.238.29 - - [26/Feb/2008:02:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.98.238.29 - - [26/Feb/2008:02:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.78.34.170 - - [26/Feb/2008:02:03:41 -0600] "GET /ply/README HTTP/1.1" 200 8605 -84.143.76.58 - - [26/Feb/2008:02:11:02 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.78.34.166 - - [26/Feb/2008:02:11:25 -0600] "GET / HTTP/1.1" 200 4447 -74.6.25.20 - - [26/Feb/2008:02:14:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.195.58.174 - - [26/Feb/2008:02:14:32 -0600] "GET /ply/ HTTP/1.0" 304 - -88.191.19.81 - - [26/Feb/2008:02:16:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.78.34.170 - - [26/Feb/2008:02:16:47 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 24524 -213.194.211.50 - - [26/Feb/2008:02:17:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 -213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.153.70.82 - - [26/Feb/2008:02:17:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.153.70.82 - - [26/Feb/2008:02:17:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.153.70.82 - - [26/Feb/2008:02:17:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.194.211.50 - - [26/Feb/2008:02:17:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.78.34.174 - - [26/Feb/2008:02:21:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -202.81.69.153 - - [26/Feb/2008:02:21:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -202.81.69.153 - - [26/Feb/2008:02:21:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -202.81.69.153 - - [26/Feb/2008:02:21:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.81.69.153 - - [26/Feb/2008:02:21:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.81.69.153 - - [26/Feb/2008:02:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.81.69.153 - - [26/Feb/2008:02:21:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -202.81.69.153 - - [26/Feb/2008:02:22:07 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -72.51.43.159 - - [26/Feb/2008:02:23:45 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -67.78.34.166 - - [26/Feb/2008:02:27:22 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -193.111.46.17 - - [26/Feb/2008:02:27:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -193.111.46.17 - - [26/Feb/2008:02:27:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -193.111.46.17 - - [26/Feb/2008:02:27:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 999 -193.111.46.17 - - [26/Feb/2008:02:27:43 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.0" 200 5873 -213.194.211.50 - - [26/Feb/2008:02:29:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.194.211.50 - - [26/Feb/2008:02:30:03 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -67.78.34.166 - - [26/Feb/2008:02:32:07 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -67.78.34.170 - - [26/Feb/2008:02:36:25 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 12820 -74.6.23.210 - - [26/Feb/2008:02:37:36 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE081.HTM HTTP/1.0" 200 1543 -80.99.119.201 - - [26/Feb/2008:02:38:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -88.191.19.81 - - [26/Feb/2008:02:39:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 -79.179.99.175 - - [26/Feb/2008:02:42:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 -79.179.99.175 - - [26/Feb/2008:02:42:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -202.81.69.153 - - [26/Feb/2008:02:42:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -79.179.99.175 - - [26/Feb/2008:02:42:19 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 -67.78.34.174 - - [26/Feb/2008:02:42:20 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 12819 -88.131.106.15 - - [26/Feb/2008:02:42:57 -0600] "GET /robots.txt HTTP/1.0" 200 71 -88.131.106.15 - - [26/Feb/2008:02:42:58 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -65.214.45.129 - - [26/Feb/2008:02:42:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 -221.6.82.112 - - [26/Feb/2008:02:43:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 -221.6.82.112 - - [26/Feb/2008:02:43:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -206.51.226.87 - - [26/Feb/2008:02:43:42 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -87.101.244.6 - - [26/Feb/2008:02:43:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -67.207.145.238 - - [26/Feb/2008:02:43:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -221.6.82.112 - - [26/Feb/2008:02:44:39 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 15480 -221.6.82.112 - - [26/Feb/2008:02:44:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -221.6.82.112 - - [26/Feb/2008:02:44:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -221.6.82.112 - - [26/Feb/2008:02:44:49 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 206 36955 -221.6.82.112 - - [26/Feb/2008:02:45:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -80.99.119.201 - - [26/Feb/2008:02:45:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.99.119.201 - - [26/Feb/2008:02:45:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -80.99.119.201 - - [26/Feb/2008:02:45:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.99.119.201 - - [26/Feb/2008:02:45:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -80.30.226.4 - - [26/Feb/2008:02:47:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 -67.78.34.170 - - [26/Feb/2008:02:47:27 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.1" 200 12819 -66.232.113.62 - - [26/Feb/2008:02:47:41 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -80.97.94.178 - - [26/Feb/2008:02:47:43 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -201.18.38.242 - - [26/Feb/2008:02:47:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -67.78.34.170 - - [26/Feb/2008:02:52:27 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.1" 200 12819 -83.145.122.242 - - [26/Feb/2008:02:55:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -83.145.122.242 - - [26/Feb/2008:02:55:13 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.214.45.129 - - [26/Feb/2008:02:56:29 -0600] "GET /cartage/index.html HTTP/1.0" 404 133 -67.78.34.174 - - [26/Feb/2008:02:58:08 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.1" 200 12819 -202.81.69.153 - - [26/Feb/2008:03:00:35 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 100406 -202.81.69.153 - - [26/Feb/2008:03:00:39 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 -88.191.19.81 - - [26/Feb/2008:03:00:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.78.34.166 - - [26/Feb/2008:03:04:27 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.1" 200 12819 -71.62.148.145 - - [26/Feb/2008:03:04:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.62.148.145 - - [26/Feb/2008:03:04:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.62.148.145 - - [26/Feb/2008:03:04:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.62.148.145 - - [26/Feb/2008:03:05:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.78.34.166 - - [26/Feb/2008:03:11:34 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 200 12820 -67.78.34.166 - - [26/Feb/2008:03:17:09 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 12818 -67.78.34.166 - - [26/Feb/2008:03:21:13 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.1" 200 12820 -74.6.23.102 - - [26/Feb/2008:03:23:08 -0600] "GET /index.html HTTP/1.0" 304 - -67.78.34.170 - - [26/Feb/2008:03:24:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 12818 -74.6.8.73 - - [26/Feb/2008:03:25:05 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 -62.171.194.36 - - [26/Feb/2008:03:26:12 -0600] "GET /ply/ HTTP/1.0" 200 8018 -62.171.194.36 - - [26/Feb/2008:03:26:12 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -67.78.34.174 - - [26/Feb/2008:03:29:51 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 12818 -137.226.57.203 - - [26/Feb/2008:03:30:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -137.226.57.203 - - [26/Feb/2008:03:30:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [26/Feb/2008:03:30:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.134.105.172 - - [26/Feb/2008:03:31:25 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -218.94.9.35 - - [26/Feb/2008:03:33:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 -218.94.9.35 - - [26/Feb/2008:03:33:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -218.94.9.35 - - [26/Feb/2008:03:33:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -218.94.9.35 - - [26/Feb/2008:03:33:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.165.207.58 - - [26/Feb/2008:03:36:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.165.207.58 - - [26/Feb/2008:03:36:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.165.207.58 - - [26/Feb/2008:03:36:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.165.207.58 - - [26/Feb/2008:03:36:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.165.207.58 - - [26/Feb/2008:03:36:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.165.207.58 - - [26/Feb/2008:03:36:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -74.6.8.73 - - [26/Feb/2008:03:39:26 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 -67.78.34.170 - - [26/Feb/2008:03:43:01 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 12820 -203.123.162.235 - - [26/Feb/2008:03:44:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -203.123.162.235 - - [26/Feb/2008:03:44:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [26/Feb/2008:03:44:58 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -99.140.232.220 - - [26/Feb/2008:03:45:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - -99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.78.34.166 - - [26/Feb/2008:03:53:00 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.1" 200 12819 -75.125.165.10 - - [26/Feb/2008:03:57:36 -0600] "GET / HTTP/1.1" 200 4447 -65.55.208.119 - - [26/Feb/2008:03:58:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.119 - - [26/Feb/2008:03:58:21 -0600] "GET /swill/Doc/index.html HTTP/1.1" 304 - -128.165.207.58 - - [26/Feb/2008:04:05:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.196.43.134 - - [26/Feb/2008:04:05:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -99.167.100.246 - - [26/Feb/2008:04:05:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -99.167.100.246 - - [26/Feb/2008:04:05:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.167.100.246 - - [26/Feb/2008:04:05:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -99.167.100.246 - - [26/Feb/2008:04:05:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -202.81.69.153 - - [26/Feb/2008:04:06:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -192.100.130.8 - - [26/Feb/2008:04:24:31 -0600] "GET /ply HTTP/1.1" 301 242 -192.100.130.8 - - [26/Feb/2008:04:24:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:04:25:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -160.45.115.176 - - [26/Feb/2008:04:33:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -160.45.115.176 - - [26/Feb/2008:04:33:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -160.45.115.176 - - [26/Feb/2008:04:33:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -160.45.115.176 - - [26/Feb/2008:04:33:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -160.45.115.176 - - [26/Feb/2008:04:33:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -61.57.149.13 - - [26/Feb/2008:04:35:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -61.57.149.13 - - [26/Feb/2008:04:35:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -195.217.226.30 - - [26/Feb/2008:04:44:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -195.217.226.30 - - [26/Feb/2008:04:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -195.217.226.30 - - [26/Feb/2008:04:44:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 935 -160.45.115.176 - - [26/Feb/2008:04:48:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -160.45.115.176 - - [26/Feb/2008:04:48:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -82.239.61.147 - - [26/Feb/2008:04:54:07 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.239.61.147 - - [26/Feb/2008:04:54:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [26/Feb/2008:04:54:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -160.45.115.176 - - [26/Feb/2008:04:56:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -160.45.115.176 - - [26/Feb/2008:04:56:24 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 -59.93.91.251 - - [26/Feb/2008:05:00:41 -0600] "GET /python.html HTTP/1.0" 200 18870 -59.93.91.251 - - [26/Feb/2008:05:00:42 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -59.93.91.251 - - [26/Feb/2008:05:00:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -82.239.61.147 - - [26/Feb/2008:05:01:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.120.121.126 - - [26/Feb/2008:05:04:01 -0600] "GET /ply/ HTTP/1.0" 200 8018 -220.181.38.169 - - [26/Feb/2008:05:05:24 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [26/Feb/2008:05:07:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [26/Feb/2008:05:07:57 -0600] "GET / HTTP/1.1" 200 4447 -160.45.115.176 - - [26/Feb/2008:05:08:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -59.93.91.251 - - [26/Feb/2008:05:09:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -74.6.25.29 - - [26/Feb/2008:05:09:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -61.135.166.102 - - [26/Feb/2008:05:10:12 -0600] "GET / HTTP/1.1" 200 4447 -213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 -213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.180.137.172 - - [26/Feb/2008:05:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.180.137.172 - - [26/Feb/2008:05:12:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.180.137.172 - - [26/Feb/2008:05:12:58 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 -213.180.137.172 - - [26/Feb/2008:05:13:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -150.254.74.235 - - [26/Feb/2008:05:16:26 -0600] "GET /ply/ HTTP/1.0" 200 8018 -150.254.74.235 - - [26/Feb/2008:05:16:41 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -150.254.74.235 - - [26/Feb/2008:05:16:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -150.254.74.235 - - [26/Feb/2008:05:16:58 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -84.58.212.53 - - [26/Feb/2008:05:20:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -84.58.212.53 - - [26/Feb/2008:05:20:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.58.212.53 - - [26/Feb/2008:05:20:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.58.212.53 - - [26/Feb/2008:05:20:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.58.212.53 - - [26/Feb/2008:05:20:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:05:28:38 -0600] "GET /ply/README HTTP/1.1" 200 8605 -199.111.224.90 - - [26/Feb/2008:05:29:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -62.219.208.169 - - [26/Feb/2008:05:45:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.219.208.169 - - [26/Feb/2008:05:45:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.219.208.169 - - [26/Feb/2008:05:45:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [26/Feb/2008:05:51:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.232.14 - - [26/Feb/2008:05:51:57 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -137.226.57.203 - - [26/Feb/2008:05:55:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -137.226.57.203 - - [26/Feb/2008:05:55:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [26/Feb/2008:05:55:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.171.34.14 - - [26/Feb/2008:05:57:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -200.171.34.14 - - [26/Feb/2008:05:57:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.171.98.139 - - [26/Feb/2008:06:02:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -203.171.98.139 - - [26/Feb/2008:06:02:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -217.218.215.8 - - [26/Feb/2008:06:03:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -217.218.215.8 - - [26/Feb/2008:06:03:29 -0600] "GET /ply/ HTTP/1.0" 200 8018 -217.218.215.8 - - [26/Feb/2008:06:03:32 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -217.218.215.8 - - [26/Feb/2008:06:04:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -217.218.215.8 - - [26/Feb/2008:06:04:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -62.219.208.169 - - [26/Feb/2008:06:05:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.20.149 - - [26/Feb/2008:06:05:49 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.0" 200 731143 -203.171.98.139 - - [26/Feb/2008:06:05:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -203.171.98.139 - - [26/Feb/2008:06:06:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 -203.171.98.139 - - [26/Feb/2008:06:06:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 -62.219.208.169 - - [26/Feb/2008:06:08:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [26/Feb/2008:06:11:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.213.227.243 - - [26/Feb/2008:06:11:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -193.213.227.243 - - [26/Feb/2008:06:11:25 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -82.239.61.147 - - [26/Feb/2008:06:11:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.213.227.243 - - [26/Feb/2008:06:11:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -193.213.227.243 - - [26/Feb/2008:06:11:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 -122.55.236.192 - - [26/Feb/2008:06:14:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -131.111.47.93 - - [26/Feb/2008:06:16:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -195.41.139.41 - - [26/Feb/2008:06:24:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 -195.41.139.41 - - [26/Feb/2008:06:24:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -195.41.139.41 - - [26/Feb/2008:06:24:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.235.1.29 - - [26/Feb/2008:06:25:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -85.235.1.29 - - [26/Feb/2008:06:25:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.235.1.29 - - [26/Feb/2008:06:25:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -87.244.30.18 - - [26/Feb/2008:06:29:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 -87.244.30.18 - - [26/Feb/2008:06:29:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -87.244.30.18 - - [26/Feb/2008:06:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.244.30.18 - - [26/Feb/2008:06:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.244.30.18 - - [26/Feb/2008:06:30:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.244.30.18 - - [26/Feb/2008:06:30:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -87.244.30.18 - - [26/Feb/2008:06:30:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -41.221.19.2 - - [26/Feb/2008:06:32:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -41.221.19.2 - - [26/Feb/2008:06:32:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 21275 -219.64.4.24 - - [26/Feb/2008:06:34:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -219.64.4.24 - - [26/Feb/2008:06:34:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqHPUXSharedLibraries HTTP/1.0" 200 3683 -62.153.70.82 - - [26/Feb/2008:06:40:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.153.70.82 - - [26/Feb/2008:06:40:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.153.70.82 - - [26/Feb/2008:06:40:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.153.70.82 - - [26/Feb/2008:06:40:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.153.70.82 - - [26/Feb/2008:06:40:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.213.227.243 - - [26/Feb/2008:06:42:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -193.213.227.243 - - [26/Feb/2008:06:42:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 -84.110.129.69 - - [26/Feb/2008:06:42:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.129.69 - - [26/Feb/2008:06:42:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 999 -203.73.43.189 - - [26/Feb/2008:06:46:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.24.58.194 - - [26/Feb/2008:06:48:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.24.58.194 - - [26/Feb/2008:06:48:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.24.58.194 - - [26/Feb/2008:06:48:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -83.24.58.194 - - [26/Feb/2008:06:48:52 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -141.84.220.111 - - [26/Feb/2008:06:50:42 -0600] "GET /ply HTTP/1.1" 301 242 -141.84.220.111 - - [26/Feb/2008:06:50:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 -141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.67.152.241 - - [26/Feb/2008:06:52:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.67.152.241 - - [26/Feb/2008:06:52:08 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 243188 -71.57.91.136 - - [26/Feb/2008:06:54:44 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213787 -141.84.220.111 - - [26/Feb/2008:06:55:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.198.156.6 - - [26/Feb/2008:06:55:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -199.111.224.90 - - [26/Feb/2008:06:57:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:06:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:06:57:24 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -199.111.224.90 - - [26/Feb/2008:07:01:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:07:01:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.226.179.4 - - [26/Feb/2008:07:02:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 -194.226.179.4 - - [26/Feb/2008:07:02:19 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -194.226.179.4 - - [26/Feb/2008:07:02:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -203.200.218.2 - - [26/Feb/2008:07:03:54 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 -85.179.0.240 - - [26/Feb/2008:07:03:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.224.90 - - [26/Feb/2008:07:03:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.179.0.240 - - [26/Feb/2008:07:03:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -85.179.0.240 - - [26/Feb/2008:07:03:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.179.0.240 - - [26/Feb/2008:07:04:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -199.111.224.90 - - [26/Feb/2008:07:04:02 -0600] "GET /ply/README HTTP/1.1" 200 8605 -199.111.224.90 - - [26/Feb/2008:07:05:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.7.134 - - [26/Feb/2008:07:08:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1236 -137.226.57.203 - - [26/Feb/2008:07:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -137.226.57.203 - - [26/Feb/2008:07:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [26/Feb/2008:07:09:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.146.189.12 - - [26/Feb/2008:07:10:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -62.153.70.82 - - [26/Feb/2008:07:14:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.153.70.82 - - [26/Feb/2008:07:15:01 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -62.153.70.82 - - [26/Feb/2008:07:15:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.153.70.82 - - [26/Feb/2008:07:15:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [26/Feb/2008:07:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.197.76.25 - - [26/Feb/2008:07:22:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -217.197.76.25 - - [26/Feb/2008:07:22:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -217.197.76.25 - - [26/Feb/2008:07:22:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:07:23:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.197.76.25 - - [26/Feb/2008:07:23:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -159.149.89.40 - - [26/Feb/2008:07:24:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -159.149.89.40 - - [26/Feb/2008:07:24:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -159.149.89.40 - - [26/Feb/2008:07:24:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -217.197.76.25 - - [26/Feb/2008:07:26:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -199.111.224.90 - - [26/Feb/2008:07:26:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.22.22 - - [26/Feb/2008:07:43:23 -0600] "GET /cartage/index.html HTTP/1.0" 404 133 -199.111.224.90 - - [26/Feb/2008:07:44:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:07:44:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [26/Feb/2008:07:46:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.239.61.147 - - [26/Feb/2008:07:49:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.246.129.196 - - [26/Feb/2008:07:52:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.221.197.20 - - [26/Feb/2008:07:52:45 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -81.80.245.157 - - [26/Feb/2008:07:54:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 -66.232.113.194 - - [26/Feb/2008:07:55:43 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 -200.51.41.29 - - [26/Feb/2008:07:55:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -161.53.125.15 - - [26/Feb/2008:07:55:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -218.109.113.209 - - [26/Feb/2008:07:57:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -218.109.113.209 - - [26/Feb/2008:07:57:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -64.22.160.1 - - [26/Feb/2008:07:57:55 -0600] "GET / HTTP/1.1" 200 4447 -64.22.160.1 - - [26/Feb/2008:07:58:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -64.22.160.1 - - [26/Feb/2008:07:58:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -121.95.130.217 - - [26/Feb/2008:07:58:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -121.95.130.217 - - [26/Feb/2008:07:58:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -121.95.130.217 - - [26/Feb/2008:07:58:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.245.137.240 - - [26/Feb/2008:08:00:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -70.245.137.240 - - [26/Feb/2008:08:00:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -70.245.137.240 - - [26/Feb/2008:08:00:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -77.91.224.3 - - [26/Feb/2008:08:08:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 -77.91.224.3 - - [26/Feb/2008:08:08:01 -0600] "GET / HTTP/1.1" 200 4447 -77.91.224.3 - - [26/Feb/2008:08:10:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -87.170.204.27 - - [26/Feb/2008:08:11:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -87.170.204.27 - - [26/Feb/2008:08:11:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -87.170.204.27 - - [26/Feb/2008:08:11:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 -64.233.178.136 - - [26/Feb/2008:08:11:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 -87.170.204.27 - - [26/Feb/2008:08:12:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 -199.111.224.90 - - [26/Feb/2008:08:12:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -190.74.202.25 - - [26/Feb/2008:08:12:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -75.205.208.176 - - [26/Feb/2008:08:15:46 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -75.205.208.176 - - [26/Feb/2008:08:15:55 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -217.65.240.234 - - [26/Feb/2008:08:18:17 -0600] "GET /ply/ply.html HTTP/1.0" 304 - -217.65.240.234 - - [26/Feb/2008:08:18:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -217.65.240.234 - - [26/Feb/2008:08:18:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -82.239.61.147 - - [26/Feb/2008:08:22:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.84.68.242 - - [26/Feb/2008:08:29:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -87.84.68.242 - - [26/Feb/2008:08:30:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -75.205.208.176 - - [26/Feb/2008:08:30:00 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 1373410 -87.84.68.242 - - [26/Feb/2008:08:30:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.221.197.20 - - [26/Feb/2008:08:30:00 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -59.103.3.200 - - [26/Feb/2008:08:33:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 -206.51.237.114 - - [26/Feb/2008:08:33:01 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 -59.103.3.200 - - [26/Feb/2008:08:33:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 -196.207.40.212 - - [26/Feb/2008:08:33:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -143.248.134.61 - - [26/Feb/2008:08:33:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -89.179.223.243 - - [26/Feb/2008:08:33:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -89.179.223.243 - - [26/Feb/2008:08:34:02 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1296 -202.160.174.4 - - [26/Feb/2008:08:36:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -202.160.174.4 - - [26/Feb/2008:08:36:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -202.160.174.4 - - [26/Feb/2008:08:36:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -202.160.174.4 - - [26/Feb/2008:08:37:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -202.160.174.4 - - [26/Feb/2008:08:38:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 -193.205.212.172 - - [26/Feb/2008:08:41:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -193.205.212.172 - - [26/Feb/2008:08:41:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.205.212.172 - - [26/Feb/2008:08:42:00 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -193.205.212.172 - - [26/Feb/2008:08:42:05 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/ApiFreeze HTTP/1.1" 200 4108 -193.205.212.172 - - [26/Feb/2008:08:42:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -193.205.212.172 - - [26/Feb/2008:08:42:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -193.205.212.172 - - [26/Feb/2008:08:42:26 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -122.117.168.219 - - [26/Feb/2008:08:43:20 -0600] "GET /ply/ HTTP/1.1" 304 - -122.117.168.219 - - [26/Feb/2008:08:43:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -122.117.168.219 - - [26/Feb/2008:08:43:23 -0600] "GET /ply/example.html HTTP/1.1" 304 - -200.21.209.183 - - [26/Feb/2008:08:45:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.100.130.8 - - [26/Feb/2008:08:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.100.130.8 - - [26/Feb/2008:08:46:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.100.130.8 - - [26/Feb/2008:08:46:11 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -192.100.130.8 - - [26/Feb/2008:08:46:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.100.130.8 - - [26/Feb/2008:08:46:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.214.45.129 - - [26/Feb/2008:08:46:52 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.214.45.129 - - [26/Feb/2008:08:46:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1337 -200.21.209.183 - - [26/Feb/2008:08:47:18 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -213.156.57.189 - - [26/Feb/2008:08:49:16 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -213.156.57.189 - - [26/Feb/2008:08:49:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.156.57.189 - - [26/Feb/2008:08:49:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -213.156.57.189 - - [26/Feb/2008:08:49:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -137.226.57.203 - - [26/Feb/2008:08:50:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [26/Feb/2008:08:50:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [26/Feb/2008:08:50:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.23.124.137 - - [26/Feb/2008:08:50:41 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 206 3623122 -88.112.138.119 - - [26/Feb/2008:08:53:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 -88.112.138.119 - - [26/Feb/2008:08:53:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -88.112.138.119 - - [26/Feb/2008:08:53:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.112.138.119 - - [26/Feb/2008:08:53:32 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -130.236.182.96 - - [26/Feb/2008:08:55:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.35.216.121 - - [26/Feb/2008:09:03:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.35.216.121 - - [26/Feb/2008:09:03:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.35.216.121 - - [26/Feb/2008:09:03:17 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 -82.35.216.121 - - [26/Feb/2008:09:03:19 -0600] "GET /cgi-bin/wiki.pl?action=rc&from=1201142973 HTTP/1.1" 200 1887 -82.35.216.121 - - [26/Feb/2008:09:03:21 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4762 -82.35.216.121 - - [26/Feb/2008:09:03:33 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=365 HTTP/1.1" 200 9413 -82.35.216.121 - - [26/Feb/2008:09:03:40 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -193.190.253.151 - - [26/Feb/2008:09:12:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -193.190.253.151 - - [26/Feb/2008:09:12:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.190.253.151 - - [26/Feb/2008:09:12:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 -67.195.44.110 - - [26/Feb/2008:09:12:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 -216.171.98.77 - - [26/Feb/2008:09:19:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 -212.85.1.1 - - [26/Feb/2008:09:19:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 -212.85.1.1 - - [26/Feb/2008:09:19:51 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -75.23.124.137 - - [26/Feb/2008:09:20:40 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -75.23.124.137 - - [26/Feb/2008:09:20:40 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [26/Feb/2008:09:22:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -209.120.207.235 - - [26/Feb/2008:09:23:45 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -209.120.207.235 - - [26/Feb/2008:09:23:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.160.138.222 - - [26/Feb/2008:09:23:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -88.160.138.222 - - [26/Feb/2008:09:23:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.160.138.222 - - [26/Feb/2008:09:23:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.160.138.222 - - [26/Feb/2008:09:23:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -98.193.69.179 - - [26/Feb/2008:09:24:22 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -150.210.155.167 - - [26/Feb/2008:09:26:51 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -87.65.165.194 - - [26/Feb/2008:09:27:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -87.65.165.194 - - [26/Feb/2008:09:27:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.226.57.203 - - [26/Feb/2008:09:34:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.160.180.70 - - [26/Feb/2008:09:36:05 -0600] "GET /robots.txt HTTP/1.0" 200 71 -202.160.179.46 - - [26/Feb/2008:09:36:13 -0600] "GET /ply/ HTTP/1.0" 304 - -138.100.217.162 - - [26/Feb/2008:09:39:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 -138.100.217.162 - - [26/Feb/2008:09:39:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -138.100.217.162 - - [26/Feb/2008:09:39:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.160.172.70 - - [26/Feb/2008:09:39:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -212.160.172.70 - - [26/Feb/2008:09:39:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -138.100.217.162 - - [26/Feb/2008:09:39:49 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -192.54.144.229 - - [26/Feb/2008:09:41:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -192.54.144.229 - - [26/Feb/2008:09:41:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.54.144.229 - - [26/Feb/2008:09:41:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.65.240.234 - - [26/Feb/2008:09:42:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -208.22.104.18 - - [26/Feb/2008:09:42:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -148.87.1.172 - - [26/Feb/2008:09:43:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -148.87.1.172 - - [26/Feb/2008:09:44:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -201.236.226.90 - - [26/Feb/2008:09:44:34 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [26/Feb/2008:09:44:36 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -201.236.226.90 - - [26/Feb/2008:09:44:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [26/Feb/2008:09:44:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [26/Feb/2008:09:44:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -64.156.216.96 - - [26/Feb/2008:09:44:57 -0600] "GET /python.html HTTP/1.1" 200 18870 -64.156.216.96 - - [26/Feb/2008:09:45:00 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -64.156.216.96 - - [26/Feb/2008:09:45:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -63.239.69.1 - - [26/Feb/2008:09:47:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -63.239.69.1 - - [26/Feb/2008:09:47:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 -64.156.216.96 - - [26/Feb/2008:09:48:33 -0600] "GET /training.html HTTP/1.1" 200 6154 -204.246.129.196 - - [26/Feb/2008:09:48:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -128.29.43.1 - - [26/Feb/2008:09:48:41 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -128.29.43.1 - - [26/Feb/2008:09:48:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.54.144.229 - - [26/Feb/2008:09:49:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.24.76 - - [26/Feb/2008:09:51:25 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 -63.239.69.1 - - [26/Feb/2008:09:53:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -63.239.69.1 - - [26/Feb/2008:09:53:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 -63.239.69.1 - - [26/Feb/2008:09:53:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 -63.239.69.1 - - [26/Feb/2008:09:54:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 -67.195.58.174 - - [26/Feb/2008:09:54:12 -0600] "GET /ply/ HTTP/1.0" 304 - -63.239.69.1 - - [26/Feb/2008:09:54:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMultipleLanguageSharedLibraries HTTP/1.1" 200 2600 -216.191.234.70 - - [26/Feb/2008:09:54:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -216.191.234.70 - - [26/Feb/2008:09:54:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.7.75 - - [26/Feb/2008:09:55:13 -0600] "GET /swig/SWIG_Doc1.pdf HTTP/1.0" 304 - -89.128.52.247 - - [26/Feb/2008:09:55:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.128.52.247 - - [26/Feb/2008:09:55:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.128.52.247 - - [26/Feb/2008:09:56:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.65.240.234 - - [26/Feb/2008:10:04:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.55.208.117 - - [26/Feb/2008:10:09:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.117 - - [26/Feb/2008:10:09:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.1" 200 990 -192.54.144.229 - - [26/Feb/2008:10:10:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.145.52.154 - - [26/Feb/2008:10:12:06 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -68.145.52.154 - - [26/Feb/2008:10:12:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /python.html HTTP/1.1" 200 18870 -18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -18.188.70.114 - - [26/Feb/2008:10:15:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.149.31.231 - - [26/Feb/2008:10:18:17 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -139.149.31.231 - - [26/Feb/2008:10:18:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.193.220.201 - - [26/Feb/2008:10:20:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -192.193.220.201 - - [26/Feb/2008:10:20:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -63.239.69.1 - - [26/Feb/2008:10:21:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 -68.145.52.154 - - [26/Feb/2008:10:24:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.145.52.154 - - [26/Feb/2008:10:24:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.121.187.18 - - [26/Feb/2008:10:25:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.121.187.18 - - [26/Feb/2008:10:25:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.121.187.18 - - [26/Feb/2008:10:26:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -80.121.187.18 - - [26/Feb/2008:10:26:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -80.121.187.18 - - [26/Feb/2008:10:26:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.121.187.18 - - [26/Feb/2008:10:27:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.175.40.146 - - [26/Feb/2008:10:30:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -64.175.40.146 - - [26/Feb/2008:10:30:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.172.19.20 - - [26/Feb/2008:10:30:51 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -64.175.40.146 - - [26/Feb/2008:10:31:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.169.46.98 - - [26/Feb/2008:10:31:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -213.41.243.144 - - [26/Feb/2008:10:31:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -122.117.168.219 - - [26/Feb/2008:10:32:12 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -192.54.144.229 - - [26/Feb/2008:10:33:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.191.234.70 - - [26/Feb/2008:10:37:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -216.191.234.70 - - [26/Feb/2008:10:38:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -216.191.234.70 - - [26/Feb/2008:10:39:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 -38.104.0.30 - - [26/Feb/2008:10:43:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.104.0.30 - - [26/Feb/2008:10:43:24 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -76.223.13.234 - - [26/Feb/2008:10:44:05 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -76.223.13.234 - - [26/Feb/2008:10:44:09 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -18.188.70.114 - - [26/Feb/2008:10:46:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.230.107 - - [26/Feb/2008:10:47:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.15.187.198 - - [26/Feb/2008:10:51:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -24.15.187.198 - - [26/Feb/2008:10:51:47 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -83.249.251.158 - - [26/Feb/2008:10:53:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -83.249.251.158 - - [26/Feb/2008:10:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [26/Feb/2008:10:53:46 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -141.35.1.57 - - [26/Feb/2008:10:54:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -141.35.1.57 - - [26/Feb/2008:10:54:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -141.35.1.57 - - [26/Feb/2008:10:54:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -141.35.1.57 - - [26/Feb/2008:10:55:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 -141.35.1.57 - - [26/Feb/2008:10:56:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.0" 200 2725 -128.143.230.107 - - [26/Feb/2008:10:57:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -189.177.109.221 - - [26/Feb/2008:11:01:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -189.177.109.221 - - [26/Feb/2008:11:01:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.177.109.221 - - [26/Feb/2008:11:01:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -217.196.43.134 - - [26/Feb/2008:11:05:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 -77.127.17.194 - - [26/Feb/2008:11:06:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -77.127.17.194 - - [26/Feb/2008:11:06:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -77.127.17.194 - - [26/Feb/2008:11:06:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -77.127.17.194 - - [26/Feb/2008:11:06:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.1" 200 69278 -205.196.222.10 - - [26/Feb/2008:11:07:46 -0600] "GET /robots.txt HTTP/1.0" 200 71 -205.196.222.10 - - [26/Feb/2008:11:07:46 -0600] "GET /ply/ HTTP/1.0" 200 8018 -65.55.212.77 - - [26/Feb/2008:11:09:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.55.212.77 - - [26/Feb/2008:11:09:44 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -194.117.40.162 - - [26/Feb/2008:11:10:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -194.117.40.162 - - [26/Feb/2008:11:10:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.117.40.162 - - [26/Feb/2008:11:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.117.40.162 - - [26/Feb/2008:11:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.117.40.162 - - [26/Feb/2008:11:11:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -194.117.40.162 - - [26/Feb/2008:11:11:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -24.7.210.64 - - [26/Feb/2008:11:12:54 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.7.210.64 - - [26/Feb/2008:11:13:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -149.65.130.31 - - [26/Feb/2008:11:13:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -149.65.130.31 - - [26/Feb/2008:11:13:23 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 -149.65.130.31 - - [26/Feb/2008:11:13:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 997 -24.1.159.241 - - [26/Feb/2008:11:16:40 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -24.1.159.241 - - [26/Feb/2008:11:16:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [26/Feb/2008:11:16:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [26/Feb/2008:11:17:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [26/Feb/2008:11:17:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -134.67.6.11 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -134.67.6.11 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -204.246.129.196 - - [26/Feb/2008:11:20:23 -0600] "GET /ply HTTP/1.1" 301 242 -204.246.129.196 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.98.120.84 - - [26/Feb/2008:11:23:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [26/Feb/2008:11:23:28 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [26/Feb/2008:11:23:31 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [26/Feb/2008:11:23:32 -0600] "GET / HTTP/1.1" 200 4447 -84.233.245.68 - - [26/Feb/2008:11:24:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -84.233.245.68 - - [26/Feb/2008:11:24:42 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 -84.233.245.68 - - [26/Feb/2008:11:25:19 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.0" 200 1775 -84.233.245.68 - - [26/Feb/2008:11:25:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -84.233.245.68 - - [26/Feb/2008:11:25:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 -38.113.160.194 - - [26/Feb/2008:11:30:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -38.113.160.194 - - [26/Feb/2008:11:30:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -38.113.160.194 - - [26/Feb/2008:11:30:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -38.113.160.194 - - [26/Feb/2008:11:30:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -128.135.125.239 - - [26/Feb/2008:11:31:03 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -128.135.125.239 - - [26/Feb/2008:11:31:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.239 - - [26/Feb/2008:11:31:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.239 - - [26/Feb/2008:11:31:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.135.125.239 - - [26/Feb/2008:11:31:28 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -38.113.160.194 - - [26/Feb/2008:11:32:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -38.113.160.194 - - [26/Feb/2008:11:33:21 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -129.42.208.182 - - [26/Feb/2008:11:39:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 -129.42.208.182 - - [26/Feb/2008:11:39:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -72.172.42.58 - - [26/Feb/2008:11:42:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -72.172.42.58 - - [26/Feb/2008:11:42:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.0.246.111 - - [26/Feb/2008:11:47:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -90.0.246.111 - - [26/Feb/2008:11:47:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -90.0.246.111 - - [26/Feb/2008:11:47:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.0.246.111 - - [26/Feb/2008:11:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.239 - - [26/Feb/2008:11:51:14 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -74.6.25.20 - - [26/Feb/2008:11:57:01 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.23.225 - - [26/Feb/2008:11:57:01 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 -72.22.5.152 - - [26/Feb/2008:11:57:40 -0600] "GET /cv.html HTTP/1.1" 200 31798 -195.212.29.92 - - [26/Feb/2008:11:59:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.15.187.198 - - [26/Feb/2008:12:00:22 -0600] "GET /dynamic/ HTTP/1.1" 304 - -24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.15.187.198 - - [26/Feb/2008:12:00:37 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -24.15.187.198 - - [26/Feb/2008:12:00:44 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -129.42.208.182 - - [26/Feb/2008:12:02:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET / HTTP/1.1" 200 4447 -128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.245 - - [26/Feb/2008:12:07:00 -0600] "GET /dynamic HTTP/1.1" 301 246 -128.135.125.245 - - [26/Feb/2008:12:07:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 -141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.204.246 - - [26/Feb/2008:12:14:48 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.204.246 - - [26/Feb/2008:12:14:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 972 -195.131.84.202 - - [26/Feb/2008:12:17:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -195.131.84.219 - - [26/Feb/2008:12:17:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -64.3.45.46 - - [26/Feb/2008:12:18:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -64.3.45.46 - - [26/Feb/2008:12:18:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.3.45.46 - - [26/Feb/2008:12:18:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -195.131.84.251 - - [26/Feb/2008:12:19:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.152.228 - - [26/Feb/2008:12:23:05 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.0" 200 279116 -128.135.152.228 - - [26/Feb/2008:12:23:36 -0600] "GET /dynamic/assign5.html HTTP/1.0" 200 11008 -128.135.152.228 - - [26/Feb/2008:12:24:44 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 200 133908 -128.135.152.228 - - [26/Feb/2008:12:25:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.202.49.172 - - [26/Feb/2008:12:33:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 -67.202.49.172 - - [26/Feb/2008:12:34:04 -0600] "GET / HTTP/1.1" 200 4447 -67.195.44.110 - - [26/Feb/2008:12:35:28 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.195.44.109 - - [26/Feb/2008:12:35:46 -0600] "GET /ply/ HTTP/1.0" 200 8018 -38.113.160.194 - - [26/Feb/2008:12:38:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -38.113.160.194 - - [26/Feb/2008:12:38:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -38.113.160.194 - - [26/Feb/2008:12:38:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -24.15.187.198 - - [26/Feb/2008:12:38:54 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -24.15.187.198 - - [26/Feb/2008:12:39:17 -0600] "GET /dynamic/ HTTP/1.1" 304 - -24.15.187.198 - - [26/Feb/2008:12:39:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -72.30.226.134 - - [26/Feb/2008:12:40:09 -0600] "GET /ply/ HTTP/1.0" 200 8018 -64.3.45.46 - - [26/Feb/2008:12:40:43 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -24.15.187.198 - - [26/Feb/2008:12:40:48 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -204.154.183.65 - - [26/Feb/2008:12:40:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -204.154.183.65 - - [26/Feb/2008:12:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.154.183.65 - - [26/Feb/2008:12:40:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.154.183.65 - - [26/Feb/2008:12:42:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.148.212.222 - - [26/Feb/2008:12:43:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -216.148.212.222 - - [26/Feb/2008:12:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.148.212.222 - - [26/Feb/2008:12:44:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.157.220.80 - - [26/Feb/2008:12:45:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -75.157.220.80 - - [26/Feb/2008:12:45:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -75.157.220.80 - - [26/Feb/2008:12:45:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.3.45.46 - - [26/Feb/2008:12:47:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -64.3.45.46 - - [26/Feb/2008:12:47:42 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -64.34.145.201 - - [26/Feb/2008:12:48:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 -64.34.145.201 - - [26/Feb/2008:12:48:37 -0600] "GET /photos/u505/pages/IMG_1514.htm HTTP/1.0" 404 133 -65.54.99.91 - - [26/Feb/2008:12:49:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.54.99.91 - - [26/Feb/2008:12:49:09 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -65.54.99.91 - - [26/Feb/2008:12:49:09 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 200 64334 -65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 107720 -65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /python.html HTTP/1.1" 200 18870 -65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.1" 200 66002 -65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET / HTTP/1.1" 200 4447 -65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 -65.54.99.91 - - [26/Feb/2008:12:49:12 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 62496 -65.54.99.91 - - [26/Feb/2008:12:49:12 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.1" 200 74610 -24.15.187.198 - - [26/Feb/2008:12:51:57 -0600] "GET /dynamic/ HTTP/1.1" 304 - -24.15.187.198 - - [26/Feb/2008:12:52:00 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -24.15.187.198 - - [26/Feb/2008:12:52:13 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -195.252.125.134 - - [26/Feb/2008:12:52:13 -0600] "GET /swig/swig-1.3.28rc2.tar.gz HTTP/1.1" 200 3972567 -24.15.187.198 - - [26/Feb/2008:12:52:30 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -24.15.187.198 - - [26/Feb/2008:12:52:39 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -86.157.119.197 - - [26/Feb/2008:12:53:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -196.25.255.210 - - [26/Feb/2008:12:56:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.157.119.197 - - [26/Feb/2008:12:56:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -196.25.255.210 - - [26/Feb/2008:12:56:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.157.119.197 - - [26/Feb/2008:12:56:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -13.13.16.1 - - [26/Feb/2008:12:58:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -129.42.208.182 - - [26/Feb/2008:12:59:53 -0600] "GET /ply/ HTTP/1.1" 304 - -129.42.208.182 - - [26/Feb/2008:12:59:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -129.42.208.182 - - [26/Feb/2008:12:59:57 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -74.6.27.16 - - [26/Feb/2008:13:11:51 -0600] "GET /software.html HTTP/1.0" 304 - -148.241.78.63 - - [26/Feb/2008:13:12:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -148.241.78.63 - - [26/Feb/2008:13:12:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -148.241.78.63 - - [26/Feb/2008:13:12:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -148.241.78.63 - - [26/Feb/2008:13:13:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.45.109.126 - - [26/Feb/2008:13:13:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -161.45.109.126 - - [26/Feb/2008:13:13:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -161.45.109.126 - - [26/Feb/2008:13:13:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -148.241.78.63 - - [26/Feb/2008:13:13:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -148.241.78.63 - - [26/Feb/2008:13:13:52 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -161.45.109.126 - - [26/Feb/2008:13:14:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.45.105.199 - - [26/Feb/2008:13:14:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 -161.45.105.199 - - [26/Feb/2008:13:14:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -161.45.105.199 - - [26/Feb/2008:13:14:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.45.109.126 - - [26/Feb/2008:13:14:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.45.105.199 - - [26/Feb/2008:13:15:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.45.105.199 - - [26/Feb/2008:13:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.45.105.199 - - [26/Feb/2008:13:18:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.218.181 - - [26/Feb/2008:13:23:08 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.218.181 - - [26/Feb/2008:13:23:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 -89.165.73.169 - - [26/Feb/2008:13:32:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.32.37.138 - - [26/Feb/2008:13:33:14 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -75.32.37.138 - - [26/Feb/2008:13:33:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.191.234.70 - - [26/Feb/2008:13:33:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -216.191.234.70 - - [26/Feb/2008:13:33:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 -88.248.226.142 - - [26/Feb/2008:13:34:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -88.248.226.142 - - [26/Feb/2008:13:34:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/index.html HTTP/1.1" 200 40215 -64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/hello.gif HTTP/1.1" 200 1118 -64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame0.gif HTTP/1.1" 200 432 -64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/gp.gif HTTP/1.1" 200 43556 -64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame1.gif HTTP/1.1" 200 2588 -64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame2.gif HTTP/1.1" 200 2237 -64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/clip.gif HTTP/1.1" 200 21566 -64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/plot2d.gif HTTP/1.1" 200 5902 -64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d.gif HTTP/1.1" 200 2212 -64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_2.gif HTTP/1.1" 200 888 -64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_3.gif HTTP/1.1" 200 1194 -64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_4.gif HTTP/1.1" 200 811 -64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_5.gif HTTP/1.1" 200 1223 -64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_6.gif HTTP/1.1" 200 1870 -64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/plot3d_1.gif HTTP/1.1" 200 16420 -64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/plot3d_2.gif HTTP/1.1" 200 34142 -64.113.185.61 - - [26/Feb/2008:13:40:14 -0600] "GET /gifplot/plot3d_3.gif HTTP/1.1" 200 55793 -128.221.197.20 - - [26/Feb/2008:13:42:31 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -148.241.78.63 - - [26/Feb/2008:13:47:47 -0600] "GET /ply/ HTTP/1.1" 304 - -148.241.78.63 - - [26/Feb/2008:13:47:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -88.90.231.185 - - [26/Feb/2008:13:48:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.135.125.245 - - [26/Feb/2008:13:51:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -88.90.231.185 - - [26/Feb/2008:13:52:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -88.90.231.185 - - [26/Feb/2008:13:52:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.245 - - [26/Feb/2008:13:52:11 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -216.54.141.250 - - [26/Feb/2008:13:52:56 -0600] "GET /python.html HTTP/1.1" 200 18870 -216.54.141.250 - - [26/Feb/2008:13:52:57 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -216.54.141.250 - - [26/Feb/2008:13:52:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.16.64.124 - - [26/Feb/2008:13:55:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.16.64.124 - - [26/Feb/2008:13:55:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -82.16.64.124 - - [26/Feb/2008:13:55:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.16.64.124 - - [26/Feb/2008:13:55:54 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -161.45.109.126 - - [26/Feb/2008:13:59:38 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -161.45.109.126 - - [26/Feb/2008:14:06:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 12705 -89.165.73.169 - - [26/Feb/2008:14:08:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -148.241.78.63 - - [26/Feb/2008:14:19:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -148.241.78.63 - - [26/Feb/2008:14:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -148.241.78.63 - - [26/Feb/2008:14:19:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -148.241.78.63 - - [26/Feb/2008:14:19:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.31.151 - - [26/Feb/2008:14:21:41 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -88.111.158.229 - - [26/Feb/2008:14:23:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -88.111.158.229 - - [26/Feb/2008:14:23:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -88.111.158.229 - - [26/Feb/2008:14:23:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.185.65.84 - - [26/Feb/2008:14:25:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -90.185.65.84 - - [26/Feb/2008:14:25:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -90.185.65.84 - - [26/Feb/2008:14:25:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.185.65.84 - - [26/Feb/2008:14:25:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.22.104.18 - - [26/Feb/2008:14:30:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -208.22.104.18 - - [26/Feb/2008:14:30:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -128.221.197.20 - - [26/Feb/2008:14:30:42 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -208.22.104.18 - - [26/Feb/2008:14:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMultipleInputTypemaps HTTP/1.1" 200 2300 -90.185.65.84 - - [26/Feb/2008:14:31:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -208.22.104.18 - - [26/Feb/2008:14:31:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -208.22.104.18 - - [26/Feb/2008:14:32:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -208.22.104.18 - - [26/Feb/2008:14:34:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 -208.22.104.18 - - [26/Feb/2008:14:34:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 -208.22.104.18 - - [26/Feb/2008:14:35:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -67.195.58.172 - - [26/Feb/2008:14:42:14 -0600] "GET /index.html HTTP/1.0" 304 - -80.91.229.6 - - [26/Feb/2008:14:44:58 -0600] "GET /robots.txt HTTP/1.1" 200 71 -80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /robots.txt HTTP/1.1" 200 71 -80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.91.229.6 - - [26/Feb/2008:14:45:00 -0600] "GET / HTTP/1.1" 200 4447 -80.91.229.6 - - [26/Feb/2008:14:45:00 -0600] "GET / HTTP/1.1" 200 4447 -129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET / HTTP/1.1" 200 4447 -129.21.160.34 - - [26/Feb/2008:14:47:48 -0600] "GET / HTTP/1.1" 200 4447 -128.221.197.20 - - [26/Feb/2008:14:50:57 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -68.164.42.125 - - [26/Feb/2008:14:53:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -68.164.42.125 - - [26/Feb/2008:14:53:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.164.42.125 - - [26/Feb/2008:14:53:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -68.164.42.125 - - [26/Feb/2008:14:53:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -77.91.224.3 - - [26/Feb/2008:14:54:07 -0600] "GET /software.html HTTP/1.1" 200 3163 -74.6.31.165 - - [26/Feb/2008:14:54:22 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.165 - - [26/Feb/2008:14:54:22 -0600] "GET /ply/ HTTP/1.0" 200 8018 -68.164.42.125 - - [26/Feb/2008:14:54:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -77.91.224.13 - - [26/Feb/2008:14:54:32 -0600] "GET /robots.txt HTTP/1.1" 200 71 -77.91.224.13 - - [26/Feb/2008:14:54:32 -0600] "GET /training.html HTTP/1.1" 200 6154 -68.164.42.125 - - [26/Feb/2008:14:54:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 -209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /dynamic HTTP/1.1" 301 246 -209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -209.120.207.254 - - [26/Feb/2008:14:55:35 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -200.21.98.7 - - [26/Feb/2008:14:55:48 -0600] "GET / HTTP/1.0" 200 4447 -200.21.98.7 - - [26/Feb/2008:14:55:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -200.21.98.7 - - [26/Feb/2008:14:55:52 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 -209.120.207.254 - - [26/Feb/2008:14:55:58 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -84.110.202.119 - - [26/Feb/2008:14:56:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.202.119 - - [26/Feb/2008:14:56:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 -77.91.224.3 - - [26/Feb/2008:14:56:17 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -171.161.224.10 - - [26/Feb/2008:14:56:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -171.161.224.10 - - [26/Feb/2008:14:56:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -171.161.224.10 - - [26/Feb/2008:14:56:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -77.91.224.3 - - [26/Feb/2008:14:57:07 -0600] "GET /about.html HTTP/1.1" 200 7890 -77.91.224.13 - - [26/Feb/2008:14:57:13 -0600] "GET /writing.html HTTP/1.1" 200 2871 -77.91.224.3 - - [26/Feb/2008:14:59:19 -0600] "GET /python.html HTTP/1.1" 200 18870 -77.91.224.13 - - [26/Feb/2008:15:00:09 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -209.120.207.235 - - [26/Feb/2008:15:01:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -200.21.98.7 - - [26/Feb/2008:15:02:31 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 31530 -139.140.198.50 - - [26/Feb/2008:15:06:52 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 200 189268 -82.135.12.203 - - [26/Feb/2008:15:14:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.135.12.203 - - [26/Feb/2008:15:14:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.135.12.203 - - [26/Feb/2008:15:14:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -82.135.12.203 - - [26/Feb/2008:15:14:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 -82.135.12.203 - - [26/Feb/2008:15:14:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -86.157.119.197 - - [26/Feb/2008:15:15:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.135.12.203 - - [26/Feb/2008:15:19:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.135.12.203 - - [26/Feb/2008:15:19:25 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -82.135.12.203 - - [26/Feb/2008:15:19:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 -24.10.16.193 - - [26/Feb/2008:15:19:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.135.12.203 - - [26/Feb/2008:15:19:38 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -24.10.16.193 - - [26/Feb/2008:15:19:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.10.16.193 - - [26/Feb/2008:15:19:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.135.12.203 - - [26/Feb/2008:15:20:21 -0600] "GET /cgi-bin/wiki.pl?action=editprefs HTTP/1.1" 200 4233 -24.10.16.193 - - [26/Feb/2008:15:23:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -24.10.16.193 - - [26/Feb/2008:15:23:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -88.112.138.119 - - [26/Feb/2008:15:23:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.55.2.36 - - [26/Feb/2008:15:23:59 -0600] "GET / HTTP/1.1" 200 4447 -192.55.2.36 - - [26/Feb/2008:15:23:59 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /about.html HTTP/1.1" 200 7890 -192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 -192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 -192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 -192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 -192.55.2.36 - - [26/Feb/2008:15:24:46 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 -192.55.2.36 - - [26/Feb/2008:15:24:51 -0600] "GET /diet.html HTTP/1.1" 404 133 -192.55.2.36 - - [26/Feb/2008:15:25:02 -0600] "GET /index.html HTTP/1.1" 200 4447 -88.191.19.81 - - [26/Feb/2008:15:31:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 -209.120.207.235 - - [26/Feb/2008:15:33:27 -0600] "GET /dynamic HTTP/1.1" 301 246 -209.120.207.235 - - [26/Feb/2008:15:33:27 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -209.120.207.235 - - [26/Feb/2008:15:33:35 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -139.11.6.202 - - [26/Feb/2008:15:37:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -139.11.6.202 - - [26/Feb/2008:15:37:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -139.11.6.202 - - [26/Feb/2008:15:37:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 -139.11.6.202 - - [26/Feb/2008:15:38:13 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -139.11.6.202 - - [26/Feb/2008:15:38:22 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 -139.11.6.202 - - [26/Feb/2008:15:38:27 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.0" 200 3248 -139.11.6.202 - - [26/Feb/2008:15:38:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 -139.11.6.202 - - [26/Feb/2008:15:38:35 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.0" 200 1927 -139.11.6.202 - - [26/Feb/2008:15:38:45 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.0" 200 4493 -139.11.6.202 - - [26/Feb/2008:15:38:51 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.0" 200 1579 -139.11.6.202 - - [26/Feb/2008:15:38:55 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.0" 200 8981 -66.249.65.37 - - [26/Feb/2008:15:39:16 -0600] "GET /robots.txt HTTP/1.1" 200 71 -66.249.65.37 - - [26/Feb/2008:15:39:17 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 304 - -82.41.78.129 - - [26/Feb/2008:15:43:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.41.78.129 - - [26/Feb/2008:15:43:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -82.41.78.129 - - [26/Feb/2008:15:43:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.41.78.129 - - [26/Feb/2008:15:43:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -88.112.138.119 - - [26/Feb/2008:15:44:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -79.69.70.197 - - [26/Feb/2008:15:46:01 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -79.69.70.197 - - [26/Feb/2008:15:46:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.41.78.129 - - [26/Feb/2008:15:48:33 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -74.6.19.115 - - [26/Feb/2008:15:49:58 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.31.170 - - [26/Feb/2008:15:49:59 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.170 - - [26/Feb/2008:15:49:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 -146.189.58.99 - - [26/Feb/2008:15:53:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -146.189.58.99 - - [26/Feb/2008:15:53:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -146.189.58.99 - - [26/Feb/2008:15:53:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 -146.189.58.99 - - [26/Feb/2008:15:53:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -85.228.229.73 - - [26/Feb/2008:15:55:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -85.228.229.73 - - [26/Feb/2008:15:55:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.228.229.73 - - [26/Feb/2008:15:55:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -85.228.229.73 - - [26/Feb/2008:15:56:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 -85.228.229.73 - - [26/Feb/2008:15:56:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.1" 200 5005 -38.98.120.84 - - [26/Feb/2008:15:56:14 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [26/Feb/2008:15:56:15 -0600] "GET / HTTP/1.1" 200 4447 -216.9.243.111 - - [26/Feb/2008:15:57:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -216.9.243.111 - - [26/Feb/2008:15:57:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -216.9.243.111 - - [26/Feb/2008:15:57:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.9.243.111 - - [26/Feb/2008:15:58:32 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -216.9.243.111 - - [26/Feb/2008:15:58:53 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 304 - -128.135.125.239 - - [26/Feb/2008:15:59:12 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -216.9.243.111 - - [26/Feb/2008:15:59:53 -0600] "GET /ply/ HTTP/1.1" 304 - -216.9.243.111 - - [26/Feb/2008:15:59:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -208.80.193.45 - - [26/Feb/2008:16:00:46 -0600] "GET / HTTP/1.1" 200 4447 -89.128.216.67 - - [26/Feb/2008:16:01:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.128.216.67 - - [26/Feb/2008:16:01:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.128.216.67 - - [26/Feb/2008:16:01:24 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 -128.8.114.84 - - [26/Feb/2008:16:03:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -128.8.114.84 - - [26/Feb/2008:16:03:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.8.114.84 - - [26/Feb/2008:16:03:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.97.218.10 - - [26/Feb/2008:16:06:31 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -208.97.218.10 - - [26/Feb/2008:16:06:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.97.218.10 - - [26/Feb/2008:16:06:37 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -208.97.218.10 - - [26/Feb/2008:16:06:41 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -66.249.65.37 - - [26/Feb/2008:16:09:51 -0600] "GET /software.html HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:10:11 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:10:15 -0600] "GET /ply/README HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:10:34 -0600] "GET /ply/?ref=%C4%B0lkSexShop.Com HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:11:18 -0600] "GET /writing.html HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:11:29 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:12:06 -0600] "GET /ply/ HTTP/1.1" 304 - -128.100.195.84 - - [26/Feb/2008:16:12:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.100.195.84 - - [26/Feb/2008:16:12:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.100.195.84 - - [26/Feb/2008:16:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [26/Feb/2008:16:12:14 -0600] "GET /dynamic/assign1.html HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:12:14 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:12:30 -0600] "GET /consulting.html HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:12:49 -0600] "GET /ply HTTP/1.1" 301 242 -66.249.65.37 - - [26/Feb/2008:16:12:58 -0600] "GET /ply/index.html HTTP/1.1" 304 - -66.249.65.37 - - [26/Feb/2008:16:13:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.100.195.84 - - [26/Feb/2008:16:14:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -67.173.185.186 - - [26/Feb/2008:16:14:11 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 212696 -67.173.185.186 - - [26/Feb/2008:16:14:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.173.185.186 - - [26/Feb/2008:16:14:16 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213580 -67.173.185.186 - - [26/Feb/2008:16:14:16 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213787 -67.173.185.186 - - [26/Feb/2008:16:16:05 -0600] "GET / HTTP/1.1" 200 4447 -67.173.185.186 - - [26/Feb/2008:16:16:06 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -67.173.185.186 - - [26/Feb/2008:16:16:42 -0600] "GET /assign5.html HTTP/1.1" 404 133 -74.6.23.209 - - [26/Feb/2008:16:16:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE011.HTM HTTP/1.0" 200 1231 -67.173.185.186 - - [26/Feb/2008:16:16:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -68.12.248.157 - - [26/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.252.149.16 - - [26/Feb/2008:16:24:32 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.16 - - [26/Feb/2008:16:24:41 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 304 - -128.100.195.84 - - [26/Feb/2008:16:25:14 -0600] "GET /ply/README HTTP/1.1" 200 8605 -68.1.82.124 - - [26/Feb/2008:16:30:11 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -68.1.82.124 - - [26/Feb/2008:16:30:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.167.100.246 - - [26/Feb/2008:16:33:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -99.167.100.246 - - [26/Feb/2008:16:33:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.167.100.246 - - [26/Feb/2008:16:33:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.233.178.136 - - [26/Feb/2008:16:33:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 -190.24.33.53 - - [26/Feb/2008:16:34:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -64.233.178.136 - - [26/Feb/2008:16:35:57 -0600] "GET /ply/README HTTP/1.0" 200 8605 -89.170.38.243 - - [26/Feb/2008:16:36:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.170.38.243 - - [26/Feb/2008:16:36:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.170.38.243 - - [26/Feb/2008:16:36:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.233.178.136 - - [26/Feb/2008:16:39:14 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -71.133.9.141 - - [26/Feb/2008:16:47:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -71.133.9.141 - - [26/Feb/2008:16:47:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.133.9.141 - - [26/Feb/2008:16:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 -199.111.224.90 - - [26/Feb/2008:16:49:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.133.9.141 - - [26/Feb/2008:16:53:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -71.133.9.141 - - [26/Feb/2008:16:53:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 -199.111.224.90 - - [26/Feb/2008:16:54:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.198.216.189 - - [26/Feb/2008:16:55:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -199.198.216.189 - - [26/Feb/2008:16:55:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.198.216.189 - - [26/Feb/2008:16:55:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -199.198.216.189 - - [26/Feb/2008:16:56:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -210.143.35.17 - - [26/Feb/2008:16:56:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:17:05:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [26/Feb/2008:17:05:42 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -64.167.244.66 - - [26/Feb/2008:17:08:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.226.91.246 - - [26/Feb/2008:17:15:57 -0600] "GET /ply/ HTTP/1.1" 304 - -202.226.91.246 - - [26/Feb/2008:17:15:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -202.226.91.246 - - [26/Feb/2008:17:17:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [26/Feb/2008:17:30:12 -0600] "GET /robots.txt HTTP/1.1" 200 71 -66.249.65.37 - - [26/Feb/2008:17:30:12 -0600] "GET /ply HTTP/1.1" 301 242 -67.228.115.170 - - [26/Feb/2008:17:32:30 -0600] "GET / HTTP/1.1" 200 4447 -67.228.115.170 - - [26/Feb/2008:17:32:30 -0600] "GET /index.html HTTP/1.1" 200 4447 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /training.html HTTP/1.1" 200 6154 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /software.html HTTP/1.1" 200 3163 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /about.html HTTP/1.1" 200 7890 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /python.html HTTP/1.1" 200 18870 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /publications.html HTTP/1.1" 200 7758 -67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /cv.html HTTP/1.1" 200 31798 -67.228.115.170 - - [26/Feb/2008:17:32:32 -0600] "GET /diet.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:32 -0600] "GET /syllabus.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:38 -0600] "GET /01Introduction.pdf HTTP/1.1" 200 2172146 -67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /assign1.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /soln1.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /02WorkingWithData.pdf HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /assign2.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /smackdown.py HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:40 -0600] "GET /03ProgramStructure.pdf HTTP/1.1" 200 279926 -67.228.115.170 - - [26/Feb/2008:17:32:40 -0600] "GET /assign3.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:42 -0600] "GET /04Objects.pdf HTTP/1.1" 200 502854 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /05ObjectModel.pdf HTTP/1.1" 200 719628 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /assign4.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /06FilesAndText.pdf HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /assign5.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /07Functional.pdf HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /ply.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /example.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /README HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /PLYTalk.pdf HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /support.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /Doc/index.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /exec.html HTTP/1.1" 404 133 -67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Usenix2001/beazley.pdf HTTP/1.1" 200 76713 -67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 -67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 -67.228.115.170 - - [26/Feb/2008:17:32:47 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 -67.228.115.170 - - [26/Feb/2008:17:32:48 -0600] "GET /papers/IPPS97/IPPS97.pdf HTTP/1.1" 200 82126 -67.228.115.170 - - [26/Feb/2008:17:32:48 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 -67.228.115.170 - - [26/Feb/2008:17:32:49 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 -67.228.115.170 - - [26/Feb/2008:17:32:50 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 -67.228.115.170 - - [26/Feb/2008:17:32:51 -0600] "GET /papers/Perl98/swigperl.pdf HTTP/1.1" 200 151655 -67.228.115.170 - - [26/Feb/2008:17:32:51 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 -67.228.115.170 - - [26/Feb/2008:17:32:52 -0600] "GET /swigperl.pdf HTTP/1.1" 404 133 -128.135.239.253 - - [26/Feb/2008:17:33:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -128.135.239.253 - - [26/Feb/2008:17:33:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.239.253 - - [26/Feb/2008:17:33:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.174 - - [26/Feb/2008:17:34:47 -0600] "GET /ply/ HTTP/1.0" 304 - -203.144.160.249 - - [26/Feb/2008:17:38:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -67.195.58.182 - - [26/Feb/2008:17:38:50 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 304 - -67.195.58.186 - - [26/Feb/2008:17:38:53 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 304 - -128.143.136.157 - - [26/Feb/2008:17:39:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.144.160.249 - - [26/Feb/2008:17:39:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -203.144.160.249 - - [26/Feb/2008:17:39:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -67.195.58.151 - - [26/Feb/2008:17:39:32 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 304 - -67.195.58.170 - - [26/Feb/2008:17:39:38 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 304 - -203.144.160.249 - - [26/Feb/2008:17:39:46 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 -67.195.58.181 - - [26/Feb/2008:17:40:14 -0600] "GET /ply/support.html HTTP/1.0" 304 - -67.195.58.170 - - [26/Feb/2008:17:40:17 -0600] "GET / HTTP/1.0" 304 - -67.195.58.177 - - [26/Feb/2008:17:41:00 -0600] "GET /python.html HTTP/1.0" 200 18870 -67.195.58.185 - - [26/Feb/2008:17:41:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -128.143.136.157 - - [26/Feb/2008:17:41:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:17:41:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.168 - - [26/Feb/2008:17:42:31 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 -129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.34.145.194 - - [26/Feb/2008:17:45:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 -64.34.145.194 - - [26/Feb/2008:17:45:39 -0600] "GET /photos/u505/pages/IMG_1516.htm HTTP/1.0" 404 133 -128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.114.59.172 - - [26/Feb/2008:17:46:55 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -199.46.245.233 - - [26/Feb/2008:17:51:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 -199.46.245.233 - - [26/Feb/2008:17:51:46 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -199.46.245.233 - - [26/Feb/2008:17:51:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.114.59.172 - - [26/Feb/2008:17:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.213.243.113 - - [26/Feb/2008:17:54:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -69.213.243.113 - - [26/Feb/2008:17:54:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -67.195.58.164 - - [26/Feb/2008:17:55:03 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 304 - -69.213.243.113 - - [26/Feb/2008:17:57:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -69.213.243.113 - - [26/Feb/2008:17:57:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 -69.213.243.113 - - [26/Feb/2008:17:58:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 -74.6.22.11 - - [26/Feb/2008:18:02:12 -0600] "GET /dynamic/assign3.html HTTP/1.0" 200 6798 -74.6.28.205 - - [26/Feb/2008:18:03:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 -216.31.211.11 - - [26/Feb/2008:18:03:21 -0600] "GET /python.html HTTP/1.1" 200 18870 -216.31.211.11 - - [26/Feb/2008:18:03:21 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -216.31.211.11 - - [26/Feb/2008:18:03:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [26/Feb/2008:18:03:36 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -217.196.43.134 - - [26/Feb/2008:18:05:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.22.217 - - [26/Feb/2008:18:17:27 -0600] "GET /dynamic/04Objects.pdf HTTP/1.0" 200 514533 -67.195.58.168 - - [26/Feb/2008:18:19:04 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 304 - -74.6.20.14 - - [26/Feb/2008:18:19:37 -0600] "GET /tenure/trip3.html HTTP/1.0" 404 133 -87.194.101.28 - - [26/Feb/2008:18:28:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -87.194.101.28 - - [26/Feb/2008:18:29:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12329 -87.194.101.28 - - [26/Feb/2008:18:29:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -87.194.101.28 - - [26/Feb/2008:18:29:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.194.101.28 - - [26/Feb/2008:18:29:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -65.55.208.123 - - [26/Feb/2008:18:31:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.123 - - [26/Feb/2008:18:31:54 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE047.HTM HTTP/1.1" 304 - -128.143.136.157 - - [26/Feb/2008:18:35:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:18:35:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.86.204.99 - - [26/Feb/2008:18:39:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -38.98.120.84 - - [26/Feb/2008:18:39:55 -0600] "GET / HTTP/1.1" 200 4447 -65.57.245.11 - - [26/Feb/2008:18:40:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -66.232.113.194 - - [26/Feb/2008:18:43:36 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -130.86.204.99 - - [26/Feb/2008:18:43:43 -0600] "GET /ply/README HTTP/1.1" 200 8605 -125.244.152.66 - - [26/Feb/2008:18:43:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -202.216.177.14 - - [26/Feb/2008:18:43:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -85.185.11.131 - - [26/Feb/2008:18:43:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -216.148.212.222 - - [26/Feb/2008:18:50:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:18:55:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:18:55:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:18:55:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.31.145 - - [26/Feb/2008:18:56:31 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -206.51.237.114 - - [26/Feb/2008:19:02:12 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -213.29.144.10 - - [26/Feb/2008:19:02:13 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -132.181.247.105 - - [26/Feb/2008:19:07:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -132.181.247.105 - - [26/Feb/2008:19:07:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -77.91.224.3 - - [26/Feb/2008:19:13:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -65.55.208.123 - - [26/Feb/2008:19:13:59 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE067.HTM HTTP/1.1" 304 - -65.55.208.123 - - [26/Feb/2008:19:13:59 -0600] "GET /photos/u505/pages/IMG_1517.htm HTTP/1.1" 404 133 -67.195.34.108 - - [26/Feb/2008:19:18:09 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -67.195.34.114 - - [26/Feb/2008:19:19:39 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -62.59.179.107 - - [26/Feb/2008:19:21:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.59.179.107 - - [26/Feb/2008:19:21:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.59.179.107 - - [26/Feb/2008:19:21:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.59.179.107 - - [26/Feb/2008:19:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.59.179.107 - - [26/Feb/2008:19:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.59.179.107 - - [26/Feb/2008:19:22:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.59.179.107 - - [26/Feb/2008:19:23:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.59.179.107 - - [26/Feb/2008:19:23:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.59.179.107 - - [26/Feb/2008:19:23:16 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -67.195.34.111 - - [26/Feb/2008:19:24:20 -0600] "GET /ply HTTP/1.0" 301 230 -67.195.34.111 - - [26/Feb/2008:19:24:20 -0600] "GET /ply/ HTTP/1.0" 200 8018 -74.6.23.73 - - [26/Feb/2008:19:24:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE068.HTM HTTP/1.0" 200 1394 -62.59.179.107 - - [26/Feb/2008:19:27:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.79.38.12 - - [26/Feb/2008:19:27:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -213.79.38.12 - - [26/Feb/2008:19:27:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -213.79.38.12 - - [26/Feb/2008:19:28:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.195.244 - - [26/Feb/2008:19:29:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.195.244 - - [26/Feb/2008:19:30:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -75.58.86.1 - - [26/Feb/2008:19:30:50 -0600] "GET /dynamic/ HTTP/1.1" 304 - -74.6.26.171 - - [26/Feb/2008:19:30:51 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.195.34.97 - - [26/Feb/2008:19:30:51 -0600] "GET /ply HTTP/1.0" 301 230 -67.195.34.97 - - [26/Feb/2008:19:30:51 -0600] "GET /ply/ HTTP/1.0" 200 8018 -199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.167.150 - - [26/Feb/2008:19:32:11 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -62.59.179.107 - - [26/Feb/2008:19:35:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.10 - - [26/Feb/2008:19:36:30 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -201.83.27.70 - - [26/Feb/2008:19:38:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.59.179.107 - - [26/Feb/2008:19:39:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.83.27.70 - - [26/Feb/2008:19:39:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.83.27.70 - - [26/Feb/2008:19:39:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.59.179.107 - - [26/Feb/2008:19:39:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.83.27.70 - - [26/Feb/2008:19:39:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.83.27.70 - - [26/Feb/2008:19:39:07 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -201.83.27.70 - - [26/Feb/2008:19:39:34 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -199.111.205.10 - - [26/Feb/2008:19:45:16 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.202.49.172 - - [26/Feb/2008:19:45:41 -0600] "GET /robots.txt HTTP/1.1" 200 71 -128.143.117.220 - - [26/Feb/2008:19:56:33 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.117.220 - - [26/Feb/2008:19:56:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.220 - - [26/Feb/2008:19:56:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.220 - - [26/Feb/2008:19:58:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.220 - - [26/Feb/2008:19:58:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.13.91.57 - - [26/Feb/2008:20:00:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.13.91.57 - - [26/Feb/2008:20:00:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -74.6.19.236 - - [26/Feb/2008:20:04:05 -0600] "GET /training.html HTTP/1.0" 200 6154 -62.59.179.107 - - [26/Feb/2008:20:05:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.160.180.70 - - [26/Feb/2008:20:06:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 -202.160.180.213 - - [26/Feb/2008:20:07:22 -0600] "GET / HTTP/1.0" 200 4447 -76.189.146.60 - - [26/Feb/2008:20:10:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -76.189.146.60 - - [26/Feb/2008:20:10:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.189.146.60 - - [26/Feb/2008:20:10:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 -76.189.146.60 - - [26/Feb/2008:20:11:10 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -76.189.146.60 - - [26/Feb/2008:20:11:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -76.189.146.60 - - [26/Feb/2008:20:11:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [26/Feb/2008:20:12:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -192.94.38.34 - - [26/Feb/2008:20:16:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -71.130.247.245 - - [26/Feb/2008:20:27:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 -71.130.247.245 - - [26/Feb/2008:20:27:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.130.247.245 - - [26/Feb/2008:20:27:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -60.249.147.81 - - [26/Feb/2008:20:28:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 -60.249.147.81 - - [26/Feb/2008:20:28:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -60.249.147.81 - - [26/Feb/2008:20:28:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -60.249.147.81 - - [26/Feb/2008:20:28:27 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -60.249.147.81 - - [26/Feb/2008:20:28:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.94.38.34 - - [26/Feb/2008:20:29:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -192.94.38.34 - - [26/Feb/2008:20:29:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1631 -192.94.38.34 - - [26/Feb/2008:20:29:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -65.214.45.129 - - [26/Feb/2008:20:35:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 -205.214.235.18 - - [26/Feb/2008:20:40:34 -0600] "GET /ply HTTP/1.1" 301 242 -205.214.235.18 - - [26/Feb/2008:20:40:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 -205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -205.214.235.18 - - [26/Feb/2008:20:40:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -205.214.235.18 - - [26/Feb/2008:20:41:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -194.249.52.137 - - [26/Feb/2008:20:44:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 -194.249.52.137 - - [26/Feb/2008:20:44:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -194.249.52.137 - - [26/Feb/2008:20:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.249.52.137 - - [26/Feb/2008:20:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.46.29.140 - - [26/Feb/2008:20:45:10 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2739 -219.87.152.215 - - [26/Feb/2008:20:45:12 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -69.217.73.52 - - [26/Feb/2008:20:45:13 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -80.97.94.178 - - [26/Feb/2008:20:45:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -201.63.117.142 - - [26/Feb/2008:20:45:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -99.232.163.8 - - [26/Feb/2008:20:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.191.19.81 - - [26/Feb/2008:20:45:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.195.58.174 - - [26/Feb/2008:20:46:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 304 - -67.195.58.188 - - [26/Feb/2008:20:47:24 -0600] "GET /ply/example.html HTTP/1.0" 304 - -67.195.58.188 - - [26/Feb/2008:20:47:24 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 304 - -67.195.58.178 - - [26/Feb/2008:20:47:49 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 304 - -67.176.147.11 - - [26/Feb/2008:20:51:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.176.147.11 - - [26/Feb/2008:20:51:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.116.72.114 - - [26/Feb/2008:20:52:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.116.72.114 - - [26/Feb/2008:20:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.232.163.8 - - [26/Feb/2008:20:53:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -99.232.163.8 - - [26/Feb/2008:20:53:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.232.163.8 - - [26/Feb/2008:20:53:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.232.163.8 - - [26/Feb/2008:20:53:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -194.249.52.137 - - [26/Feb/2008:20:54:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.249.52.137 - - [26/Feb/2008:20:54:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -194.249.52.137 - - [26/Feb/2008:20:54:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -194.249.52.137 - - [26/Feb/2008:20:54:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.22.21.146 - - [26/Feb/2008:20:58:16 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -203.73.43.189 - - [26/Feb/2008:20:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.93.50 - - [26/Feb/2008:21:00:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.189.93.50 - - [26/Feb/2008:21:00:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.189.93.50 - - [26/Feb/2008:21:00:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.93.50 - - [26/Feb/2008:21:00:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.93.50 - - [26/Feb/2008:21:00:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.93.50 - - [26/Feb/2008:21:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.93.50 - - [26/Feb/2008:21:01:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.93.50 - - [26/Feb/2008:21:01:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.189.93.50 - - [26/Feb/2008:21:01:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -66.189.93.50 - - [26/Feb/2008:21:01:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -84.110.229.30 - - [26/Feb/2008:21:04:00 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.229.30 - - [26/Feb/2008:21:04:01 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 -82.249.138.163 - - [26/Feb/2008:21:07:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.249.138.163 - - [26/Feb/2008:21:07:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -82.249.138.163 - - [26/Feb/2008:21:07:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.249.138.163 - - [26/Feb/2008:21:07:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -193.252.149.15 - - [26/Feb/2008:21:08:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.15 - - [26/Feb/2008:21:08:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 -195.3.173.130 - - [26/Feb/2008:21:12:25 -0600] "GET /ply/ HTTP/1.1" 304 - -195.3.173.130 - - [26/Feb/2008:21:12:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -195.3.173.130 - - [26/Feb/2008:21:12:43 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 61381 -98.206.164.173 - - [26/Feb/2008:21:16:17 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -98.206.164.173 - - [26/Feb/2008:21:16:26 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -69.219.161.183 - - [26/Feb/2008:21:19:43 -0600] "GET /python.html HTTP/1.1" 200 18870 -69.219.161.183 - - [26/Feb/2008:21:19:44 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -69.219.161.183 - - [26/Feb/2008:21:19:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.232.163.8 - - [26/Feb/2008:21:20:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 -99.232.163.8 - - [26/Feb/2008:21:21:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [26/Feb/2008:21:21:53 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -74.6.7.107 - - [26/Feb/2008:21:24:19 -0600] "GET /swill/ HTTP/1.0" 200 3786 -201.236.226.90 - - [26/Feb/2008:21:26:23 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [26/Feb/2008:21:26:26 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -201.236.226.90 - - [26/Feb/2008:21:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [26/Feb/2008:21:26:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -201.236.226.90 - - [26/Feb/2008:21:26:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [26/Feb/2008:21:26:47 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 206 23851 -201.236.226.90 - - [26/Feb/2008:21:26:59 -0600] "GET /python.html HTTP/1.1" 200 18870 -201.236.226.90 - - [26/Feb/2008:21:27:03 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -208.97.218.10 - - [26/Feb/2008:21:28:20 -0600] "GET /python.html HTTP/1.1" 200 18870 -208.97.218.10 - - [26/Feb/2008:21:28:21 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -208.97.218.10 - - [26/Feb/2008:21:28:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.5.217.3 - - [26/Feb/2008:21:36:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -203.5.217.3 - - [26/Feb/2008:21:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.5.217.3 - - [26/Feb/2008:21:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.164 - - [26/Feb/2008:21:41:59 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 -204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 -204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 -204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.111.252.233 - - [26/Feb/2008:21:42:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -204.111.252.233 - - [26/Feb/2008:21:42:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.113.58.196 - - [26/Feb/2008:21:44:56 -0600] "GET / HTTP/1.1" 200 4447 -70.113.58.196 - - [26/Feb/2008:21:47:24 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -70.113.58.196 - - [26/Feb/2008:21:47:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.113.58.196 - - [26/Feb/2008:21:47:53 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -70.113.58.196 - - [26/Feb/2008:21:47:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -72.14.195.226 - - [26/Feb/2008:21:47:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -204.111.252.233 - - [26/Feb/2008:21:48:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -204.111.252.233 - - [26/Feb/2008:21:48:00 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -204.111.252.233 - - [26/Feb/2008:21:48:01 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -204.111.252.233 - - [26/Feb/2008:21:48:02 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -203.5.217.3 - - [26/Feb/2008:21:48:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.198.190.123 - - [26/Feb/2008:21:49:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -203.198.190.123 - - [26/Feb/2008:21:49:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.14.195.226 - - [26/Feb/2008:21:53:56 -0600] "GET /ply/README HTTP/1.1" 200 8605 -68.165.56.5 - - [26/Feb/2008:21:54:41 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -68.165.56.5 - - [26/Feb/2008:21:54:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.165.56.5 - - [26/Feb/2008:21:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.165.56.5 - - [26/Feb/2008:21:54:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -74.6.26.147 - - [26/Feb/2008:21:56:50 -0600] "GET /photos/wind/pages/IMG_1331.htm HTTP/1.0" 404 133 -70.113.58.196 - - [26/Feb/2008:22:02:43 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -70.113.58.196 - - [26/Feb/2008:22:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.113.58.196 - - [26/Feb/2008:22:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.250.6.243 - - [26/Feb/2008:22:04:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -128.250.6.243 - - [26/Feb/2008:22:04:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -67.165.221.14 - - [26/Feb/2008:22:05:31 -0600] "GET / HTTP/1.1" 200 4447 -67.165.221.14 - - [26/Feb/2008:22:05:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -67.165.221.14 - - [26/Feb/2008:22:05:51 -0600] "GET /training.html HTTP/1.1" 200 6154 -67.165.221.14 - - [26/Feb/2008:22:06:29 -0600] "GET /software.html HTTP/1.1" 200 3163 -67.165.221.14 - - [26/Feb/2008:22:06:31 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -67.165.221.14 - - [26/Feb/2008:22:06:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 -67.165.221.14 - - [26/Feb/2008:22:06:46 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 -67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /about.html HTTP/1.1" 200 7890 -67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 -67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 -67.165.221.14 - - [26/Feb/2008:22:06:49 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 -67.165.221.14 - - [26/Feb/2008:22:06:49 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 -67.165.221.14 - - [26/Feb/2008:22:08:25 -0600] "GET /diet.html HTTP/1.1" 404 133 -67.165.221.14 - - [26/Feb/2008:22:08:31 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 -71.145.147.182 - - [26/Feb/2008:22:19:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -71.145.147.182 - - [26/Feb/2008:22:19:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:22:19:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:22:19:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 -128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.145.147.182 - - [26/Feb/2008:22:19:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -128.143.38.176 - - [26/Feb/2008:22:20:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -74.6.28.122 - - [26/Feb/2008:22:22:23 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 -71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.220 - - [26/Feb/2008:22:33:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.117.220 - - [26/Feb/2008:22:33:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.10 - - [26/Feb/2008:22:33:44 -0600] "GET /ply/README HTTP/1.1" 200 8605 -199.111.228.99 - - [26/Feb/2008:22:34:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.233.178.136 - - [26/Feb/2008:22:39:17 -0600] "GET /ply/ HTTP/1.0" 200 8018 -163.29.130.55 - - [26/Feb/2008:22:39:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -64.124.85.80 - - [26/Feb/2008:22:40:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 -64.124.85.80 - - [26/Feb/2008:22:42:26 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -222.153.72.162 - - [26/Feb/2008:22:43:28 -0600] "GET / HTTP/1.1" 200 4447 -128.143.117.220 - - [26/Feb/2008:22:45:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.220 - - [26/Feb/2008:22:45:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.228.99 - - [26/Feb/2008:23:04:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.126.245.198 - - [26/Feb/2008:23:04:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -203.126.222.62 - - [26/Feb/2008:23:04:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.126.222.62 - - [26/Feb/2008:23:04:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -74.6.26.212 - - [26/Feb/2008:23:07:59 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.0" 200 85901 -65.214.45.129 - - [26/Feb/2008:23:09:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.214.45.129 - - [26/Feb/2008:23:09:54 -0600] "GET / HTTP/1.0" 200 4447 -65.55.208.120 - - [26/Feb/2008:23:19:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.120 - - [26/Feb/2008:23:19:45 -0600] "GET /training.html HTTP/1.1" 200 6154 -128.250.6.243 - - [26/Feb/2008:23:22:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.143.136.157 - - [26/Feb/2008:23:23:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [26/Feb/2008:23:23:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.250.6.243 - - [26/Feb/2008:23:26:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -128.250.6.243 - - [26/Feb/2008:23:26:48 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 -128.250.6.243 - - [26/Feb/2008:23:26:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 -217.172.44.82 - - [26/Feb/2008:23:27:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.25.20 - - [26/Feb/2008:23:28:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.176.147.11 - - [26/Feb/2008:23:29:15 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -67.176.147.11 - - [26/Feb/2008:23:34:13 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -98.206.164.173 - - [26/Feb/2008:23:41:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -98.206.164.173 - - [26/Feb/2008:23:41:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.206.164.173 - - [26/Feb/2008:23:44:04 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -65.214.45.129 - - [26/Feb/2008:23:49:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE121.HTM HTTP/1.0" 200 1316 -59.93.252.161 - - [26/Feb/2008:23:53:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -59.93.252.161 - - [26/Feb/2008:23:53:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.143.35.13 - - [27/Feb/2008:00:09:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.143.35.13 - - [27/Feb/2008:00:09:43 -0600] "GET /favicon.gif HTTP/1.1" 404 133 -210.143.35.13 - - [27/Feb/2008:00:09:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.143.35.13 - - [27/Feb/2008:00:09:54 -0600] "GET /favicon.gif HTTP/1.1" 404 133 -61.11.86.160 - - [27/Feb/2008:00:13:50 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -61.11.86.160 - - [27/Feb/2008:00:13:51 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -61.11.86.160 - - [27/Feb/2008:00:13:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -61.11.86.160 - - [27/Feb/2008:00:14:13 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -213.145.165.82 - - [27/Feb/2008:00:15:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -61.11.86.160 - - [27/Feb/2008:00:17:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.82.115.10 - - [27/Feb/2008:00:26:17 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.0" 200 188949 -67.195.58.169 - - [27/Feb/2008:00:26:53 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 304 - -74.6.20.121 - - [27/Feb/2008:00:29:28 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.0" 200 279116 -67.195.58.175 - - [27/Feb/2008:00:47:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 304 - -65.214.45.129 - - [27/Feb/2008:00:49:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 -91.98.137.144 - - [27/Feb/2008:00:52:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -213.217.40.133 - - [27/Feb/2008:00:52:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -91.98.137.144 - - [27/Feb/2008:00:52:34 -0600] "GET / HTTP/1.1" 200 4447 -213.217.40.133 - - [27/Feb/2008:00:52:36 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -91.98.137.144 - - [27/Feb/2008:00:52:38 -0600] "GET /training.html HTTP/1.1" 200 6154 -91.98.137.144 - - [27/Feb/2008:00:52:43 -0600] "GET /index.html HTTP/1.1" 200 4447 -91.98.137.144 - - [27/Feb/2008:00:52:52 -0600] "GET /software.html HTTP/1.1" 200 3163 -91.98.137.144 - - [27/Feb/2008:00:53:14 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 -91.98.137.144 - - [27/Feb/2008:00:53:19 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 62496 -91.98.137.144 - - [27/Feb/2008:00:53:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -122.164.159.157 - - [27/Feb/2008:00:55:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -122.164.159.157 - - [27/Feb/2008:00:55:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -41.232.39.203 - - [27/Feb/2008:01:03:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -41.232.39.203 - - [27/Feb/2008:01:03:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.196.43.134 - - [27/Feb/2008:01:05:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.137.228.16 - - [27/Feb/2008:01:08:59 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 130588 -69.137.228.16 - - [27/Feb/2008:01:09:00 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 -69.137.228.16 - - [27/Feb/2008:01:09:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [27/Feb/2008:01:11:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.206.102.103 - - [27/Feb/2008:01:16:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 -68.206.102.103 - - [27/Feb/2008:01:16:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -68.206.102.103 - - [27/Feb/2008:01:16:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.160 - - [27/Feb/2008:01:22:43 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 304 - -199.111.224.90 - - [27/Feb/2008:01:26:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.65.240.234 - - [27/Feb/2008:01:28:22 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -38.104.7.146 - - [27/Feb/2008:01:28:36 -0600] "HEAD /ply/ HTTP/1.0" 200 0 -38.104.7.146 - - [27/Feb/2008:01:28:36 -0600] "GET /ply/ HTTP/1.0" 200 8018 -80.121.187.18 - - [27/Feb/2008:01:29:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 -157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -80.121.187.18 - - [27/Feb/2008:01:29:51 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -80.121.187.18 - - [27/Feb/2008:01:29:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -80.121.187.18 - - [27/Feb/2008:01:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -80.121.187.18 - - [27/Feb/2008:01:30:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -80.121.187.18 - - [27/Feb/2008:01:31:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -80.121.187.18 - - [27/Feb/2008:01:31:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -80.121.187.18 - - [27/Feb/2008:01:31:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 -76.24.27.20 - - [27/Feb/2008:01:33:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 -76.24.27.20 - - [27/Feb/2008:01:33:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -76.24.27.20 - - [27/Feb/2008:01:33:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.24.27.20 - - [27/Feb/2008:01:33:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -76.24.27.20 - - [27/Feb/2008:01:33:44 -0600] "GET /ply/README HTTP/1.1" 200 8605 -65.55.208.121 - - [27/Feb/2008:01:35:19 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.121 - - [27/Feb/2008:01:35:19 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 304 - -67.195.58.170 - - [27/Feb/2008:01:42:56 -0600] "GET / HTTP/1.0" 304 - -67.195.58.178 - - [27/Feb/2008:01:43:35 -0600] "GET /software.html HTTP/1.0" 200 3163 -67.195.58.185 - - [27/Feb/2008:01:44:22 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -65.55.212.77 - - [27/Feb/2008:01:48:02 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.20.75 - - [27/Feb/2008:02:00:30 -0600] "GET /papers/IPPS97/IPPS97.pdf HTTP/1.0" 304 - -130.76.32.182 - - [27/Feb/2008:02:00:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -130.76.32.182 - - [27/Feb/2008:02:00:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.76.32.182 - - [27/Feb/2008:02:00:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -130.76.32.182 - - [27/Feb/2008:02:00:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -130.76.32.182 - - [27/Feb/2008:02:01:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 -130.76.32.182 - - [27/Feb/2008:02:01:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsDarwin HTTP/1.1" 200 1916 -130.76.32.182 - - [27/Feb/2008:02:01:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -65.54.165.36 - - [27/Feb/2008:02:02:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.54.165.36 - - [27/Feb/2008:02:02:35 -0600] "GET /python.html HTTP/1.1" 200 18870 -130.76.32.182 - - [27/Feb/2008:02:06:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -130.76.32.182 - - [27/Feb/2008:02:06:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -130.76.32.182 - - [27/Feb/2008:02:06:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 -157.99.64.13 - - [27/Feb/2008:02:06:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -157.99.64.13 - - [27/Feb/2008:02:06:45 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -217.65.240.234 - - [27/Feb/2008:02:08:30 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -130.76.32.182 - - [27/Feb/2008:02:09:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -212.35.109.3 - - [27/Feb/2008:02:10:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 -212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.214.45.129 - - [27/Feb/2008:02:13:14 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE035.HTM HTTP/1.0" 200 1122 -67.195.58.174 - - [27/Feb/2008:02:19:18 -0600] "GET /ply/ HTTP/1.0" 304 - -210.81.80.193 - - [27/Feb/2008:02:29:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -77.64.8.202 - - [27/Feb/2008:02:30:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 -77.64.8.202 - - [27/Feb/2008:02:30:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -77.64.8.202 - - [27/Feb/2008:02:30:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.54.144.229 - - [27/Feb/2008:02:32:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.80.245.157 - - [27/Feb/2008:02:36:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -81.80.245.157 - - [27/Feb/2008:02:37:19 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -195.212.29.83 - - [27/Feb/2008:02:48:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.213.7.130 - - [27/Feb/2008:02:52:23 -0600] "GET /ply/ HTTP/1.0" 200 8018 -203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -202.248.73.112 - - [27/Feb/2008:02:52:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -203.213.7.130 - - [27/Feb/2008:02:54:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -74.6.24.233 - - [27/Feb/2008:02:59:02 -0600] "GET /photos/wind/index.htm HTTP/1.0" 404 133 -81.21.242.91 - - [27/Feb/2008:03:02:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 -81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.21.242.91 - - [27/Feb/2008:03:02:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.194.174.76 - - [27/Feb/2008:03:03:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -87.194.174.76 - - [27/Feb/2008:03:03:17 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -87.194.174.76 - - [27/Feb/2008:03:03:17 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -87.194.174.76 - - [27/Feb/2008:03:03:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 -80.149.16.155 - - [27/Feb/2008:03:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.155.221.86 - - [27/Feb/2008:03:14:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.149.251.212 - - [27/Feb/2008:03:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -88.149.251.212 - - [27/Feb/2008:03:16:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.149.251.212 - - [27/Feb/2008:03:16:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -88.149.251.212 - - [27/Feb/2008:03:16:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 -24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [27/Feb/2008:03:23:04 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -24.1.159.241 - - [27/Feb/2008:03:24:10 -0600] "HEAD /dynamic/07Functional.pdf HTTP/1.1" 200 0 -24.1.159.241 - - [27/Feb/2008:03:24:12 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -64.81.229.55 - - [27/Feb/2008:03:26:48 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -64.81.229.55 - - [27/Feb/2008:03:26:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.88.18.65 - - [27/Feb/2008:03:34:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -210.13.71.67 - - [27/Feb/2008:03:46:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 -210.13.71.67 - - [27/Feb/2008:03:46:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -210.13.71.67 - - [27/Feb/2008:03:46:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.13.71.67 - - [27/Feb/2008:03:46:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.13.71.67 - - [27/Feb/2008:03:46:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.13.71.67 - - [27/Feb/2008:03:46:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -81.21.242.91 - - [27/Feb/2008:03:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.13.71.67 - - [27/Feb/2008:03:48:27 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -192.54.144.229 - - [27/Feb/2008:03:48:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -192.54.144.229 - - [27/Feb/2008:03:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.24.228 - - [27/Feb/2008:03:53:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE124.HTM HTTP/1.0" 200 1355 -221.11.5.180 - - [27/Feb/2008:03:59:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 -221.11.5.180 - - [27/Feb/2008:03:59:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -69.39.3.92 - - [27/Feb/2008:04:13:23 -0600] "GET / HTTP/1.1" 206 4447 -192.54.144.229 - - [27/Feb/2008:04:16:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.104.101.239 - - [27/Feb/2008:04:16:55 -0600] "GET /robots.txt HTTP/1.0" 200 71 -124.104.101.239 - - [27/Feb/2008:04:17:59 -0600] "GET /python.html HTTP/1.0" 200 18870 -202.248.73.112 - - [27/Feb/2008:04:24:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -81.255.238.189 - - [27/Feb/2008:04:25:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.103.40.50 - - [27/Feb/2008:04:27:54 -0600] "HEAD /ply/ HTTP/1.1" 200 0 -192.44.63.162 - - [27/Feb/2008:04:28:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -192.44.63.162 - - [27/Feb/2008:04:28:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -192.44.63.162 - - [27/Feb/2008:04:28:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.210.2.219 - - [27/Feb/2008:04:30:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.210.2.219 - - [27/Feb/2008:04:30:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.165.73.3 - - [27/Feb/2008:04:44:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.103.40.50 - - [27/Feb/2008:04:47:27 -0600] "HEAD /ply/ HTTP/1.1" 200 0 -74.6.20.163 - - [27/Feb/2008:04:51:53 -0600] "GET /ply/ HTTP/1.0" 200 8018 -195.212.29.83 - - [27/Feb/2008:05:01:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.76.66.64 - - [27/Feb/2008:05:05:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -91.76.66.64 - - [27/Feb/2008:05:05:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.76.66.64 - - [27/Feb/2008:05:05:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -91.76.66.64 - - [27/Feb/2008:05:05:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -90.80.39.41 - - [27/Feb/2008:05:06:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -90.80.39.41 - - [27/Feb/2008:05:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.80.39.41 - - [27/Feb/2008:05:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.127.188.96 - - [27/Feb/2008:05:22:11 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -85.127.188.96 - - [27/Feb/2008:05:22:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -195.212.29.75 - - [27/Feb/2008:05:22:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.97.106.98 - - [27/Feb/2008:05:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -194.97.106.98 - - [27/Feb/2008:05:30:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.97.106.98 - - [27/Feb/2008:05:30:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.40.255.154 - - [27/Feb/2008:05:31:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -128.40.255.154 - - [27/Feb/2008:05:31:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.97.106.98 - - [27/Feb/2008:05:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.97.106.98 - - [27/Feb/2008:05:31:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -128.40.255.154 - - [27/Feb/2008:05:33:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.97.106.98 - - [27/Feb/2008:05:35:37 -0600] "GET /cgi-bin/wiki.pl?action=editprefs HTTP/1.1" 200 4261 -194.97.106.98 - - [27/Feb/2008:05:35:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.55.52.1 - - [27/Feb/2008:05:35:45 -0600] "GET /ply/ HTTP/1.0" 200 8018 -192.55.52.1 - - [27/Feb/2008:05:35:46 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -192.55.52.1 - - [27/Feb/2008:05:35:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -192.55.52.1 - - [27/Feb/2008:05:36:02 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -194.97.106.98 - - [27/Feb/2008:05:36:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.80.39.41 - - [27/Feb/2008:05:36:33 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -90.80.39.41 - - [27/Feb/2008:05:37:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -192.55.52.1 - - [27/Feb/2008:05:37:12 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -90.80.39.41 - - [27/Feb/2008:05:37:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -90.80.39.41 - - [27/Feb/2008:05:37:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -192.55.52.1 - - [27/Feb/2008:05:40:19 -0600] "GET /ply/README HTTP/1.0" 200 8605 -66.232.113.194 - - [27/Feb/2008:05:41:09 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2748 -201.55.193.26 - - [27/Feb/2008:05:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -161.53.125.15 - - [27/Feb/2008:05:41:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -87.101.244.6 - - [27/Feb/2008:05:41:22 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.124.252.210 - - [27/Feb/2008:05:42:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.124.252.210 - - [27/Feb/2008:05:42:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -62.43.200.236 - - [27/Feb/2008:05:44:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -62.43.200.236 - - [27/Feb/2008:05:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.252.149.15 - - [27/Feb/2008:05:45:47 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.15 - - [27/Feb/2008:05:45:49 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.1" 304 - -193.3.39.1 - - [27/Feb/2008:05:48:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 -193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.151.18.10 - - [27/Feb/2008:05:49:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 -194.151.18.10 - - [27/Feb/2008:05:49:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -194.151.18.10 - - [27/Feb/2008:05:49:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.53.125.15 - - [27/Feb/2008:05:54:13 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -161.53.125.15 - - [27/Feb/2008:05:54:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -161.53.125.15 - - [27/Feb/2008:05:54:14 -0600] "GET /cgi-bin/wiki.pl?ImportDirective HTTP/1.1" 200 2215 -193.3.39.1 - - [27/Feb/2008:05:54:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.255.238.189 - - [27/Feb/2008:06:02:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -206.51.237.114 - - [27/Feb/2008:06:06:30 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 -60.28.31.194 - - [27/Feb/2008:06:06:34 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -189.56.68.138 - - [27/Feb/2008:06:06:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -67.195.58.158 - - [27/Feb/2008:06:25:02 -0600] "GET /ply/ply.html HTTP/1.0" 304 - -222.122.236.43 - - [27/Feb/2008:06:26:38 -0600] "GET /robots.txt HTTP/1.1" 200 71 -90.185.65.84 - - [27/Feb/2008:06:26:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -90.185.65.84 - - [27/Feb/2008:06:26:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.185.65.84 - - [27/Feb/2008:06:27:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.185.65.84 - - [27/Feb/2008:06:27:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -90.185.65.84 - - [27/Feb/2008:06:27:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MzScheme HTTP/1.1" 200 2865 -220.23.136.184 - - [27/Feb/2008:06:34:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 -220.23.136.184 - - [27/Feb/2008:06:34:52 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -64.233.178.136 - - [27/Feb/2008:06:34:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 -220.23.136.184 - - [27/Feb/2008:06:35:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -84.110.148.125 - - [27/Feb/2008:06:44:30 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.148.125 - - [27/Feb/2008:06:44:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 -193.112.172.10 - - [27/Feb/2008:06:46:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -193.112.172.10 - - [27/Feb/2008:06:46:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -80.169.151.100 - - [27/Feb/2008:06:46:35 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -193.112.172.10 - - [27/Feb/2008:06:46:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.0" 200 2040 -193.112.172.10 - - [27/Feb/2008:06:47:18 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -193.112.172.10 - - [27/Feb/2008:06:47:33 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.0" 200 3248 -193.112.172.10 - - [27/Feb/2008:06:47:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 -203.213.7.133 - - [27/Feb/2008:06:48:44 -0600] "GET /ply/ HTTP/1.0" 200 8018 -203.213.7.133 - - [27/Feb/2008:06:48:45 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -203.213.7.133 - - [27/Feb/2008:06:48:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -203.213.7.133 - - [27/Feb/2008:06:48:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -80.156.46.53 - - [27/Feb/2008:06:49:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -203.213.7.133 - - [27/Feb/2008:06:51:12 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -203.213.7.133 - - [27/Feb/2008:06:51:16 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -203.213.7.133 - - [27/Feb/2008:06:53:12 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -201.236.226.90 - - [27/Feb/2008:06:56:56 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [27/Feb/2008:06:56:58 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -201.236.226.90 - - [27/Feb/2008:06:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [27/Feb/2008:06:57:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [27/Feb/2008:06:57:00 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -62.181.186.82 - - [27/Feb/2008:07:00:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 -62.181.186.82 - - [27/Feb/2008:07:00:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -118.172.30.90 - - [27/Feb/2008:07:01:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 80157 -201.24.117.154 - - [27/Feb/2008:07:01:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.24.117.154 - - [27/Feb/2008:07:01:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.24.117.154 - - [27/Feb/2008:07:01:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.24.117.154 - - [27/Feb/2008:07:02:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.24.117.154 - - [27/Feb/2008:07:02:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.24.117.154 - - [27/Feb/2008:07:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.24.117.154 - - [27/Feb/2008:07:02:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.181.186.82 - - [27/Feb/2008:07:03:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.252.247.8 - - [27/Feb/2008:07:11:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -68.252.247.8 - - [27/Feb/2008:07:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.12.88.244 - - [27/Feb/2008:07:13:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.12.88.244 - - [27/Feb/2008:07:13:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -89.12.88.244 - - [27/Feb/2008:07:13:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.13.133.202 - - [27/Feb/2008:07:14:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.13.133.202 - - [27/Feb/2008:07:14:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.13.133.202 - - [27/Feb/2008:07:14:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.13.133.202 - - [27/Feb/2008:07:15:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.54.144.229 - - [27/Feb/2008:07:15:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.13.133.202 - - [27/Feb/2008:07:15:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.13.133.202 - - [27/Feb/2008:07:18:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.34.145.201 - - [27/Feb/2008:07:20:53 -0600] "GET /robots.txt HTTP/1.0" 200 71 -64.34.145.201 - - [27/Feb/2008:07:20:53 -0600] "GET /photos/u505/pages/IMG_1490.htm HTTP/1.0" 404 133 -84.110.191.75 - - [27/Feb/2008:07:22:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.191.75 - - [27/Feb/2008:07:22:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 -68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.166.120.178 - - [27/Feb/2008:07:29:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.166.120.178 - - [27/Feb/2008:07:29:32 -0600] "GET / HTTP/1.1" 200 4447 -68.166.120.178 - - [27/Feb/2008:07:29:32 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -68.166.120.178 - - [27/Feb/2008:07:30:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 -68.166.120.178 - - [27/Feb/2008:07:30:19 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 -217.65.240.234 - - [27/Feb/2008:07:30:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -122.117.168.219 - - [27/Feb/2008:07:30:44 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -68.166.120.178 - - [27/Feb/2008:07:31:52 -0600] "GET /about.html HTTP/1.1" 200 7890 -68.166.120.178 - - [27/Feb/2008:07:31:52 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 -68.166.120.178 - - [27/Feb/2008:07:31:53 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 -68.166.120.178 - - [27/Feb/2008:07:31:54 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 -68.166.120.178 - - [27/Feb/2008:07:31:54 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 -68.166.120.178 - - [27/Feb/2008:07:34:30 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 -68.166.120.178 - - [27/Feb/2008:07:35:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -189.13.133.202 - - [27/Feb/2008:07:36:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.166.120.178 - - [27/Feb/2008:07:36:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -68.166.120.178 - - [27/Feb/2008:07:37:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -61.57.130.42 - - [27/Feb/2008:07:42:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -61.57.130.42 - - [27/Feb/2008:07:42:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -61.57.130.42 - - [27/Feb/2008:07:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.57.130.42 - - [27/Feb/2008:07:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.57.130.42 - - [27/Feb/2008:07:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.57.130.42 - - [27/Feb/2008:07:42:49 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -205.196.222.10 - - [27/Feb/2008:07:49:50 -0600] "GET /robots.txt HTTP/1.0" 200 71 -205.196.222.10 - - [27/Feb/2008:07:49:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 -212.124.252.210 - - [27/Feb/2008:07:50:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -61.57.130.42 - - [27/Feb/2008:07:53:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -217.196.43.134 - - [27/Feb/2008:08:05:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 -195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -195.71.101.182 - - [27/Feb/2008:08:06:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.54.192.10 - - [27/Feb/2008:08:09:38 -0600] "GET /ply/ HTTP/1.0" 200 8018 -193.54.192.10 - - [27/Feb/2008:08:09:40 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -193.54.192.10 - - [27/Feb/2008:08:09:40 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -193.54.192.10 - - [27/Feb/2008:08:09:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -193.54.192.10 - - [27/Feb/2008:08:09:47 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -194.186.83.193 - - [27/Feb/2008:08:12:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -194.186.83.193 - - [27/Feb/2008:08:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.207.152.130 - - [27/Feb/2008:08:20:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -58.207.152.130 - - [27/Feb/2008:08:20:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.207.152.130 - - [27/Feb/2008:08:21:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -58.207.152.130 - - [27/Feb/2008:08:21:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.207.152.130 - - [27/Feb/2008:08:21:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -58.207.152.130 - - [27/Feb/2008:08:21:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.207.152.130 - - [27/Feb/2008:08:21:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -58.207.152.130 - - [27/Feb/2008:08:21:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.177.245 - - [27/Feb/2008:08:22:09 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.177.245 - - [27/Feb/2008:08:22:10 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1098 -202.163.114.53 - - [27/Feb/2008:08:23:09 -0600] "GET /cv.html HTTP/1.1" 200 31798 -189.31.124.147 - - [27/Feb/2008:08:27:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -192.88.162.35 - - [27/Feb/2008:08:27:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -189.31.124.147 - - [27/Feb/2008:08:27:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.31.124.147 - - [27/Feb/2008:08:27:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -144.204.65.6 - - [27/Feb/2008:08:29:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 -144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -189.31.124.147 - - [27/Feb/2008:08:29:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.14.110.6 - - [27/Feb/2008:08:29:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.14.110.6 - - [27/Feb/2008:08:29:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.14.110.6 - - [27/Feb/2008:08:29:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.14.110.6 - - [27/Feb/2008:08:29:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.14.110.6 - - [27/Feb/2008:08:29:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.24.117.154 - - [27/Feb/2008:08:31:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.14.110.6 - - [27/Feb/2008:08:34:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.127.117.160 - - [27/Feb/2008:08:34:55 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -62.255.240.194 - - [27/Feb/2008:08:36:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -62.255.240.194 - - [27/Feb/2008:08:36:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.255.240.194 - - [27/Feb/2008:08:36:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -62.255.240.194 - - [27/Feb/2008:08:36:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -62.255.240.194 - - [27/Feb/2008:08:36:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -60.176.145.165 - - [27/Feb/2008:08:37:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.255.240.194 - - [27/Feb/2008:08:37:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.40.255.154 - - [27/Feb/2008:08:41:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.186.27.193 - - [27/Feb/2008:08:45:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 -91.186.27.193 - - [27/Feb/2008:08:45:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -76.223.13.234 - - [27/Feb/2008:08:46:45 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -76.223.13.234 - - [27/Feb/2008:08:48:13 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -74.6.25.142 - - [27/Feb/2008:08:48:52 -0600] "GET /python/tutorial/ HTTP/1.0" 403 214 -128.135.212.179 - - [27/Feb/2008:08:49:23 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.135.212.179 - - [27/Feb/2008:08:51:16 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -138.246.7.155 - - [27/Feb/2008:08:58:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 -138.246.7.155 - - [27/Feb/2008:08:58:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -138.246.7.155 - - [27/Feb/2008:08:58:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.121.236.201 - - [27/Feb/2008:08:58:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 -86.121.236.201 - - [27/Feb/2008:08:58:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -86.121.236.201 - - [27/Feb/2008:08:58:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -138.246.7.155 - - [27/Feb/2008:08:59:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -98.206.164.173 - - [27/Feb/2008:09:04:16 -0600] "GET /dynamic/ HTTP/1.1" 304 - -89.165.73.133 - - [27/Feb/2008:09:04:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.59.141.172 - - [27/Feb/2008:09:05:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.59.141.172 - - [27/Feb/2008:09:05:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -124.161.65.194 - - [27/Feb/2008:09:09:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.124.13.183 - - [27/Feb/2008:09:13:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 -85.124.13.183 - - [27/Feb/2008:09:13:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -85.124.13.183 - - [27/Feb/2008:09:13:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.43.32.87 - - [27/Feb/2008:09:14:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -89.165.73.133 - - [27/Feb/2008:09:16:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.133 - - [27/Feb/2008:09:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.187.74 - - [27/Feb/2008:09:27:57 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.187.74 - - [27/Feb/2008:09:27:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1391 -150.210.155.167 - - [27/Feb/2008:09:29:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -68.236.90.186 - - [27/Feb/2008:09:30:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -68.236.90.186 - - [27/Feb/2008:09:30:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -68.236.90.186 - - [27/Feb/2008:09:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -74.6.20.147 - - [27/Feb/2008:09:34:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE083.HTM HTTP/1.0" 200 1418 -192.54.144.229 - - [27/Feb/2008:09:35:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.245.138.101 - - [27/Feb/2008:09:37:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 -70.245.138.101 - - [27/Feb/2008:09:37:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -70.245.138.101 - - [27/Feb/2008:09:37:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.74.100.50 - - [27/Feb/2008:09:38:15 -0600] "GET /ply/ HTTP/1.0" 200 8018 -87.122.102.16 - - [27/Feb/2008:09:40:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 -87.122.102.16 - - [27/Feb/2008:09:40:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -87.122.102.16 - - [27/Feb/2008:09:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.122.102.16 - - [27/Feb/2008:09:40:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.122.102.16 - - [27/Feb/2008:09:40:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 -87.122.102.16 - - [27/Feb/2008:09:40:44 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -87.122.102.16 - - [27/Feb/2008:09:40:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 19833 -87.122.102.16 - - [27/Feb/2008:09:40:55 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -87.122.102.16 - - [27/Feb/2008:09:41:01 -0600] "GET /ply/ply.html HTTP/1.1" 206 90365 -124.161.65.194 - - [27/Feb/2008:09:42:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.26.19 - - [27/Feb/2008:09:43:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 -124.161.65.194 - - [27/Feb/2008:09:43:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -155.140.133.62 - - [27/Feb/2008:09:43:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -75.24.211.116 - - [27/Feb/2008:09:43:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -75.24.211.116 - - [27/Feb/2008:09:43:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.24.211.116 - - [27/Feb/2008:09:43:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -155.140.133.62 - - [27/Feb/2008:09:43:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 -134.157.248.214 - - [27/Feb/2008:09:55:06 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -134.157.248.214 - - [27/Feb/2008:09:55:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.63.137.190 - - [27/Feb/2008:10:01:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -212.63.137.190 - - [27/Feb/2008:10:01:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.63.137.190 - - [27/Feb/2008:10:01:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 923 -212.63.137.190 - - [27/Feb/2008:10:01:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1009 -212.63.137.190 - - [27/Feb/2008:10:01:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -141.115.28.2 - - [27/Feb/2008:10:02:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 -141.115.28.2 - - [27/Feb/2008:10:02:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -141.115.28.2 - - [27/Feb/2008:10:02:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -62.219.135.118 - - [27/Feb/2008:10:03:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.219.135.118 - - [27/Feb/2008:10:04:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.174 - - [27/Feb/2008:10:07:34 -0600] "GET /ply/ HTTP/1.0" 304 - -133.9.245.73 - - [27/Feb/2008:10:07:38 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -67.151.52.19 - - [27/Feb/2008:10:16:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 -67.151.52.19 - - [27/Feb/2008:10:16:07 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -209.159.33.99 - - [27/Feb/2008:10:21:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 -209.159.33.99 - - [27/Feb/2008:10:21:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -157.157.91.18 - - [27/Feb/2008:10:25:22 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -66.249.65.37 - - [27/Feb/2008:10:29:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.196.80.231 - - [27/Feb/2008:10:29:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.196.80.231 - - [27/Feb/2008:10:29:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -69.218.90.173 - - [27/Feb/2008:10:31:55 -0600] "GET /ply/README HTTP/1.1" 200 8605 -69.218.90.173 - - [27/Feb/2008:10:31:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.218.90.173 - - [27/Feb/2008:10:32:50 -0600] "GET /ply HTTP/1.1" 301 242 -69.218.90.173 - - [27/Feb/2008:10:32:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.218.90.173 - - [27/Feb/2008:10:32:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -69.218.90.173 - - [27/Feb/2008:10:33:26 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -204.130.247.244 - - [27/Feb/2008:10:37:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -204.130.247.244 - - [27/Feb/2008:10:37:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -24.7.210.64 - - [27/Feb/2008:10:40:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.7.210.64 - - [27/Feb/2008:10:40:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -205.209.249.3 - - [27/Feb/2008:10:41:05 -0600] "GET /python.html HTTP/1.1" 200 18870 -205.209.249.3 - - [27/Feb/2008:10:41:06 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -41.219.193.49 - - [27/Feb/2008:10:44:25 -0600] "GET / HTTP/1.1" 200 4447 -141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 -141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -141.142.240.56 - - [27/Feb/2008:10:50:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.234.244.162 - - [27/Feb/2008:10:50:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -64.234.244.162 - - [27/Feb/2008:10:50:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.234.244.162 - - [27/Feb/2008:10:50:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -141.142.240.56 - - [27/Feb/2008:10:50:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -141.142.240.56 - - [27/Feb/2008:10:52:38 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -68.216.13.202 - - [27/Feb/2008:10:53:04 -0600] "GET /ply HTTP/1.1" 301 242 -68.216.13.202 - - [27/Feb/2008:10:53:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -68.216.13.202 - - [27/Feb/2008:10:53:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -68.216.13.202 - - [27/Feb/2008:10:53:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.216.13.202 - - [27/Feb/2008:10:53:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -141.142.240.56 - - [27/Feb/2008:10:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -198.175.55.5 - - [27/Feb/2008:10:54:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -198.175.55.5 - - [27/Feb/2008:10:54:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -198.175.55.5 - - [27/Feb/2008:10:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.9.243.111 - - [27/Feb/2008:10:56:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -216.9.243.111 - - [27/Feb/2008:10:56:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -198.175.55.5 - - [27/Feb/2008:10:56:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.208.118 - - [27/Feb/2008:10:57:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.118 - - [27/Feb/2008:10:57:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.1" 304 - -204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 -204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.53.65.44 - - [27/Feb/2008:11:00:56 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 -84.110.205.221 - - [27/Feb/2008:11:02:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.205.221 - - [27/Feb/2008:11:02:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 -89.165.73.133 - - [27/Feb/2008:11:04:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.133 - - [27/Feb/2008:11:04:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.59.123.114 - - [27/Feb/2008:11:06:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -86.59.123.114 - - [27/Feb/2008:11:07:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.59.123.114 - - [27/Feb/2008:11:07:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -89.165.73.133 - - [27/Feb/2008:11:09:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.133 - - [27/Feb/2008:11:09:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.133 - - [27/Feb/2008:11:09:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.214.175.76 - - [27/Feb/2008:11:09:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.196.80.231 - - [27/Feb/2008:11:11:46 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -80.221.26.30 - - [27/Feb/2008:11:16:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.221.26.30 - - [27/Feb/2008:11:16:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -80.221.26.30 - - [27/Feb/2008:11:16:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [27/Feb/2008:11:18:45 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -201.236.226.90 - - [27/Feb/2008:11:19:12 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -128.196.80.231 - - [27/Feb/2008:11:22:00 -0600] "GET /ply/README HTTP/1.1" 200 8605 -200.55.140.181 - - [27/Feb/2008:11:27:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -200.55.140.181 - - [27/Feb/2008:11:27:23 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -74.6.28.45 - - [27/Feb/2008:11:33:12 -0600] "GET /dynamic/smackdown.py HTTP/1.0" 200 1981 -128.196.80.231 - - [27/Feb/2008:11:33:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -83.24.38.176 - - [27/Feb/2008:11:37:57 -0600] "GET /ply/ HTTP/1.1" 304 - -83.24.38.176 - - [27/Feb/2008:11:38:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -83.24.38.176 - - [27/Feb/2008:11:43:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.24.38.176 - - [27/Feb/2008:11:43:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -83.24.38.176 - - [27/Feb/2008:11:43:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.24.38.176 - - [27/Feb/2008:11:43:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -86.141.0.114 - - [27/Feb/2008:11:51:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 -86.141.0.114 - - [27/Feb/2008:11:51:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -86.141.0.114 - - [27/Feb/2008:11:51:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [27/Feb/2008:11:51:56 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -24.1.159.241 - - [27/Feb/2008:11:51:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [27/Feb/2008:11:51:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [27/Feb/2008:11:52:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -24.1.159.241 - - [27/Feb/2008:11:57:55 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -87.221.119.235 - - [27/Feb/2008:11:58:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -87.221.119.235 - - [27/Feb/2008:11:58:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.221.119.235 - - [27/Feb/2008:11:58:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -190.199.163.61 - - [27/Feb/2008:12:06:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -190.199.163.61 - - [27/Feb/2008:12:06:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -190.199.163.61 - - [27/Feb/2008:12:06:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -80.191.172.9 - - [27/Feb/2008:12:13:59 -0600] "GET /python.html HTTP/1.1" 200 18870 -80.191.172.9 - - [27/Feb/2008:12:13:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.191.172.9 - - [27/Feb/2008:12:14:00 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -204.246.129.196 - - [27/Feb/2008:12:19:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.221.197.20 - - [27/Feb/2008:12:19:27 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -74.6.25.20 - - [27/Feb/2008:12:21:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.25.22 - - [27/Feb/2008:12:21:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1403 -74.6.26.235 - - [27/Feb/2008:12:21:48 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE046.HTM HTTP/1.0" 200 1582 -128.196.205.39 - - [27/Feb/2008:12:33:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.196.205.39 - - [27/Feb/2008:12:33:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -65.55.208.118 - - [27/Feb/2008:12:35:24 -0600] "GET /photos/wind/pages/IMG_1253.htm HTTP/1.1" 404 133 -128.196.205.39 - - [27/Feb/2008:12:36:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -209.17.146.129 - - [27/Feb/2008:12:44:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.228.97.106 - - [27/Feb/2008:12:47:46 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.228.97.106 - - [27/Feb/2008:12:47:46 -0600] "HEAD /ply/ HTTP/1.0" 200 0 -84.110.209.164 - - [27/Feb/2008:12:47:47 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.209.164 - - [27/Feb/2008:12:47:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1032 -67.228.97.106 - - [27/Feb/2008:12:48:23 -0600] "HEAD /ply/example.html HTTP/1.0" 304 - -76.185.24.146 - - [27/Feb/2008:12:49:54 -0600] "GET /python.html HTTP/1.1" 200 18870 -76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.185.24.146 - - [27/Feb/2008:12:52:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.88.178 - - [27/Feb/2008:12:52:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -76.185.24.146 - - [27/Feb/2008:12:55:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.162.120.220 - - [27/Feb/2008:12:56:06 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 -76.185.24.146 - - [27/Feb/2008:12:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.23.41 - - [27/Feb/2008:12:59:13 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE008.HTM HTTP/1.0" 200 1336 -76.185.24.146 - - [27/Feb/2008:12:59:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.88.178 - - [27/Feb/2008:13:01:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -134.173.88.178 - - [27/Feb/2008:13:01:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.88.178 - - [27/Feb/2008:13:01:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.88.178 - - [27/Feb/2008:13:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.88.178 - - [27/Feb/2008:13:01:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.186.48.152 - - [27/Feb/2008:13:02:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.186.48.152 - - [27/Feb/2008:13:02:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -24.186.48.152 - - [27/Feb/2008:13:02:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.32.37.138 - - [27/Feb/2008:13:02:59 -0600] "GET /dynamic/ HTTP/1.1" 304 - -66.87.72.51 - - [27/Feb/2008:13:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.87.72.51 - - [27/Feb/2008:13:03:18 -0600] "GET /cv.html HTTP/1.1" 200 31798 -76.185.24.146 - - [27/Feb/2008:13:11:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.5.113.131 - - [27/Feb/2008:13:13:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -71.5.113.131 - - [27/Feb/2008:13:13:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.5.113.131 - - [27/Feb/2008:13:13:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.5.113.131 - - [27/Feb/2008:13:13:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -71.5.113.131 - - [27/Feb/2008:13:13:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -74.6.26.145 - - [27/Feb/2008:13:14:05 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE071.HTM HTTP/1.0" 200 1322 -134.173.88.178 - - [27/Feb/2008:13:16:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -140.160.138.141 - - [27/Feb/2008:13:17:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -140.160.138.141 - - [27/Feb/2008:13:17:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -144.51.43.161 - - [27/Feb/2008:13:23:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -144.51.43.161 - - [27/Feb/2008:13:23:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -144.51.43.161 - - [27/Feb/2008:13:23:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -144.51.43.161 - - [27/Feb/2008:13:23:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -144.51.43.161 - - [27/Feb/2008:13:24:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -144.51.43.161 - - [27/Feb/2008:13:24:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -144.51.43.161 - - [27/Feb/2008:13:25:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 -195.214.232.10 - - [27/Feb/2008:13:32:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -195.214.232.10 - - [27/Feb/2008:13:32:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.48.8.237 - - [27/Feb/2008:13:32:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -151.48.8.237 - - [27/Feb/2008:13:32:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.48.8.237 - - [27/Feb/2008:13:32:59 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -199.111.205.190 - - [27/Feb/2008:13:38:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.190 - - [27/Feb/2008:13:38:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.190 - - [27/Feb/2008:13:38:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.190 - - [27/Feb/2008:13:38:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.198.239.27 - - [27/Feb/2008:13:40:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -81.198.239.27 - - [27/Feb/2008:13:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.208.123 - - [27/Feb/2008:13:41:55 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 -88.191.19.81 - - [27/Feb/2008:13:42:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -81.198.239.27 - - [27/Feb/2008:13:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.179.69 - - [27/Feb/2008:13:44:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -134.173.88.178 - - [27/Feb/2008:13:45:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.191.255.98 - - [27/Feb/2008:13:47:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -68.191.255.98 - - [27/Feb/2008:13:47:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.191.255.98 - - [27/Feb/2008:13:47:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -68.191.255.98 - - [27/Feb/2008:13:47:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -68.191.255.98 - - [27/Feb/2008:13:47:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -68.191.255.98 - - [27/Feb/2008:13:47:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -76.171.199.78 - - [27/Feb/2008:13:48:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -76.171.199.78 - - [27/Feb/2008:13:48:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -76.171.199.78 - - [27/Feb/2008:13:49:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -12.107.176.254 - - [27/Feb/2008:13:50:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [27/Feb/2008:13:52:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.218.75.11 - - [27/Feb/2008:13:59:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -70.218.75.11 - - [27/Feb/2008:13:59:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -67.195.58.158 - - [27/Feb/2008:14:00:43 -0600] "GET /ply/ply.html HTTP/1.0" 304 - -64.234.244.162 - - [27/Feb/2008:14:01:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.171.199.78 - - [27/Feb/2008:14:02:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -128.135.125.239 - - [27/Feb/2008:14:03:45 -0600] "GET / HTTP/1.1" 200 4447 -128.135.125.239 - - [27/Feb/2008:14:03:45 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -128.135.125.239 - - [27/Feb/2008:14:04:49 -0600] "GET /python.html HTTP/1.1" 200 18870 -128.135.125.239 - - [27/Feb/2008:14:04:49 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -84.110.138.116 - - [27/Feb/2008:14:08:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.138.116 - - [27/Feb/2008:14:08:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 972 -171.66.35.216 - - [27/Feb/2008:14:12:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -171.66.35.216 - - [27/Feb/2008:14:12:37 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 -24.15.187.198 - - [27/Feb/2008:14:13:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -128.143.117.220 - - [27/Feb/2008:14:14:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.117.220 - - [27/Feb/2008:14:14:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.220 - - [27/Feb/2008:14:14:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.117.220 - - [27/Feb/2008:14:14:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.55.200.20 - - [27/Feb/2008:14:15:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -129.55.200.20 - - [27/Feb/2008:14:15:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -129.55.200.20 - - [27/Feb/2008:14:16:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 -128.143.117.220 - - [27/Feb/2008:14:16:41 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -128.143.117.220 - - [27/Feb/2008:14:16:42 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 145565 -86.206.17.195 - - [27/Feb/2008:14:19:25 -0600] "GET / HTTP/1.1" 200 4447 -86.206.17.195 - - [27/Feb/2008:14:19:26 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -86.206.17.195 - - [27/Feb/2008:14:19:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.206.17.195 - - [27/Feb/2008:14:19:32 -0600] "GET /writing.html HTTP/1.1" 200 2871 -86.206.17.195 - - [27/Feb/2008:14:19:33 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 -199.111.200.69 - - [27/Feb/2008:14:26:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.195.44.110 - - [27/Feb/2008:14:26:06 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.195.44.109 - - [27/Feb/2008:14:26:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 -74.6.31.151 - - [27/Feb/2008:14:28:16 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -24.15.187.198 - - [27/Feb/2008:14:30:33 -0600] "GET /dynamic/ HTTP/1.1" 304 - -24.15.187.198 - - [27/Feb/2008:14:30:45 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -24.14.206.105 - - [27/Feb/2008:14:36:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -24.14.206.105 - - [27/Feb/2008:14:36:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.14.206.105 - - [27/Feb/2008:14:36:41 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -88.165.126.62 - - [27/Feb/2008:14:39:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 -88.165.126.62 - - [27/Feb/2008:14:39:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.165.126.62 - - [27/Feb/2008:14:39:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.6.151.80 - - [27/Feb/2008:14:40:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 -24.2.76.175 - - [27/Feb/2008:14:45:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.2.76.175 - - [27/Feb/2008:14:45:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -155.91.28.232 - - [27/Feb/2008:14:47:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 -155.91.28.232 - - [27/Feb/2008:14:47:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -24.15.187.198 - - [27/Feb/2008:14:54:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -84.134.8.241 - - [27/Feb/2008:14:56:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -84.134.8.241 - - [27/Feb/2008:14:56:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -84.134.8.241 - - [27/Feb/2008:14:56:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.134.8.241 - - [27/Feb/2008:14:56:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.134.8.241 - - [27/Feb/2008:14:56:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.134.8.241 - - [27/Feb/2008:14:57:25 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -24.15.187.198 - - [27/Feb/2008:15:01:44 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -220.239.245.127 - - [27/Feb/2008:15:01:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -220.239.245.127 - - [27/Feb/2008:15:01:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -220.239.245.127 - - [27/Feb/2008:15:01:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.196.43.134 - - [27/Feb/2008:15:05:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.15.187.198 - - [27/Feb/2008:15:06:22 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -88.67.224.55 - - [27/Feb/2008:15:10:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -88.67.224.55 - - [27/Feb/2008:15:10:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.67.224.55 - - [27/Feb/2008:15:11:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 -88.67.224.55 - - [27/Feb/2008:15:13:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -65.55.208.124 - - [27/Feb/2008:15:15:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [27/Feb/2008:15:15:56 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [27/Feb/2008:15:15:56 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:15:15:57 -0600] "GET / HTTP/1.1" 200 4447 -64.0.160.210 - - [27/Feb/2008:15:17:33 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -64.0.160.210 - - [27/Feb/2008:15:17:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.0.160.210 - - [27/Feb/2008:15:18:06 -0600] "GET /ply HTTP/1.1" 301 242 -64.0.160.210 - - [27/Feb/2008:15:18:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -64.0.160.210 - - [27/Feb/2008:15:18:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -64.0.160.210 - - [27/Feb/2008:15:18:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -200.11.208.122 - - [27/Feb/2008:15:20:01 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -200.11.208.122 - - [27/Feb/2008:15:20:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -77.91.224.13 - - [27/Feb/2008:15:22:32 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -77.91.224.3 - - [27/Feb/2008:15:22:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 -77.91.224.3 - - [27/Feb/2008:15:22:58 -0600] "GET /swill/ HTTP/1.1" 200 3786 -77.91.224.13 - - [27/Feb/2008:15:24:51 -0600] "GET /publications.html HTTP/1.1" 200 7758 -89.168.18.6 - - [27/Feb/2008:15:26:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -89.168.18.6 - - [27/Feb/2008:15:26:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.168.18.6 - - [27/Feb/2008:15:26:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.168.18.6 - - [27/Feb/2008:15:26:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -77.91.224.3 - - [27/Feb/2008:15:26:13 -0600] "GET /cv.html HTTP/1.1" 200 31798 -89.168.18.6 - - [27/Feb/2008:15:26:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -89.168.18.6 - - [27/Feb/2008:15:27:50 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -89.168.18.6 - - [27/Feb/2008:15:28:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -77.91.224.13 - - [27/Feb/2008:15:28:03 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -89.168.18.6 - - [27/Feb/2008:15:28:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -89.168.18.6 - - [27/Feb/2008:15:28:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -89.168.18.6 - - [27/Feb/2008:15:28:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 -77.91.224.3 - - [27/Feb/2008:15:28:35 -0600] "GET /diet.html HTTP/1.1" 404 133 -89.168.18.6 - - [27/Feb/2008:15:28:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -77.91.224.3 - - [27/Feb/2008:15:29:27 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -89.168.18.6 - - [27/Feb/2008:15:29:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -89.168.18.6 - - [27/Feb/2008:15:29:50 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -89.168.18.6 - - [27/Feb/2008:15:30:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -89.168.18.6 - - [27/Feb/2008:15:30:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -89.168.18.6 - - [27/Feb/2008:15:30:35 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 -89.168.18.6 - - [27/Feb/2008:15:30:39 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 -77.91.224.13 - - [27/Feb/2008:15:31:09 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -77.91.224.3 - - [27/Feb/2008:15:31:45 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -77.91.224.3 - - [27/Feb/2008:15:32:37 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -77.91.224.13 - - [27/Feb/2008:15:34:18 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -77.91.224.3 - - [27/Feb/2008:15:34:58 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -66.249.65.37 - - [27/Feb/2008:15:36:30 -0600] "GET / HTTP/1.1" 304 - -77.91.224.13 - - [27/Feb/2008:15:36:41 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -77.91.224.3 - - [27/Feb/2008:15:38:12 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -66.249.65.37 - - [27/Feb/2008:15:39:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 -66.249.65.37 - - [27/Feb/2008:15:39:26 -0600] "GET / HTTP/1.1" 200 4447 -66.249.65.37 - - [27/Feb/2008:15:42:08 -0600] "GET /python.html HTTP/1.1" 304 - -66.249.65.37 - - [27/Feb/2008:15:43:17 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -209.198.142.114 - - [27/Feb/2008:15:44:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -209.198.142.114 - - [27/Feb/2008:15:44:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -67.202.49.172 - - [27/Feb/2008:15:46:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 -67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /publications.html HTTP/1.1" 200 7758 -67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -209.198.142.114 - - [27/Feb/2008:15:48:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -209.198.142.114 - - [27/Feb/2008:15:48:30 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -66.249.65.37 - - [27/Feb/2008:15:48:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -209.198.142.114 - - [27/Feb/2008:15:48:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -209.198.142.114 - - [27/Feb/2008:15:48:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -209.198.142.114 - - [27/Feb/2008:15:49:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -209.198.142.114 - - [27/Feb/2008:15:49:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -209.198.142.114 - - [27/Feb/2008:15:50:08 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -209.198.142.114 - - [27/Feb/2008:15:50:18 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 -80.229.70.194 - - [27/Feb/2008:15:51:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -80.229.70.194 - - [27/Feb/2008:15:51:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.229.70.194 - - [27/Feb/2008:15:51:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.229.70.194 - - [27/Feb/2008:15:51:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.191.19.81 - - [27/Feb/2008:15:51:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /diet.html HTTP/1.1" 404 133 -67.202.49.172 - - [27/Feb/2008:15:52:14 -0600] "GET /cv.html HTTP/1.1" 200 31798 -65.55.208.117 - - [27/Feb/2008:15:52:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.117 - - [27/Feb/2008:15:52:26 -0600] "GET /photos/wind/ThumbnailFrame.htm HTTP/1.1" 404 133 -66.249.65.37 - - [27/Feb/2008:15:54:20 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -213.67.242.79 - - [27/Feb/2008:15:57:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 -213.67.242.79 - - [27/Feb/2008:15:57:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -213.67.242.79 - - [27/Feb/2008:15:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.25.221 - - [27/Feb/2008:15:58:32 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -87.194.206.16 - - [27/Feb/2008:15:58:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 -87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.19.115 - - [27/Feb/2008:15:59:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.31.170 - - [27/Feb/2008:15:59:11 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.170 - - [27/Feb/2008:15:59:11 -0600] "GET /ply/ HTTP/1.0" 200 8018 -66.249.65.37 - - [27/Feb/2008:15:59:22 -0600] "GET /ply/README HTTP/1.1" 200 8605 -87.194.206.16 - - [27/Feb/2008:15:59:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.252.149.15 - - [27/Feb/2008:16:01:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.15 - - [27/Feb/2008:16:01:43 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 304 - -74.6.31.165 - - [27/Feb/2008:16:02:13 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.165 - - [27/Feb/2008:16:02:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 -87.194.206.16 - - [27/Feb/2008:16:03:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.92.109.170 - - [27/Feb/2008:16:04:20 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -84.92.109.170 - - [27/Feb/2008:16:04:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.92.109.170 - - [27/Feb/2008:16:04:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -84.92.109.170 - - [27/Feb/2008:16:04:36 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -84.92.109.170 - - [27/Feb/2008:16:04:47 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 -66.249.65.37 - - [27/Feb/2008:16:05:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -199.111.204.154 - - [27/Feb/2008:16:11:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.204.154 - - [27/Feb/2008:16:11:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.204.154 - - [27/Feb/2008:16:11:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.204.154 - - [27/Feb/2008:16:11:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -216.160.75.249 - - [27/Feb/2008:16:12:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -216.160.75.249 - - [27/Feb/2008:16:12:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.160.75.249 - - [27/Feb/2008:16:12:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -216.160.75.249 - - [27/Feb/2008:16:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -216.160.75.249 - - [27/Feb/2008:16:12:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -216.160.75.249 - - [27/Feb/2008:16:13:10 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -216.160.75.249 - - [27/Feb/2008:16:13:13 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -71.201.41.248 - - [27/Feb/2008:16:16:47 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -71.201.41.248 - - [27/Feb/2008:16:16:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.41.248 - - [27/Feb/2008:16:16:59 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.135.125.239 - - [27/Feb/2008:16:17:53 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -24.1.159.241 - - [27/Feb/2008:16:23:55 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -24.1.159.241 - - [27/Feb/2008:16:23:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [27/Feb/2008:16:23:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [27/Feb/2008:16:24:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -89.168.18.6 - - [27/Feb/2008:16:24:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -89.168.18.6 - - [27/Feb/2008:16:24:50 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 -89.168.18.6 - - [27/Feb/2008:16:25:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -89.168.18.6 - - [27/Feb/2008:16:25:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 -89.168.18.6 - - [27/Feb/2008:16:25:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 -65.55.212.77 - - [27/Feb/2008:16:25:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 -84.75.247.28 - - [27/Feb/2008:16:27:33 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 15592 -84.75.247.28 - - [27/Feb/2008:16:27:44 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 3110734 -99.226.226.178 - - [27/Feb/2008:16:31:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [27/Feb/2008:16:31:56 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 304 - -74.6.26.159 - - [27/Feb/2008:16:32:04 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE061.HTM HTTP/1.0" 304 - -128.221.197.20 - - [27/Feb/2008:16:34:07 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -87.205.225.143 - - [27/Feb/2008:16:42:46 -0600] "GET /ply/ HTTP/1.0" 304 - -24.99.94.177 - - [27/Feb/2008:16:43:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.99.94.177 - - [27/Feb/2008:16:43:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -24.99.94.177 - - [27/Feb/2008:16:43:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -221.194.136.18 - - [27/Feb/2008:16:45:40 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.248.3 - - [27/Feb/2008:16:46:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.173.185.186 - - [27/Feb/2008:16:47:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -66.232.113.194 - - [27/Feb/2008:16:49:35 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -202.106.212.226 - - [27/Feb/2008:16:49:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -66.249.65.37 - - [27/Feb/2008:16:49:54 -0600] "GET /index.html HTTP/1.1" 304 - -74.6.7.110 - - [27/Feb/2008:16:58:40 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE025.HTM HTTP/1.0" 200 1570 -199.111.224.90 - - [27/Feb/2008:16:58:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.19.201 - - [27/Feb/2008:17:00:39 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.19.201 - - [27/Feb/2008:17:00:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 -134.173.59.157 - - [27/Feb/2008:17:02:22 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -134.173.59.157 - - [27/Feb/2008:17:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.59.157 - - [27/Feb/2008:17:02:34 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1036 -134.173.59.157 - - [27/Feb/2008:17:02:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -134.173.59.157 - - [27/Feb/2008:17:11:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -134.173.59.157 - - [27/Feb/2008:17:11:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -134.173.59.157 - - [27/Feb/2008:17:11:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.141 - - [27/Feb/2008:17:12:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.141 - - [27/Feb/2008:17:12:15 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -24.10.16.193 - - [27/Feb/2008:17:17:30 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.10.16.193 - - [27/Feb/2008:17:17:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:17:17:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.141 - - [27/Feb/2008:17:18:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:17:19:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [27/Feb/2008:17:20:58 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 -206.51.237.114 - - [27/Feb/2008:17:22:30 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 -203.113.115.20 - - [27/Feb/2008:17:22:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -80.97.94.178 - - [27/Feb/2008:17:22:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -213.67.242.79 - - [27/Feb/2008:17:23:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -134.173.59.157 - - [27/Feb/2008:17:25:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -210.9.32.205 - - [27/Feb/2008:17:25:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 -210.9.32.205 - - [27/Feb/2008:17:25:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -210.9.32.205 - - [27/Feb/2008:17:25:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.9.32.205 - - [27/Feb/2008:17:26:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.38.141 - - [27/Feb/2008:17:27:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -134.173.59.157 - - [27/Feb/2008:17:28:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1858 -134.173.59.157 - - [27/Feb/2008:17:28:35 -0600] "GET /cgi-bin/wiki.pl?InstallationProblems HTTP/1.1" 200 4209 -24.10.16.193 - - [27/Feb/2008:17:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.59.157 - - [27/Feb/2008:17:29:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 971 -134.173.59.157 - - [27/Feb/2008:17:29:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 -199.111.224.90 - - [27/Feb/2008:17:30:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.141 - - [27/Feb/2008:17:31:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:17:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.160.246.247 - - [27/Feb/2008:17:34:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -76.160.246.247 - - [27/Feb/2008:17:34:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:17:35:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.176.194 - - [27/Feb/2008:17:40:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.201.176.194 - - [27/Feb/2008:17:40:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.176.194 - - [27/Feb/2008:17:40:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.67.242.79 - - [27/Feb/2008:17:42:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -213.67.242.79 - - [27/Feb/2008:17:42:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -148.87.1.171 - - [27/Feb/2008:17:44:21 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -148.87.1.171 - - [27/Feb/2008:17:44:25 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -212.90.208.194 - - [27/Feb/2008:17:44:33 -0600] "GET /cv.html HTTP/1.1" 200 31798 -24.10.16.193 - - [27/Feb/2008:17:47:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:17:47:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.224.135.187 - - [27/Feb/2008:17:53:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -89.224.135.187 - - [27/Feb/2008:17:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.224.135.187 - - [27/Feb/2008:17:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.224.135.187 - - [27/Feb/2008:17:53:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.224.135.187 - - [27/Feb/2008:17:53:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -89.224.135.187 - - [27/Feb/2008:17:53:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -81.103.63.40 - - [27/Feb/2008:17:54:03 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 -68.42.70.206 - - [27/Feb/2008:17:56:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 -68.42.70.206 - - [27/Feb/2008:17:56:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -68.42.70.206 - - [27/Feb/2008:17:56:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [27/Feb/2008:18:00:28 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -99.140.232.220 - - [27/Feb/2008:18:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.140.232.220 - - [27/Feb/2008:18:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.157.119.197 - - [27/Feb/2008:18:01:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -140.180.132.213 - - [27/Feb/2008:18:09:03 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -140.180.132.213 - - [27/Feb/2008:18:09:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -140.180.132.213 - - [27/Feb/2008:18:09:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.146.214.212 - - [27/Feb/2008:18:13:05 -0600] "GET /dynamic/ HTTP/1.1" 304 - -66.146.214.212 - - [27/Feb/2008:18:13:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -86.157.119.197 - - [27/Feb/2008:18:16:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.26.223 - - [27/Feb/2008:18:17:08 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1858 -67.195.58.174 - - [27/Feb/2008:18:23:38 -0600] "GET /ply/ HTTP/1.0" 304 - -140.180.132.213 - - [27/Feb/2008:18:24:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.69.160.150 - - [27/Feb/2008:18:25:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -24.69.160.150 - - [27/Feb/2008:18:25:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.69.160.150 - - [27/Feb/2008:18:25:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -24.69.160.150 - - [27/Feb/2008:18:26:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -24.69.160.150 - - [27/Feb/2008:18:27:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -24.69.160.150 - - [27/Feb/2008:18:27:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMissingHeaderFiles HTTP/1.1" 200 3193 -24.69.160.150 - - [27/Feb/2008:18:27:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -24.69.160.150 - - [27/Feb/2008:18:27:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDevelopmentMachines HTTP/1.1" 200 1781 -24.69.160.150 - - [27/Feb/2008:18:27:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -24.69.160.150 - - [27/Feb/2008:18:27:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 -24.69.160.150 - - [27/Feb/2008:18:28:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -24.69.160.150 - - [27/Feb/2008:18:28:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -24.69.160.150 - - [27/Feb/2008:18:28:17 -0600] "GET /cgi-bin/wiki.pl?DefineDirective HTTP/1.1" 200 2760 -24.69.160.150 - - [27/Feb/2008:18:28:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 -24.69.160.150 - - [27/Feb/2008:18:28:37 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 -24.69.160.150 - - [27/Feb/2008:18:28:42 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4769 -24.69.160.150 - - [27/Feb/2008:18:28:45 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 -24.69.160.150 - - [27/Feb/2008:18:29:08 -0600] "GET /cgi-bin/wiki.pl?PrettyXmlSwig HTTP/1.1" 200 1495 -24.69.160.150 - - [27/Feb/2008:18:29:24 -0600] "GET /cgi-bin/wiki.pl?Unit_Tests_With_SWIG HTTP/1.1" 200 2103 -66.146.214.212 - - [27/Feb/2008:18:31:34 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -74.6.26.203 - - [27/Feb/2008:18:33:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE098.HTM HTTP/1.0" 200 1328 -67.195.58.158 - - [27/Feb/2008:18:35:27 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - -67.195.58.158 - - [27/Feb/2008:18:35:28 -0600] "GET /ply/README HTTP/1.0" 304 - -199.111.224.90 - - [27/Feb/2008:18:41:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [27/Feb/2008:18:41:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [27/Feb/2008:18:43:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [27/Feb/2008:18:43:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.192.172.49 - - [27/Feb/2008:18:52:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 -85.192.172.49 - - [27/Feb/2008:18:54:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.57.91.136 - - [27/Feb/2008:18:54:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -85.192.172.49 - - [27/Feb/2008:18:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.192.172.49 - - [27/Feb/2008:18:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [27/Feb/2008:18:55:11 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -71.57.91.136 - - [27/Feb/2008:18:55:18 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -71.57.91.136 - - [27/Feb/2008:18:55:38 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -85.192.172.49 - - [27/Feb/2008:18:55:45 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -85.192.172.49 - - [27/Feb/2008:18:55:47 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -207.47.10.211 - - [27/Feb/2008:18:59:09 -0600] "GET /python.html HTTP/1.1" 200 18870 -207.47.10.211 - - [27/Feb/2008:18:59:13 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -207.47.10.211 - - [27/Feb/2008:18:59:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.31.145 - - [27/Feb/2008:18:59:53 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -199.111.224.90 - - [27/Feb/2008:19:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -211.110.86.80 - - [27/Feb/2008:19:04:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -211.110.86.80 - - [27/Feb/2008:19:04:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -211.110.86.80 - - [27/Feb/2008:19:04:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 -211.110.86.80 - - [27/Feb/2008:19:04:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -211.110.86.80 - - [27/Feb/2008:19:04:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.127.118.125 - - [27/Feb/2008:19:09:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.127.118.125 - - [27/Feb/2008:19:09:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.149.227.70 - - [27/Feb/2008:19:13:07 -0600] "GET / HTTP/1.0" 200 4447 -62.149.227.70 - - [27/Feb/2008:19:13:07 -0600] "GET /training.html HTTP/1.0" 200 6154 -62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 -62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /software.html HTTP/1.0" 200 3163 -62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -62.149.227.70 - - [27/Feb/2008:19:13:09 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -62.149.227.70 - - [27/Feb/2008:19:13:09 -0600] "GET /about.html HTTP/1.0" 200 7890 -62.149.227.70 - - [27/Feb/2008:19:13:10 -0600] "GET /python.html HTTP/1.0" 200 18870 -62.149.227.70 - - [27/Feb/2008:19:13:10 -0600] "GET /index.html HTTP/1.0" 200 4447 -62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /dynamic/assign2.html HTTP/1.0" 200 4907 -62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /dynamic/syllabus.html HTTP/1.0" 200 4589 -62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 -62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /ply/README HTTP/1.0" 200 8605 -62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /sysop.html HTTP/1.0" 200 1760 -62.149.227.70 - - [27/Feb/2008:19:13:13 -0600] "GET /cv.html HTTP/1.0" 200 31798 -62.149.227.70 - - [27/Feb/2008:19:13:13 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.0" 200 158 -62.149.227.70 - - [27/Feb/2008:19:13:14 -0600] "GET /swill/software.html HTTP/1.0" 404 133 -69.91.149.168 - - [27/Feb/2008:19:14:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.91.149.168 - - [27/Feb/2008:19:14:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -69.91.149.168 - - [27/Feb/2008:19:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:19:15:23 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -199.111.205.204 - - [27/Feb/2008:19:15:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.91.149.168 - - [27/Feb/2008:19:15:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 -199.111.205.204 - - [27/Feb/2008:19:16:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:19:21:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.34.108 - - [27/Feb/2008:19:22:37 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -67.195.34.114 - - [27/Feb/2008:19:23:02 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -24.127.118.125 - - [27/Feb/2008:19:23:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:19:24:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.127.118.125 - - [27/Feb/2008:19:29:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.34.145.194 - - [27/Feb/2008:19:31:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 -64.34.145.194 - - [27/Feb/2008:19:31:10 -0600] "GET /photos/u505/pages/IMG_1522.htm HTTP/1.0" 404 133 -67.195.34.111 - - [27/Feb/2008:19:31:15 -0600] "GET /ply HTTP/1.0" 301 230 -199.111.205.204 - - [27/Feb/2008:19:31:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.34.111 - - [27/Feb/2008:19:31:19 -0600] "GET /ply/ HTTP/1.0" 200 8018 -24.127.118.125 - - [27/Feb/2008:19:34:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:19:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.92.161.138 - - [27/Feb/2008:19:37:00 -0600] "GET /ply/README HTTP/1.1" 200 8605 -193.252.149.15 - - [27/Feb/2008:19:38:00 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.15 - - [27/Feb/2008:19:38:03 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 -201.50.187.62 - - [27/Feb/2008:19:39:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.50.187.62 - - [27/Feb/2008:19:39:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.50.187.62 - - [27/Feb/2008:19:39:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.34.97 - - [27/Feb/2008:19:40:04 -0600] "GET /ply HTTP/1.0" 301 230 -67.195.34.97 - - [27/Feb/2008:19:40:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 -208.223.208.181 - - [27/Feb/2008:19:42:02 -0600] "GET / HTTP/1.0" 200 4447 -208.223.208.181 - - [27/Feb/2008:19:42:02 -0600] "GET / HTTP/1.0" 200 4447 -24.127.118.125 - - [27/Feb/2008:19:42:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [27/Feb/2008:19:43:58 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [27/Feb/2008:19:44:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -201.236.226.90 - - [27/Feb/2008:19:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [27/Feb/2008:19:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [27/Feb/2008:19:44:11 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -199.111.224.90 - - [27/Feb/2008:19:44:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.224.90 - - [27/Feb/2008:19:44:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.127.118.125 - - [27/Feb/2008:19:44:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.213.243.113 - - [27/Feb/2008:19:50:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -69.213.243.113 - - [27/Feb/2008:19:50:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -69.213.243.113 - - [27/Feb/2008:19:50:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.127.118.125 - - [27/Feb/2008:19:57:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.127.118.125 - - [27/Feb/2008:19:57:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -213.180.198.130 - - [27/Feb/2008:19:59:26 -0600] "HEAD /ply/index.html HTTP/1.0" 200 0 -199.111.205.204 - - [27/Feb/2008:20:04:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.124.35.196 - - [27/Feb/2008:20:04:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -200.124.35.196 - - [27/Feb/2008:20:04:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.124.35.196 - - [27/Feb/2008:20:05:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:20:05:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.232.14 - - [27/Feb/2008:20:05:01 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -32.145.0.255 - - [27/Feb/2008:20:06:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -32.145.0.255 - - [27/Feb/2008:20:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:20:07:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:20:09:02 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -66.29.115.55 - - [27/Feb/2008:20:14:47 -0600] "GET / HTTP/1.1" 200 4447 -65.55.235.161 - - [27/Feb/2008:20:22:00 -0600] "GET /robots.txt HTTP/1.0" 200 71 -122.208.5.10 - - [27/Feb/2008:20:22:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -122.208.5.10 - - [27/Feb/2008:20:22:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.208.5.10 - - [27/Feb/2008:20:22:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -122.208.5.10 - - [27/Feb/2008:20:22:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 -68.38.140.124 - - [27/Feb/2008:20:23:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 -68.38.140.124 - - [27/Feb/2008:20:23:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -68.38.140.124 - - [27/Feb/2008:20:23:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.25.105.176 - - [27/Feb/2008:20:24:40 -0600] "GET / HTTP/1.1" 200 4447 -72.25.105.176 - - [27/Feb/2008:20:24:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -72.25.105.176 - - [27/Feb/2008:20:24:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:20:29:46 -0600] "GET /about.html HTTP/1.1" 200 7890 -65.55.232.14 - - [27/Feb/2008:20:32:11 -0600] "GET /training.html HTTP/1.1" 200 6154 -199.111.205.204 - - [27/Feb/2008:20:32:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.214.45.129 - - [27/Feb/2008:20:33:15 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.214.45.129 - - [27/Feb/2008:20:33:15 -0600] "GET / HTTP/1.0" 200 4447 -199.111.205.204 - - [27/Feb/2008:20:37:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:20:38:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:20:38:56 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -65.55.232.14 - - [27/Feb/2008:20:38:58 -0600] "GET /index.html HTTP/1.1" 200 4447 -65.55.232.14 - - [27/Feb/2008:20:38:59 -0600] "GET /software.html HTTP/1.1" 200 3163 -65.55.232.14 - - [27/Feb/2008:20:38:59 -0600] "GET /writing.html HTTP/1.1" 200 2871 -199.111.205.204 - - [27/Feb/2008:20:39:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.135.190.54 - - [27/Feb/2008:20:41:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 -199.111.205.204 - - [27/Feb/2008:20:42:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.141 - - [27/Feb/2008:20:43:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.204.154 - - [27/Feb/2008:20:44:26 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -199.111.205.204 - - [27/Feb/2008:20:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:20:52:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:20:52:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:20:52:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.158 - - [27/Feb/2008:20:53:53 -0600] "GET /ply/ply.html HTTP/1.0" 304 - -70.190.236.166 - - [27/Feb/2008:20:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.54.165.36 - - [27/Feb/2008:21:03:56 -0600] "GET /robots.txt HTTP/1.1" 200 71 -69.213.243.113 - - [27/Feb/2008:21:04:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -69.213.243.113 - - [27/Feb/2008:21:04:25 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 -69.213.243.113 - - [27/Feb/2008:21:04:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -69.213.243.113 - - [27/Feb/2008:21:04:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -199.111.205.204 - - [27/Feb/2008:21:05:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -207.176.224.242 - - [27/Feb/2008:21:05:53 -0600] "GET /robots.txt HTTP/1.0" 200 71 -199.111.205.204 - - [27/Feb/2008:21:11:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 -199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.195.244 - - [27/Feb/2008:21:11:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply HTTP/1.1" 301 242 -132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 -132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -132.206.52.168 - - [27/Feb/2008:21:13:32 -0600] "GET /ply/README HTTP/1.1" 200 8605 -38.98.120.84 - - [27/Feb/2008:21:13:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [27/Feb/2008:21:13:45 -0600] "GET / HTTP/1.1" 200 4447 -132.206.52.168 - - [27/Feb/2008:21:13:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.1.247.118 - - [27/Feb/2008:21:13:57 -0600] "GET /dynamic/ HTTP/1.1" 304 - -38.98.120.84 - - [27/Feb/2008:21:14:20 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:14:21 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:14:22 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:14:23 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:14:24 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [27/Feb/2008:21:14:25 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:14:26 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:14:27 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:14:28 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:14:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -38.98.120.84 - - [27/Feb/2008:21:14:30 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:14:31 -0600] "GET /publications.html HTTP/1.1" 200 7758 -38.98.120.84 - - [27/Feb/2008:21:14:33 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:14:33 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:14:34 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -38.98.120.84 - - [27/Feb/2008:21:14:35 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:14:36 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:14:39 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -38.98.120.84 - - [27/Feb/2008:21:14:40 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 -38.98.120.84 - - [27/Feb/2008:21:14:40 -0600] "GET /ply/README HTTP/1.1" 200 8605 -38.98.120.84 - - [27/Feb/2008:21:14:41 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:14:43 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 -38.98.120.84 - - [27/Feb/2008:21:14:43 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:14:44 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [27/Feb/2008:21:14:46 -0600] "GET /swill/consulting.html HTTP/1.1" 404 133 -38.98.120.84 - - [27/Feb/2008:21:14:46 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:14:47 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:14:48 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:14:49 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 -38.98.120.84 - - [27/Feb/2008:21:14:50 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:14:51 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 -38.98.120.84 - - [27/Feb/2008:21:14:52 -0600] "GET /swill/software.html HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:21:14:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -38.98.120.84 - - [27/Feb/2008:21:14:53 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:14:54 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:14:55 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:14:56 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:14:57 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:14:58 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:15:02 -0600] "GET /diet.html HTTP/1.1" 404 133 -38.98.120.84 - - [27/Feb/2008:21:15:02 -0600] "GET /cv.html HTTP/1.1" 200 31798 -38.98.120.84 - - [27/Feb/2008:21:15:03 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:15:04 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -38.98.120.84 - - [27/Feb/2008:21:15:05 -0600] "GET /swill/about.html HTTP/1.1" 404 133 -38.98.120.84 - - [27/Feb/2008:21:15:06 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:15:07 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:15:08 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:15:09 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:15:10 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:15:11 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:15:12 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:15:13 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 -38.98.120.84 - - [27/Feb/2008:21:15:14 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:15:15 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:15:16 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:15:17 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:15:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:15:19 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:15:20 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:15:21 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:15:22 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:15:23 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -38.98.120.84 - - [27/Feb/2008:21:15:24 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:15:25 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:15:26 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:15:27 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:15:28 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:15:29 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 -38.98.120.84 - - [27/Feb/2008:21:15:30 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:15:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:15:32 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:15:33 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:15:34 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:15:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:15:36 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:15:37 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 -38.98.120.84 - - [27/Feb/2008:21:15:38 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:15:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:15:40 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 -38.98.120.84 - - [27/Feb/2008:21:15:43 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:15:44 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [27/Feb/2008:21:15:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:15:46 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:15:47 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:15:48 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:15:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [27/Feb/2008:21:15:50 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:15:51 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -38.98.120.84 - - [27/Feb/2008:21:15:52 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:15:53 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 -38.98.120.84 - - [27/Feb/2008:21:15:54 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:15:55 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [27/Feb/2008:21:15:56 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 135478 -38.98.120.84 - - [27/Feb/2008:21:15:57 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:15:58 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:15:59 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:16:00 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -38.98.120.84 - - [27/Feb/2008:21:16:01 -0600] "GET /swill/training.html HTTP/1.1" 404 133 -38.98.120.84 - - [27/Feb/2008:21:16:02 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -38.98.120.84 - - [27/Feb/2008:21:16:03 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:16:04 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -38.98.120.84 - - [27/Feb/2008:21:16:05 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:16:06 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:16:07 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:16:08 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -38.98.120.84 - - [27/Feb/2008:21:16:09 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:16:10 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:16:11 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 -38.98.120.84 - - [27/Feb/2008:21:16:12 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:16:13 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.1" 200 399 -38.98.120.84 - - [27/Feb/2008:21:16:14 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -38.98.120.84 - - [27/Feb/2008:21:16:15 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 -38.98.120.84 - - [27/Feb/2008:21:16:16 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -38.98.120.84 - - [27/Feb/2008:21:16:17 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [27/Feb/2008:21:16:18 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:16:19 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -38.98.120.84 - - [27/Feb/2008:21:16:20 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:16:21 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:16:22 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:16:23 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:16:24 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:16:25 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [27/Feb/2008:21:16:26 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [27/Feb/2008:21:16:28 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:16:29 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:16:33 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [27/Feb/2008:21:16:33 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 -38.98.120.84 - - [27/Feb/2008:21:16:34 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.1" 200 375 -38.98.120.84 - - [27/Feb/2008:21:16:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -38.98.120.84 - - [27/Feb/2008:21:16:36 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 -38.98.120.84 - - [27/Feb/2008:21:16:37 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:16:38 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 -38.98.120.84 - - [27/Feb/2008:21:16:46 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [27/Feb/2008:21:16:47 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -38.98.120.84 - - [27/Feb/2008:21:16:48 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -38.98.120.84 - - [27/Feb/2008:21:16:49 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:16:50 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [27/Feb/2008:21:16:51 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [27/Feb/2008:21:16:52 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [27/Feb/2008:21:16:53 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:16:54 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [27/Feb/2008:21:16:55 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.1" 200 293 -38.98.120.84 - - [27/Feb/2008:21:16:56 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:16:57 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [27/Feb/2008:21:16:58 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -38.98.120.84 - - [27/Feb/2008:21:17:00 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:17:00 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [27/Feb/2008:21:17:29 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 119844 -199.111.205.204 - - [27/Feb/2008:21:20:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [27/Feb/2008:21:27:16 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -76.206.238.222 - - [27/Feb/2008:21:29:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /training.html HTTP/1.0" 200 6154 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /training.html HTTP/1.0" 200 6154 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /index.html HTTP/1.0" 200 4447 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /software.html HTTP/1.0" 200 3163 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /software.html HTTP/1.0" 200 3163 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /index.html HTTP/1.0" 200 4447 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /writing.html HTTP/1.0" 200 2871 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /writing.html HTTP/1.0" 200 2871 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /about.html HTTP/1.0" 200 7890 -208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /about.html HTTP/1.0" 200 7890 -217.172.44.82 - - [27/Feb/2008:21:30:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 -65.55.232.14 - - [27/Feb/2008:21:30:38 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -72.87.11.163 - - [27/Feb/2008:21:31:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 -72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -72.87.11.163 - - [27/Feb/2008:21:31:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -65.55.232.14 - - [27/Feb/2008:21:37:45 -0600] "GET /ply/README HTTP/1.1" 304 - -76.206.238.222 - - [27/Feb/2008:21:38:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 79526 -76.206.238.222 - - [27/Feb/2008:21:38:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 135312 -128.135.139.146 - - [27/Feb/2008:21:40:21 -0600] "GET /dynamic/ HTTP/1.1" 304 - -65.55.232.14 - - [27/Feb/2008:21:41:42 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -24.10.16.193 - - [27/Feb/2008:21:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:21:42:07 -0600] "GET /cv.html HTTP/1.1" 200 31798 -65.55.232.14 - - [27/Feb/2008:21:42:09 -0600] "GET /diet.html HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:21:42:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:21:43:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:21:46:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -65.55.232.14 - - [27/Feb/2008:21:46:58 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -65.55.232.14 - - [27/Feb/2008:21:46:59 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -65.55.232.14 - - [27/Feb/2008:21:47:03 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 3110734 -24.10.16.193 - - [27/Feb/2008:21:47:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:21:47:21 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 200 288790 -24.10.16.193 - - [27/Feb/2008:21:48:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.176.147.11 - - [27/Feb/2008:21:49:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.176.147.11 - - [27/Feb/2008:21:49:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.15 - - [27/Feb/2008:21:49:23 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.232.15 - - [27/Feb/2008:21:49:27 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 200 3246437 -24.10.16.193 - - [27/Feb/2008:21:51:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:21:52:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:21:53:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:21:54:05 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -24.10.16.193 - - [27/Feb/2008:21:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:21:56:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:22:00:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:22:01:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.194.239 - - [27/Feb/2008:22:02:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -199.111.194.239 - - [27/Feb/2008:22:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.194.239 - - [27/Feb/2008:22:02:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.54.165.36 - - [27/Feb/2008:22:06:27 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -65.55.232.14 - - [27/Feb/2008:22:07:12 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -65.55.232.14 - - [27/Feb/2008:22:07:15 -0600] "GET /publications.html HTTP/1.1" 200 7758 -65.55.232.14 - - [27/Feb/2008:22:07:15 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -65.55.232.14 - - [27/Feb/2008:22:07:22 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -65.55.232.14 - - [27/Feb/2008:22:07:24 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -24.10.16.193 - - [27/Feb/2008:22:09:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.232.14 - - [27/Feb/2008:22:09:50 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 200 514533 -24.10.16.193 - - [27/Feb/2008:22:09:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:22:13:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:22:14:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.141 - - [27/Feb/2008:22:14:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.57.245.11 - - [27/Feb/2008:22:17:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 -65.57.245.11 - - [27/Feb/2008:22:17:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -65.57.245.11 - - [27/Feb/2008:22:17:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.57.245.11 - - [27/Feb/2008:22:19:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.57.245.11 - - [27/Feb/2008:22:19:19 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -201.78.136.222 - - [27/Feb/2008:22:20:55 -0600] "GET / HTTP/1.1" 200 4447 -201.78.136.222 - - [27/Feb/2008:22:20:55 -0600] "GET /ply HTTP/1.1" 301 242 -201.78.136.222 - - [27/Feb/2008:22:21:38 -0600] "GET /about.html HTTP/1.1" 200 7890 -199.111.205.204 - - [27/Feb/2008:22:22:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:22:24:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.57.245.11 - - [27/Feb/2008:22:25:47 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -199.111.205.204 - - [27/Feb/2008:22:26:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:22:27:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:22:28:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -199.111.205.204 - - [27/Feb/2008:22:28:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [27/Feb/2008:22:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -24.10.16.193 - - [27/Feb/2008:22:34:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.116.72.114 - - [27/Feb/2008:22:38:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.116.72.114 - - [27/Feb/2008:22:38:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.170 - - [27/Feb/2008:22:41:47 -0600] "GET / HTTP/1.0" 304 - -67.195.58.186 - - [27/Feb/2008:22:43:05 -0600] "GET /about.html HTTP/1.0" 200 7890 -128.143.255.129 - - [27/Feb/2008:22:48:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 -75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.72.178.39 - - [27/Feb/2008:22:55:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -190.128.108.147 - - [27/Feb/2008:23:07:42 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -190.128.108.147 - - [27/Feb/2008:23:07:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -190.128.108.147 - - [27/Feb/2008:23:07:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -190.128.108.147 - - [27/Feb/2008:23:07:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -81.210.132.13 - - [27/Feb/2008:23:09:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 -81.210.132.13 - - [27/Feb/2008:23:09:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -81.210.132.13 - - [27/Feb/2008:23:09:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.72.178.39 - - [27/Feb/2008:23:23:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -75.72.178.39 - - [27/Feb/2008:23:23:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -75.72.178.39 - - [27/Feb/2008:23:23:54 -0600] "GET /ply/README HTTP/1.1" 200 8605 -75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.58.86.1 - - [27/Feb/2008:23:30:31 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -24.10.16.193 - - [27/Feb/2008:23:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -131.107.0.101 - - [27/Feb/2008:23:41:48 -0600] "GET /photos/u505/pages/IMG_1543.htm HTTP/1.1" 404 133 -65.55.208.124 - - [27/Feb/2008:23:45:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.124 - - [27/Feb/2008:23:45:06 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE007.HTM HTTP/1.1" 304 - -65.55.208.123 - - [27/Feb/2008:23:45:22 -0600] "GET /robots.txt HTTP/1.1" 200 71 -199.111.194.239 - - [27/Feb/2008:23:46:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.29.115.55 - - [27/Feb/2008:23:48:49 -0600] "GET /about.html HTTP/1.1" 200 7890 -66.232.113.62 - - [27/Feb/2008:23:49:57 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 -201.63.117.142 - - [27/Feb/2008:23:50:04 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -217.172.56.49 - - [27/Feb/2008:23:50:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -195.229.242.154 - - [27/Feb/2008:23:50:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -89.122.29.76 - - [27/Feb/2008:23:54:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -24.10.16.193 - - [27/Feb/2008:23:59:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.34.145.201 - - [28/Feb/2008:00:02:30 -0600] "GET /robots.txt HTTP/1.0" 200 71 -64.34.145.201 - - [28/Feb/2008:00:02:30 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 -206.51.226.87 - - [28/Feb/2008:00:13:22 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -213.209.210.70 - - [28/Feb/2008:00:13:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -212.247.11.155 - - [28/Feb/2008:00:13:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -72.236.184.249 - - [28/Feb/2008:00:13:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -65.55.208.118 - - [28/Feb/2008:00:13:47 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.118 - - [28/Feb/2008:00:13:47 -0600] "GET /photos/wind/pages/IMG_1282.htm HTTP/1.1" 404 133 -65.55.208.118 - - [28/Feb/2008:00:13:48 -0600] "GET /photos/wind/pages/IMG_1329.htm HTTP/1.1" 404 133 -213.145.165.82 - - [28/Feb/2008:00:15:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -220.233.181.234 - - [28/Feb/2008:00:20:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.233.181.234 - - [28/Feb/2008:00:20:34 -0600] "GET /favicon.gif HTTP/1.1" 404 133 -220.233.181.234 - - [28/Feb/2008:00:20:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.233.181.234 - - [28/Feb/2008:00:20:35 -0600] "GET /favicon.gif HTTP/1.1" 404 133 -24.10.16.193 - - [28/Feb/2008:00:20:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [28/Feb/2008:00:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:00:29:30 -0600] "GET / HTTP/1.1" 200 4447 -67.186.98.20 - - [28/Feb/2008:00:29:30 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -67.186.98.20 - - [28/Feb/2008:00:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:00:29:41 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -67.186.98.20 - - [28/Feb/2008:00:29:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.186.98.20 - - [28/Feb/2008:00:31:59 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -65.55.208.118 - - [28/Feb/2008:00:37:47 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 -65.55.208.118 - - [28/Feb/2008:00:37:48 -0600] "GET /swill/training.html HTTP/1.1" 404 133 -213.157.22.122 - - [28/Feb/2008:00:51:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -213.157.22.122 - - [28/Feb/2008:00:51:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.157.22.122 - - [28/Feb/2008:00:51:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.157.22.122 - - [28/Feb/2008:00:52:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.157.22.122 - - [28/Feb/2008:00:52:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -195.3.254.138 - - [28/Feb/2008:00:52:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 -195.3.254.138 - - [28/Feb/2008:00:52:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -195.3.254.138 - - [28/Feb/2008:00:53:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -195.3.254.138 - - [28/Feb/2008:00:53:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.212.77 - - [28/Feb/2008:00:55:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 -195.3.254.138 - - [28/Feb/2008:00:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.54.144.229 - - [28/Feb/2008:00:57:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -192.54.144.229 - - [28/Feb/2008:00:57:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.54.144.229 - - [28/Feb/2008:00:57:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -67.186.98.20 - - [28/Feb/2008:00:58:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.186.98.20 - - [28/Feb/2008:00:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.54.144.229 - - [28/Feb/2008:00:58:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -67.186.98.20 - - [28/Feb/2008:00:58:32 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -217.196.43.134 - - [28/Feb/2008:01:05:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -84.89.249.77 - - [28/Feb/2008:01:09:28 -0600] "GET /cv.html HTTP/1.1" 200 31798 -84.89.249.77 - - [28/Feb/2008:01:09:37 -0600] "GET / HTTP/1.1" 200 4447 -84.89.249.77 - - [28/Feb/2008:01:09:38 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -84.89.249.77 - - [28/Feb/2008:01:12:22 -0600] "GET /software.html HTTP/1.1" 200 3163 -65.55.208.118 - - [28/Feb/2008:01:12:45 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 304 - -84.89.249.77 - - [28/Feb/2008:01:12:55 -0600] "GET /writing.html HTTP/1.1" 200 2871 -84.89.249.77 - - [28/Feb/2008:01:12:56 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 -84.89.249.77 - - [28/Feb/2008:01:13:21 -0600] "GET /about.html HTTP/1.1" 200 7890 -84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 -84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 -84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 -84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 -84.89.249.77 - - [28/Feb/2008:01:13:54 -0600] "GET /index.html HTTP/1.1" 200 4447 -72.163.216.217 - - [28/Feb/2008:01:17:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.25.112 - - [28/Feb/2008:01:18:35 -0600] "GET /swill/about.html HTTP/1.0" 404 133 -65.55.104.13 - - [28/Feb/2008:01:23:00 -0600] "GET /robots.txt HTTP/1.1" 200 71 -67.123.80.214 - - [28/Feb/2008:01:38:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -67.123.80.214 - - [28/Feb/2008:01:38:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.123.80.214 - - [28/Feb/2008:01:38:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.123.80.214 - - [28/Feb/2008:01:38:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -67.123.80.214 - - [28/Feb/2008:01:38:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -67.123.80.214 - - [28/Feb/2008:01:41:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 -194.186.83.193 - - [28/Feb/2008:01:42:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 56145 -195.3.254.138 - - [28/Feb/2008:01:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -88.131.106.15 - - [28/Feb/2008:01:52:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 -88.131.106.15 - - [28/Feb/2008:01:52:39 -0600] "GET /ply/README HTTP/1.0" 200 8605 -24.223.228.170 - - [28/Feb/2008:01:59:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.223.228.170 - - [28/Feb/2008:01:59:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -24.223.228.170 - - [28/Feb/2008:01:59:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.58.232.58 - - [28/Feb/2008:02:02:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -84.58.232.58 - - [28/Feb/2008:02:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.58.232.58 - - [28/Feb/2008:02:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [28/Feb/2008:02:02:49 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -61.135.166.102 - - [28/Feb/2008:02:04:34 -0600] "GET / HTTP/1.1" 200 4447 -220.181.38.169 - - [28/Feb/2008:02:07:15 -0600] "GET / HTTP/1.1" 200 4447 -67.195.58.174 - - [28/Feb/2008:02:26:24 -0600] "GET /ply/ HTTP/1.0" 304 - -65.214.45.129 - - [28/Feb/2008:02:26:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.0" 200 1679 -123.240.74.156 - - [28/Feb/2008:02:30:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -123.240.74.156 - - [28/Feb/2008:02:30:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 -123.240.74.156 - - [28/Feb/2008:02:30:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -82.150.2.20 - - [28/Feb/2008:02:36:26 -0600] "GET /ply/ HTTP/1.0" 200 8018 -82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -82.150.2.20 - - [28/Feb/2008:02:36:32 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -62.161.167.222 - - [28/Feb/2008:02:41:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.161.167.222 - - [28/Feb/2008:02:41:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.161.167.222 - - [28/Feb/2008:02:41:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:02:41:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:02:41:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:02:41:56 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -62.161.167.222 - - [28/Feb/2008:02:42:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:02:49:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:02:49:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.223.228.170 - - [28/Feb/2008:02:50:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.10.60.85 - - [28/Feb/2008:02:50:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 -217.10.60.85 - - [28/Feb/2008:02:50:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -217.10.60.85 - - [28/Feb/2008:02:50:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.10.60.85 - - [28/Feb/2008:02:50:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.10.60.85 - - [28/Feb/2008:02:50:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.10.60.85 - - [28/Feb/2008:02:50:19 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -80.136.165.22 - - [28/Feb/2008:02:53:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.136.165.22 - - [28/Feb/2008:02:53:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -80.136.165.22 - - [28/Feb/2008:02:53:43 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -62.161.167.222 - - [28/Feb/2008:03:01:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -209.9.228.166 - - [28/Feb/2008:03:06:39 -0600] "GET /robots.txt HTTP/1.1" 200 71 -209.9.228.174 - - [28/Feb/2008:03:06:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -62.161.167.222 - - [28/Feb/2008:03:08:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.25.20 - - [28/Feb/2008:03:08:49 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.28.223 - - [28/Feb/2008:03:08:49 -0600] "GET /python/python.html HTTP/1.0" 404 133 -98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET / HTTP/1.1" 200 4447 -98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /about.html HTTP/1.1" 200 7890 -98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 -98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 -98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 -98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 -203.101.103.2 - - [28/Feb/2008:03:09:25 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -213.246.173.21 - - [28/Feb/2008:03:11:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -213.246.173.21 - - [28/Feb/2008:03:11:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.246.173.21 - - [28/Feb/2008:03:11:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -65.55.208.118 - - [28/Feb/2008:03:12:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.1" 403 257 -83.12.228.78 - - [28/Feb/2008:03:14:42 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.1" 200 0 -139.149.31.231 - - [28/Feb/2008:03:15:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -139.149.31.231 - - [28/Feb/2008:03:15:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.3.134.34 - - [28/Feb/2008:03:19:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -218.107.55.253 - - [28/Feb/2008:03:21:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 -218.107.55.253 - - [28/Feb/2008:03:21:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -218.107.55.253 - - [28/Feb/2008:03:21:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12707 -218.107.55.253 - - [28/Feb/2008:03:21:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -218.107.55.253 - - [28/Feb/2008:03:21:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -218.107.55.253 - - [28/Feb/2008:03:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -218.107.55.253 - - [28/Feb/2008:03:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.92.181.114 - - [28/Feb/2008:03:23:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -193.92.181.114 - - [28/Feb/2008:03:23:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.92.181.114 - - [28/Feb/2008:03:23:07 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -62.161.167.222 - - [28/Feb/2008:03:27:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.202.49.172 - - [28/Feb/2008:03:30:36 -0600] "GET /robots.txt HTTP/1.1" 200 71 -218.107.55.253 - - [28/Feb/2008:03:30:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.202.49.172 - - [28/Feb/2008:03:31:12 -0600] "GET /ply HTTP/1.1" 301 242 -67.202.49.172 - - [28/Feb/2008:03:31:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -218.107.55.253 - - [28/Feb/2008:03:31:36 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -83.206.220.147 - - [28/Feb/2008:03:40:59 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.0" 200 0 -83.206.220.147 - - [28/Feb/2008:03:41:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -82.127.117.160 - - [28/Feb/2008:03:43:59 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -134.173.56.81 - - [28/Feb/2008:03:46:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 -134.173.56.81 - - [28/Feb/2008:03:46:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -134.173.56.81 - - [28/Feb/2008:03:46:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.145.209.82 - - [28/Feb/2008:03:47:12 -0600] "GET /ply/ HTTP/1.0" 200 8018 -89.145.209.82 - - [28/Feb/2008:03:47:13 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -89.145.209.82 - - [28/Feb/2008:03:47:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -89.145.209.82 - - [28/Feb/2008:03:47:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -134.173.56.81 - - [28/Feb/2008:03:48:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.56.81 - - [28/Feb/2008:03:48:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:03:50:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.145.209.82 - - [28/Feb/2008:03:52:02 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -193.252.149.16 - - [28/Feb/2008:03:53:39 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.16 - - [28/Feb/2008:03:53:45 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -66.232.113.194 - - [28/Feb/2008:03:54:54 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2755 -72.236.184.249 - - [28/Feb/2008:03:54:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -89.222.157.76 - - [28/Feb/2008:03:54:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -86.15.171.18 - - [28/Feb/2008:03:55:53 -0600] "GET /ply HTTP/1.1" 301 242 -86.15.171.18 - - [28/Feb/2008:03:55:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -192.25.206.10 - - [28/Feb/2008:03:57:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 -137.138.83.15 - - [28/Feb/2008:04:06:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 -137.138.83.15 - - [28/Feb/2008:04:06:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -137.138.83.15 - - [28/Feb/2008:04:06:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -65.55.208.117 - - [28/Feb/2008:04:06:46 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.117 - - [28/Feb/2008:04:06:46 -0600] "GET /photos/wind/pages/IMG_1258.htm HTTP/1.1" 404 133 -65.55.208.117 - - [28/Feb/2008:04:06:59 -0600] "GET /photos/wind/pages/IMG_1330.htm HTTP/1.1" 404 133 -98.215.100.68 - - [28/Feb/2008:04:07:37 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -98.215.100.68 - - [28/Feb/2008:04:07:43 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -98.215.100.68 - - [28/Feb/2008:04:08:07 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -67.195.58.158 - - [28/Feb/2008:04:13:19 -0600] "GET /ply/ply.html HTTP/1.0" 304 - -198.54.202.210 - - [28/Feb/2008:04:13:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -60.248.16.38 - - [28/Feb/2008:04:15:51 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -60.248.16.38 - - [28/Feb/2008:04:15:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.117.168.219 - - [28/Feb/2008:04:22:43 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -206.51.237.114 - - [28/Feb/2008:04:24:03 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 -189.42.79.133 - - [28/Feb/2008:04:24:07 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -201.228.123.66 - - [28/Feb/2008:04:24:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -89.145.209.82 - - [28/Feb/2008:04:24:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.55.208.121 - - [28/Feb/2008:04:26:53 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.121 - - [28/Feb/2008:04:26:53 -0600] "GET /photos/wind/pages/IMG_1274.htm HTTP/1.1" 404 133 -203.199.144.195 - - [28/Feb/2008:04:31:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -203.199.144.195 - - [28/Feb/2008:04:31:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.119.242.94 - - [28/Feb/2008:04:37:55 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -82.119.242.94 - - [28/Feb/2008:04:37:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.165.112.85 - - [28/Feb/2008:04:42:44 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -128.165.112.85 - - [28/Feb/2008:04:42:54 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -82.119.242.94 - - [28/Feb/2008:04:44:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.177.81 - - [28/Feb/2008:04:50:25 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.177.81 - - [28/Feb/2008:04:50:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1035 -203.213.7.130 - - [28/Feb/2008:04:57:34 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -203.213.7.130 - - [28/Feb/2008:04:57:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.104 - - [28/Feb/2008:05:09:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.38.104 - - [28/Feb/2008:05:09:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -153.110.6.241 - - [28/Feb/2008:05:22:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -153.110.6.241 - - [28/Feb/2008:05:22:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -203.213.7.130 - - [28/Feb/2008:05:27:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -88.215.172.82 - - [28/Feb/2008:05:29:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -88.215.172.82 - - [28/Feb/2008:05:29:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -88.215.172.82 - - [28/Feb/2008:05:29:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 -62.180.231.91 - - [28/Feb/2008:05:30:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -62.180.231.91 - - [28/Feb/2008:05:30:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -147.228.209.194 - - [28/Feb/2008:05:32:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 -147.228.209.194 - - [28/Feb/2008:05:32:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 20114 -89.145.209.82 - - [28/Feb/2008:05:32:21 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.55.208.119 - - [28/Feb/2008:05:39:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.119 - - [28/Feb/2008:05:39:06 -0600] "GET /swill/about.html HTTP/1.1" 404 133 -74.6.25.199 - - [28/Feb/2008:05:39:28 -0600] "GET /cv.html HTTP/1.0" 304 - -74.6.19.156 - - [28/Feb/2008:05:40:54 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 -83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /ply/README HTTP/1.1" 200 8605 -83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.12.146.250 - - [28/Feb/2008:05:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.208.120 - - [28/Feb/2008:05:44:50 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.120 - - [28/Feb/2008:05:44:50 -0600] "GET /photos/u505/pages/IMG_1516.htm HTTP/1.1" 404 133 -203.213.7.130 - - [28/Feb/2008:05:51:12 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.55.208.117 - - [28/Feb/2008:05:56:18 -0600] "GET /photos/wind/pages/IMG_1262.htm HTTP/1.1" 404 133 -153.110.6.241 - - [28/Feb/2008:06:01:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -203.213.7.130 - - [28/Feb/2008:06:09:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -59.124.68.145 - - [28/Feb/2008:06:10:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -59.124.68.145 - - [28/Feb/2008:06:10:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -59.124.68.145 - - [28/Feb/2008:06:10:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -83.13.207.74 - - [28/Feb/2008:06:12:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.13.207.74 - - [28/Feb/2008:06:12:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -83.13.207.74 - - [28/Feb/2008:06:12:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -83.13.207.74 - - [28/Feb/2008:06:12:57 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -80.200.221.74 - - [28/Feb/2008:06:15:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.200.221.74 - - [28/Feb/2008:06:15:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.200.221.74 - - [28/Feb/2008:06:16:08 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -80.200.221.74 - - [28/Feb/2008:06:16:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -80.200.221.74 - - [28/Feb/2008:06:16:19 -0600] "GET /cgi-bin/wiki.pl?IgnoreDirective HTTP/1.1" 200 2253 -83.13.207.74 - - [28/Feb/2008:06:17:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.200.221.74 - - [28/Feb/2008:06:18:20 -0600] "GET /cgi-bin/wiki.pl?NewobjectDirective HTTP/1.1" 200 4258 -80.200.221.74 - - [28/Feb/2008:06:18:29 -0600] "GET /cgi-bin/wiki.pl?CallbackDirective HTTP/1.1" 200 3269 -80.200.221.74 - - [28/Feb/2008:06:18:51 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 -80.200.221.74 - - [28/Feb/2008:06:19:00 -0600] "GET /cgi-bin/wiki.pl?TypemapDirective HTTP/1.1" 200 1584 -80.200.221.74 - - [28/Feb/2008:06:19:19 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 -80.200.221.74 - - [28/Feb/2008:06:19:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -80.200.221.74 - - [28/Feb/2008:06:20:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.200.221.74 - - [28/Feb/2008:06:21:03 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 -62.180.231.91 - - [28/Feb/2008:06:21:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -80.200.221.74 - - [28/Feb/2008:06:21:28 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -80.200.221.74 - - [28/Feb/2008:06:21:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -80.200.221.74 - - [28/Feb/2008:06:21:37 -0600] "GET /cgi-bin/wiki.pl?NodefaultDirective HTTP/1.1" 200 1742 -80.200.221.74 - - [28/Feb/2008:06:21:43 -0600] "GET /cgi-bin/wiki.pl?MakedefaultDirective HTTP/1.1" 200 2961 -80.200.221.74 - - [28/Feb/2008:06:22:04 -0600] "GET /cgi-bin/wiki.pl?FeatureDirective HTTP/1.1" 200 4535 -80.200.221.74 - - [28/Feb/2008:06:22:22 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 -80.200.221.74 - - [28/Feb/2008:06:23:06 -0600] "GET /cgi-bin/wiki.pl?ContractDirective HTTP/1.1" 200 1512 -80.200.221.74 - - [28/Feb/2008:06:23:12 -0600] "GET /cgi-bin/wiki.pl?ConstantDirective HTTP/1.1" 200 1372 -72.20.109.37 - - [28/Feb/2008:06:28:13 -0600] "GET /robots.txt HTTP/1.1" 200 71 -72.20.109.37 - - [28/Feb/2008:06:28:13 -0600] "GET / HTTP/1.1" 200 4447 -72.20.109.37 - - [28/Feb/2008:06:28:14 -0600] "GET /about.html HTTP/1.1" 200 7890 -213.95.25.246 - - [28/Feb/2008:06:30:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -213.95.25.246 - - [28/Feb/2008:06:30:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.95.25.246 - - [28/Feb/2008:06:30:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -213.95.25.246 - - [28/Feb/2008:06:30:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -213.95.25.246 - - [28/Feb/2008:06:30:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -213.95.25.246 - - [28/Feb/2008:06:30:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -213.95.25.246 - - [28/Feb/2008:06:30:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -213.95.25.246 - - [28/Feb/2008:06:31:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 -71.126.89.51 - - [28/Feb/2008:06:31:19 -0600] "GET / HTTP/1.1" 200 4447 -88.191.19.81 - - [28/Feb/2008:06:31:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 -61.247.217.34 - - [28/Feb/2008:06:32:07 -0600] "GET /robots.txt HTTP/1.1" 200 71 -61.247.217.34 - - [28/Feb/2008:06:32:08 -0600] "GET / HTTP/1.1" 200 4447 -213.95.25.246 - - [28/Feb/2008:06:32:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 -213.95.25.246 - - [28/Feb/2008:06:33:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -213.95.25.246 - - [28/Feb/2008:06:34:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 -203.213.7.130 - - [28/Feb/2008:06:35:40 -0600] "GET /ply/ HTTP/1.0" 304 - -64.34.145.194 - - [28/Feb/2008:06:36:09 -0600] "GET /robots.txt HTTP/1.0" 200 71 -64.34.145.194 - - [28/Feb/2008:06:36:09 -0600] "GET /photos/u505/pages/IMG_1492.htm HTTP/1.0" 404 133 -128.143.136.157 - - [28/Feb/2008:06:36:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [28/Feb/2008:06:36:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.104.43.5 - - [28/Feb/2008:06:38:58 -0600] "GET /robots.txt HTTP/1.0" 200 71 -84.104.43.5 - - [28/Feb/2008:06:39:00 -0600] "GET /python.html HTTP/1.0" 200 18870 -60.50.9.191 - - [28/Feb/2008:06:39:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 -60.50.9.191 - - [28/Feb/2008:06:39:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -60.50.9.191 - - [28/Feb/2008:06:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -60.50.9.191 - - [28/Feb/2008:06:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.93.128.164 - - [28/Feb/2008:06:44:43 -0600] "GET / HTTP/1.0" 200 4447 -91.93.128.164 - - [28/Feb/2008:06:44:44 -0600] "GET /python.html HTTP/1.0" 200 18870 -91.93.128.164 - - [28/Feb/2008:06:44:45 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -91.93.128.164 - - [28/Feb/2008:06:44:45 -0600] "GET /training.html HTTP/1.0" 200 6154 -91.93.128.164 - - [28/Feb/2008:06:44:46 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 -91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /software.html HTTP/1.0" 200 3163 -91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /writing.html HTTP/1.0" 200 2871 -91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /index.html HTTP/1.0" 200 4447 -91.93.128.164 - - [28/Feb/2008:06:44:48 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -91.93.128.164 - - [28/Feb/2008:06:44:48 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -91.93.128.164 - - [28/Feb/2008:06:44:50 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -91.93.128.164 - - [28/Feb/2008:06:44:50 -0600] "GET /dynamic/assign5.html HTTP/1.0" 200 11008 -91.93.128.164 - - [28/Feb/2008:06:44:51 -0600] "GET /dynamic/assign4.html HTTP/1.0" 200 8712 -91.93.128.164 - - [28/Feb/2008:06:44:52 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 -91.93.128.164 - - [28/Feb/2008:06:44:52 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 -91.93.128.164 - - [28/Feb/2008:06:44:53 -0600] "GET /dynamic/soln12_1.html HTTP/1.0" 404 133 -91.93.128.164 - - [28/Feb/2008:06:44:53 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.0" 200 293 -91.93.128.164 - - [28/Feb/2008:06:44:54 -0600] "GET /swill/about.html HTTP/1.0" 404 133 -129.192.97.6 - - [28/Feb/2008:06:47:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE002.HTM HTTP/1.0" 200 1352 -129.192.97.6 - - [28/Feb/2008:06:47:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE003.HTM HTTP/1.0" 200 1620 -129.192.97.6 - - [28/Feb/2008:06:48:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.0" 200 990 -129.192.97.6 - - [28/Feb/2008:06:48:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE005.HTM HTTP/1.0" 200 1429 -129.192.97.6 - - [28/Feb/2008:06:48:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE006.HTM HTTP/1.0" 200 1254 -129.192.97.6 - - [28/Feb/2008:06:48:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1337 -129.192.97.6 - - [28/Feb/2008:06:48:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE008.HTM HTTP/1.0" 200 1231 -129.192.97.6 - - [28/Feb/2008:06:48:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE009.HTM HTTP/1.0" 200 1279 -129.192.97.6 - - [28/Feb/2008:06:48:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1403 -129.192.97.6 - - [28/Feb/2008:06:48:31 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.0" 200 1466 -129.192.97.6 - - [28/Feb/2008:06:48:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE012.HTM HTTP/1.0" 200 1466 -129.192.97.6 - - [28/Feb/2008:06:48:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE013.HTM HTTP/1.0" 200 1447 -129.192.97.6 - - [28/Feb/2008:06:48:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE014.HTM HTTP/1.0" 200 1232 -129.192.97.6 - - [28/Feb/2008:06:48:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE015.HTM HTTP/1.0" 200 1229 -129.192.97.6 - - [28/Feb/2008:06:48:44 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE016.HTM HTTP/1.0" 200 1343 -129.192.97.6 - - [28/Feb/2008:06:48:48 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE017.HTM HTTP/1.0" 200 1458 -129.192.97.6 - - [28/Feb/2008:06:48:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE018.HTM HTTP/1.0" 200 1429 -129.192.97.6 - - [28/Feb/2008:06:48:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE019.HTM HTTP/1.0" 200 1307 -129.192.97.6 - - [28/Feb/2008:06:48:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE020.HTM HTTP/1.0" 200 1338 -129.192.97.6 - - [28/Feb/2008:06:49:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE021.HTM HTTP/1.0" 200 1207 -129.192.97.6 - - [28/Feb/2008:06:49:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE022.HTM HTTP/1.0" 200 981 -129.192.97.6 - - [28/Feb/2008:06:49:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE023.HTM HTTP/1.0" 200 1576 -129.192.97.6 - - [28/Feb/2008:06:49:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE024.HTM HTTP/1.0" 200 1339 -129.192.97.6 - - [28/Feb/2008:06:49:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE025.HTM HTTP/1.0" 200 1570 -129.192.97.6 - - [28/Feb/2008:06:49:24 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE026.HTM HTTP/1.0" 200 1692 -129.192.97.6 - - [28/Feb/2008:06:49:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 -129.192.97.6 - - [28/Feb/2008:06:49:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE028.HTM HTTP/1.0" 200 1704 -129.192.97.6 - - [28/Feb/2008:06:49:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE029.HTM HTTP/1.0" 200 1491 -129.192.97.6 - - [28/Feb/2008:06:49:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE030.HTM HTTP/1.0" 200 1549 -129.192.97.6 - - [28/Feb/2008:06:49:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE031.HTM HTTP/1.0" 200 982 -129.192.97.6 - - [28/Feb/2008:06:49:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE032.HTM HTTP/1.0" 200 1444 -60.50.9.191 - - [28/Feb/2008:06:50:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.192.97.6 - - [28/Feb/2008:06:50:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE033.HTM HTTP/1.0" 200 1804 -60.50.9.191 - - [28/Feb/2008:06:50:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.192.97.6 - - [28/Feb/2008:06:50:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE034.HTM HTTP/1.0" 200 1586 -129.192.97.6 - - [28/Feb/2008:06:50:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE035.HTM HTTP/1.0" 200 1441 -129.192.97.6 - - [28/Feb/2008:06:51:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE036.HTM HTTP/1.0" 200 2010 -129.192.97.6 - - [28/Feb/2008:06:51:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE037.HTM HTTP/1.0" 200 1772 -129.192.97.6 - - [28/Feb/2008:06:51:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE038.HTM HTTP/1.0" 200 1373 -129.192.97.6 - - [28/Feb/2008:06:51:36 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE039.HTM HTTP/1.0" 200 1638 -129.192.97.6 - - [28/Feb/2008:06:51:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE040.HTM HTTP/1.0" 200 1910 -129.192.97.6 - - [28/Feb/2008:06:51:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE041.HTM HTTP/1.0" 200 2037 -129.192.97.6 - - [28/Feb/2008:06:51:46 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.0" 200 1336 -129.192.97.6 - - [28/Feb/2008:06:51:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE043.HTM HTTP/1.0" 200 1368 -129.192.97.6 - - [28/Feb/2008:06:51:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE044.HTM HTTP/1.0" 200 1000 -129.192.97.6 - - [28/Feb/2008:06:51:53 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE045.HTM HTTP/1.0" 200 1333 -129.192.97.6 - - [28/Feb/2008:06:51:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE046.HTM HTTP/1.0" 200 1738 -129.192.97.6 - - [28/Feb/2008:06:52:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE047.HTM HTTP/1.0" 200 1410 -129.192.97.6 - - [28/Feb/2008:06:52:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE048.HTM HTTP/1.0" 200 1654 -129.192.97.6 - - [28/Feb/2008:06:52:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE049.HTM HTTP/1.0" 200 1574 -129.192.97.6 - - [28/Feb/2008:06:52:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE050.HTM HTTP/1.0" 200 989 -129.192.97.6 - - [28/Feb/2008:06:52:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE051.HTM HTTP/1.0" 200 1476 -129.192.97.6 - - [28/Feb/2008:06:52:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE052.HTM HTTP/1.0" 200 1633 -129.192.97.6 - - [28/Feb/2008:06:52:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE053.HTM HTTP/1.0" 200 1661 -129.192.97.6 - - [28/Feb/2008:06:52:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE054.HTM HTTP/1.0" 200 1450 -129.192.97.6 - - [28/Feb/2008:06:52:23 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE055.HTM HTTP/1.0" 200 1319 -129.192.97.6 - - [28/Feb/2008:06:52:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE056.HTM HTTP/1.0" 200 1504 -129.192.97.6 - - [28/Feb/2008:06:52:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE057.HTM HTTP/1.0" 200 1847 -129.192.97.6 - - [28/Feb/2008:06:52:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE058.HTM HTTP/1.0" 200 1296 -213.95.25.246 - - [28/Feb/2008:06:52:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -129.192.97.6 - - [28/Feb/2008:06:52:31 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE059.HTM HTTP/1.0" 200 1694 -129.192.97.6 - - [28/Feb/2008:06:52:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE060.HTM HTTP/1.0" 200 1686 -129.192.97.6 - - [28/Feb/2008:06:52:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE061.HTM HTTP/1.0" 200 1508 -129.192.97.6 - - [28/Feb/2008:06:52:38 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE062.HTM HTTP/1.0" 200 1322 -129.192.97.6 - - [28/Feb/2008:06:52:40 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE063.HTM HTTP/1.0" 200 1511 -129.192.97.6 - - [28/Feb/2008:06:52:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE064.HTM HTTP/1.0" 200 971 -129.192.97.6 - - [28/Feb/2008:06:52:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE065.HTM HTTP/1.0" 200 1822 -129.192.97.6 - - [28/Feb/2008:06:52:45 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE066.HTM HTTP/1.0" 200 1538 -129.192.97.6 - - [28/Feb/2008:06:52:48 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE067.HTM HTTP/1.0" 200 1750 -129.192.97.6 - - [28/Feb/2008:06:52:50 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE068.HTM HTTP/1.0" 200 1394 -129.192.97.6 - - [28/Feb/2008:06:52:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE069.HTM HTTP/1.0" 200 1702 -129.192.97.6 - - [28/Feb/2008:06:52:54 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE070.HTM HTTP/1.0" 200 1698 -129.192.97.6 - - [28/Feb/2008:06:52:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE071.HTM HTTP/1.0" 200 1659 -129.192.97.6 - - [28/Feb/2008:06:52:57 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE072.HTM HTTP/1.0" 200 1850 -129.192.97.6 - - [28/Feb/2008:06:52:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE073.HTM HTTP/1.0" 200 1290 -129.192.97.6 - - [28/Feb/2008:06:53:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE074.HTM HTTP/1.0" 200 1351 -129.192.97.6 - - [28/Feb/2008:06:53:03 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE075.HTM HTTP/1.0" 200 1512 -129.192.97.6 - - [28/Feb/2008:06:53:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE076.HTM HTTP/1.0" 200 1650 -129.192.97.6 - - [28/Feb/2008:06:53:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE077.HTM HTTP/1.0" 200 1451 -129.192.97.6 - - [28/Feb/2008:06:53:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE078.HTM HTTP/1.0" 200 1329 -129.192.97.6 - - [28/Feb/2008:06:53:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE079.HTM HTTP/1.0" 200 1466 -129.192.97.6 - - [28/Feb/2008:06:53:11 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE080.HTM HTTP/1.0" 200 1578 -129.192.97.6 - - [28/Feb/2008:06:53:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE081.HTM HTTP/1.0" 200 1543 -129.192.97.6 - - [28/Feb/2008:06:53:18 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE082.HTM HTTP/1.0" 200 983 -129.192.97.6 - - [28/Feb/2008:06:53:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE083.HTM HTTP/1.0" 200 1418 -129.192.97.6 - - [28/Feb/2008:06:53:24 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE084.HTM HTTP/1.0" 200 1475 -129.192.97.6 - - [28/Feb/2008:06:53:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE085.HTM HTTP/1.0" 200 1739 -129.192.97.6 - - [28/Feb/2008:06:53:34 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE086.HTM HTTP/1.0" 200 1726 -129.192.97.6 - - [28/Feb/2008:06:53:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE087.HTM HTTP/1.0" 200 1575 -129.192.97.6 - - [28/Feb/2008:06:53:45 -0600] "GET /python/tutorial/beazley_advanced_python/ HTTP/1.0" 403 238 -213.95.25.246 - - [28/Feb/2008:06:53:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -129.192.97.6 - - [28/Feb/2008:06:53:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE088.HTM HTTP/1.0" 200 1723 -129.192.97.6 - - [28/Feb/2008:06:53:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE089.HTM HTTP/1.0" 200 1444 -129.192.97.6 - - [28/Feb/2008:06:53:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE090.HTM HTTP/1.0" 200 2001 -129.192.97.6 - - [28/Feb/2008:06:54:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE091.HTM HTTP/1.0" 200 1772 -129.192.97.6 - - [28/Feb/2008:06:54:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE092.HTM HTTP/1.0" 200 1427 -129.192.97.6 - - [28/Feb/2008:06:54:15 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE093.HTM HTTP/1.0" 200 1412 -129.192.97.6 - - [28/Feb/2008:06:54:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE094.HTM HTTP/1.0" 200 1435 -129.192.97.6 - - [28/Feb/2008:06:54:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE095.HTM HTTP/1.0" 200 1654 -129.192.97.6 - - [28/Feb/2008:06:54:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE096.HTM HTTP/1.0" 200 1671 -129.192.97.6 - - [28/Feb/2008:06:54:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.0" 200 1679 -129.192.97.6 - - [28/Feb/2008:06:54:23 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE098.HTM HTTP/1.0" 200 1328 -129.192.97.6 - - [28/Feb/2008:06:54:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE099.HTM HTTP/1.0" 200 1493 -129.192.97.6 - - [28/Feb/2008:06:54:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE100.HTM HTTP/1.0" 200 1562 -129.192.97.6 - - [28/Feb/2008:06:54:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE101.HTM HTTP/1.0" 200 980 -129.192.97.6 - - [28/Feb/2008:06:54:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE102.HTM HTTP/1.0" 200 1252 -129.192.97.6 - - [28/Feb/2008:06:54:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE103.HTM HTTP/1.0" 200 1330 -129.192.97.6 - - [28/Feb/2008:06:54:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE104.HTM HTTP/1.0" 200 1140 -129.192.97.6 - - [28/Feb/2008:06:54:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE105.HTM HTTP/1.0" 200 1256 -129.192.97.6 - - [28/Feb/2008:06:54:54 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1298 -129.192.97.6 - - [28/Feb/2008:06:54:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE107.HTM HTTP/1.0" 200 1397 -129.192.97.6 - - [28/Feb/2008:06:55:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE108.HTM HTTP/1.0" 200 1686 -129.192.97.6 - - [28/Feb/2008:06:55:01 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1858 -129.192.97.6 - - [28/Feb/2008:06:55:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE110.HTM HTTP/1.0" 200 1791 -129.192.97.6 - - [28/Feb/2008:06:55:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE111.HTM HTTP/1.0" 200 1431 -129.192.97.6 - - [28/Feb/2008:06:55:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE112.HTM HTTP/1.0" 200 987 -129.192.97.6 - - [28/Feb/2008:06:55:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE113.HTM HTTP/1.0" 200 1134 -129.192.97.6 - - [28/Feb/2008:06:55:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE114.HTM HTTP/1.0" 200 1821 -129.192.97.6 - - [28/Feb/2008:06:55:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE115.HTM HTTP/1.0" 200 1596 -129.192.97.6 - - [28/Feb/2008:06:55:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE116.HTM HTTP/1.0" 200 1585 -129.192.97.6 - - [28/Feb/2008:06:55:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE117.HTM HTTP/1.0" 200 979 -129.192.97.6 - - [28/Feb/2008:06:55:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE118.HTM HTTP/1.0" 200 1437 -38.98.120.84 - - [28/Feb/2008:06:55:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [28/Feb/2008:06:55:25 -0600] "GET / HTTP/1.1" 200 4447 -129.192.97.6 - - [28/Feb/2008:06:55:38 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 -129.192.97.6 - - [28/Feb/2008:06:55:45 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE120.HTM HTTP/1.0" 200 1308 -129.192.97.6 - - [28/Feb/2008:06:55:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE121.HTM HTTP/1.0" 200 1316 -129.192.97.6 - - [28/Feb/2008:06:56:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE122.HTM HTTP/1.0" 200 1297 -129.192.97.6 - - [28/Feb/2008:06:56:13 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE123.HTM HTTP/1.0" 200 1195 -129.192.97.6 - - [28/Feb/2008:06:56:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE124.HTM HTTP/1.0" 200 1355 -129.192.97.6 - - [28/Feb/2008:06:56:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE125.HTM HTTP/1.0" 200 978 -129.192.97.6 - - [28/Feb/2008:06:56:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE126.HTM HTTP/1.0" 200 1555 -129.192.97.6 - - [28/Feb/2008:06:56:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE127.HTM HTTP/1.0" 404 133 -129.192.97.6 - - [28/Feb/2008:06:56:32 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.0" 403 245 -38.98.120.84 - - [28/Feb/2008:06:57:28 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:57:29 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:57:30 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:57:31 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:57:32 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:57:33 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:57:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:06:57:35 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -38.98.120.84 - - [28/Feb/2008:06:57:37 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:57:37 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:57:38 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:06:57:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:57:40 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -38.98.120.84 - - [28/Feb/2008:06:57:42 -0600] "GET /python.html HTTP/1.1" 200 18870 -192.88.162.35 - - [28/Feb/2008:06:57:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -38.98.120.84 - - [28/Feb/2008:06:57:42 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:57:44 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:57:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -38.98.120.84 - - [28/Feb/2008:06:57:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:57:46 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:57:47 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:57:48 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -129.192.97.6 - - [28/Feb/2008:06:57:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE001.HTM HTTP/1.0" 200 1225 -38.98.120.84 - - [28/Feb/2008:06:57:50 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:57:51 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 -38.98.120.84 - - [28/Feb/2008:06:58:02 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:58:04 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -38.98.120.84 - - [28/Feb/2008:06:58:04 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -38.98.120.84 - - [28/Feb/2008:06:58:05 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -38.98.120.84 - - [28/Feb/2008:06:58:06 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:58:07 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:58:08 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:58:09 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:06:58:10 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:58:11 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:06:58:12 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:58:13 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:58:16 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:58:17 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:58:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:58:19 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:58:20 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:58:21 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:58:22 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:58:23 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:58:24 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:58:25 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:58:26 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -38.98.120.84 - - [28/Feb/2008:06:58:27 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:58:28 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 -38.98.120.84 - - [28/Feb/2008:06:58:29 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:58:30 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:58:31 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 -38.98.120.84 - - [28/Feb/2008:06:58:32 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:58:33 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:58:34 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:58:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:58:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -38.98.120.84 - - [28/Feb/2008:06:58:37 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:58:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:06:58:39 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:58:40 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:06:58:41 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:58:42 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -38.98.120.84 - - [28/Feb/2008:06:58:43 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:58:44 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:58:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:58:46 -0600] "GET /publications.html HTTP/1.1" 200 7758 -38.98.120.84 - - [28/Feb/2008:06:58:47 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:58:48 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 -38.98.120.84 - - [28/Feb/2008:06:58:49 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.1" 200 399 -38.98.120.84 - - [28/Feb/2008:06:58:49 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:58:51 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:58:51 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:58:53 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:58:54 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:58:55 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:58:56 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:58:57 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 -38.98.120.84 - - [28/Feb/2008:06:58:58 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:58:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -38.98.120.84 - - [28/Feb/2008:06:59:00 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:59:01 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:59:02 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -38.98.120.84 - - [28/Feb/2008:06:59:02 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:06:59:03 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:59:04 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:59:06 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -38.98.120.84 - - [28/Feb/2008:06:59:06 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:59:07 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:59:08 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.1" 200 293 -38.98.120.84 - - [28/Feb/2008:06:59:12 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:59:12 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:06:59:13 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:59:14 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:06:59:15 -0600] "GET /swill/training.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:06:59:16 -0600] "GET /swill/about.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:06:59:17 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:59:18 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:59:19 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -38.98.120.84 - - [28/Feb/2008:06:59:21 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 -38.98.120.84 - - [28/Feb/2008:06:59:22 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 -38.98.120.84 - - [28/Feb/2008:06:59:23 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:59:24 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:59:25 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:59:26 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:59:27 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:59:28 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:59:29 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 -38.98.120.84 - - [28/Feb/2008:06:59:30 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:59:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:59:32 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:59:33 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:06:59:34 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:59:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:06:59:36 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:59:37 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:59:38 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -38.98.120.84 - - [28/Feb/2008:06:59:39 -0600] "GET /cv.html HTTP/1.1" 200 31798 -38.98.120.84 - - [28/Feb/2008:06:59:40 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:59:41 -0600] "GET /swill/consulting.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:06:59:42 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:06:59:43 -0600] "GET /diet.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:06:59:45 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 -38.98.120.84 - - [28/Feb/2008:06:59:45 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:59:46 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -38.98.120.84 - - [28/Feb/2008:06:59:47 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.1" 200 375 -38.98.120.84 - - [28/Feb/2008:06:59:48 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:06:59:52 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 -38.98.120.84 - - [28/Feb/2008:06:59:53 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:59:54 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:06:59:55 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:06:59:56 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:06:59:57 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -38.98.120.84 - - [28/Feb/2008:06:59:58 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:06:59:59 -0600] "GET /ply/README HTTP/1.1" 200 8605 -38.98.120.84 - - [28/Feb/2008:07:00:00 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 -38.98.120.84 - - [28/Feb/2008:07:00:01 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:07:00:02 -0600] "GET /swill/software.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:00:03 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:00:04 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:07:00:05 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:00:06 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:07:00:07 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 -38.98.120.84 - - [28/Feb/2008:07:00:08 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 -38.98.120.84 - - [28/Feb/2008:07:00:09 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:00:10 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:00:23 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 122740 -86.197.225.66 - - [28/Feb/2008:07:00:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -86.197.225.66 - - [28/Feb/2008:07:00:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:00:56 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 112604 -38.98.120.84 - - [28/Feb/2008:07:01:16 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:17 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:07:01:18 -0600] "GET /cv.html HTTP/1.1" 200 31798 -38.98.120.84 - - [28/Feb/2008:07:01:19 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:07:01:20 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:07:01:21 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:07:01:22 -0600] "GET /publications.html HTTP/1.1" 200 7758 -38.98.120.84 - - [28/Feb/2008:07:01:23 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:07:01:24 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:07:01:25 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 -38.98.120.84 - - [28/Feb/2008:07:01:26 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:07:01:27 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 -38.98.120.84 - - [28/Feb/2008:07:01:30 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:07:01:30 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [28/Feb/2008:07:01:31 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:32 -0600] "GET /about HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:01:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -38.98.120.84 - - [28/Feb/2008:07:01:34 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -38.98.120.84 - - [28/Feb/2008:07:01:35 -0600] "GET /assign5.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:01:36 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:01:36 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:01:37 -0600] "GET /dynamic/assign5 HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:01:38 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:39 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 -38.98.120.84 - - [28/Feb/2008:07:01:40 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 -38.98.120.84 - - [28/Feb/2008:07:01:41 -0600] "GET /Doc/index.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:01:42 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -38.98.120.84 - - [28/Feb/2008:07:01:43 -0600] "GET /swill/Doc HTTP/1.1" 301 248 -38.98.120.84 - - [28/Feb/2008:07:01:43 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 -38.98.120.84 - - [28/Feb/2008:07:01:44 -0600] "GET /swill/Doc/index HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:01:45 -0600] "GET /swill HTTP/1.1" 301 244 -38.98.120.84 - - [28/Feb/2008:07:01:45 -0600] "GET /swill/ HTTP/1.1" 200 3786 -38.98.120.84 - - [28/Feb/2008:07:01:46 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:47 -0600] "GET /swill/Doc HTTP/1.1" 301 248 -38.98.120.84 - - [28/Feb/2008:07:01:47 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 -38.98.120.84 - - [28/Feb/2008:07:01:48 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -38.98.120.84 - - [28/Feb/2008:07:01:49 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -38.98.120.84 - - [28/Feb/2008:07:01:50 -0600] "GET /swill/index HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:01:51 -0600] "GET /swill HTTP/1.1" 301 244 -38.98.120.84 - - [28/Feb/2008:07:01:51 -0600] "GET /swill/ HTTP/1.1" 200 3786 -38.98.120.84 - - [28/Feb/2008:07:01:52 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:53 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:54 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:55 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:56 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:01:57 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 -38.98.120.84 - - [28/Feb/2008:07:01:58 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 -38.98.120.84 - - [28/Feb/2008:07:01:59 -0600] "GET /dynamic/sd HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:00 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:02:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:02:01 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:02 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -38.98.120.84 - - [28/Feb/2008:07:02:03 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -65.55.208.118 - - [28/Feb/2008:07:02:04 -0600] "GET /photos/u505/pages/IMG_1510.htm HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:04 -0600] "GET /dynamic/smackdown HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:05 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:02:05 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:02:06 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:07 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 -38.98.120.84 - - [28/Feb/2008:07:02:08 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 -38.98.120.84 - - [28/Feb/2008:07:02:09 -0600] "GET /papers/Py97/beazley HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:10 -0600] "GET /Py97/beazley.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:11 -0600] "GET /papers/beazley.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:12 -0600] "GET /papers/Py97 HTTP/1.1" 301 250 -38.98.120.84 - - [28/Feb/2008:07:02:12 -0600] "GET /papers/Py97/ HTTP/1.1" 403 222 -38.98.120.84 - - [28/Feb/2008:07:02:13 -0600] "GET /papers HTTP/1.1" 301 245 -38.98.120.84 - - [28/Feb/2008:07:02:13 -0600] "GET /papers/ HTTP/1.1" 403 217 -38.98.120.84 - - [28/Feb/2008:07:02:14 -0600] "GET /beazley.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:15 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:07:02:16 -0600] "GET /training.html HTTP/1.1" 200 6154 -38.98.120.84 - - [28/Feb/2008:07:02:17 -0600] "GET /training HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:18 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 -38.98.120.84 - - [28/Feb/2008:07:02:19 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 -38.98.120.84 - - [28/Feb/2008:07:02:20 -0600] "GET /papers HTTP/1.1" 301 245 -38.98.120.84 - - [28/Feb/2008:07:02:20 -0600] "GET /papers/ HTTP/1.1" 403 217 -38.98.120.84 - - [28/Feb/2008:07:02:21 -0600] "GET /papers/Perl98/swigperl HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:22 -0600] "GET /papers/swigperl.htm HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:23 -0600] "GET /papers/Perl98 HTTP/1.1" 301 252 -38.98.120.84 - - [28/Feb/2008:07:02:23 -0600] "GET /papers/Perl98/ HTTP/1.1" 403 224 -38.98.120.84 - - [28/Feb/2008:07:02:24 -0600] "GET /swigperl.htm HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:25 -0600] "GET /Perl98/swigperl.htm HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:02:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:02:28 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:02:28 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:02:29 -0600] "GET /dynamic/index HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:30 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:31 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -38.98.120.84 - - [28/Feb/2008:07:02:32 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -38.98.120.84 - - [28/Feb/2008:07:02:37 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:02:37 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:02:38 -0600] "GET /dynamic/assign3 HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:39 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:40 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:41 -0600] "GET /index.html HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:42 -0600] "GET /index HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:43 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:07:02:44 -0600] "GET /software.html HTTP/1.1" 200 3163 -38.98.120.84 - - [28/Feb/2008:07:02:45 -0600] "GET /software HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:46 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:47 -0600] "GET /ply/README HTTP/1.1" 200 8605 -38.98.120.84 - - [28/Feb/2008:07:02:48 -0600] "GET /ply/README HTTP/1.1" 200 8605 -38.98.120.84 - - [28/Feb/2008:07:02:49 -0600] "GET /README HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:50 -0600] "GET /ply HTTP/1.1" 301 242 -38.98.120.84 - - [28/Feb/2008:07:02:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:02:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:02:52 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:53 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -38.98.120.84 - - [28/Feb/2008:07:02:54 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -38.98.120.84 - - [28/Feb/2008:07:02:55 -0600] "GET /ply HTTP/1.1" 301 242 -38.98.120.84 - - [28/Feb/2008:07:02:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:02:56 -0600] "GET /ply/support HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:02:57 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:02:58 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -38.98.120.84 - - [28/Feb/2008:07:02:59 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -38.98.120.84 - - [28/Feb/2008:07:03:00 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:03:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:03:01 -0600] "GET /dynamic/assign4 HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:02 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:03:03 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:07:03:04 -0600] "GET /writing.html HTTP/1.1" 200 2871 -38.98.120.84 - - [28/Feb/2008:07:03:05 -0600] "GET /writing HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:06 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -38.98.120.84 - - [28/Feb/2008:07:03:07 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -38.98.120.84 - - [28/Feb/2008:07:03:08 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:03:08 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:03:09 -0600] "GET /dynamic/syllabus HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:10 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:03:11 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 -38.98.120.84 - - [28/Feb/2008:07:03:12 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 -38.98.120.84 - - [28/Feb/2008:07:03:13 -0600] "GET /papers HTTP/1.1" 301 245 -38.98.120.84 - - [28/Feb/2008:07:03:13 -0600] "GET /papers/ HTTP/1.1" 403 217 -38.98.120.84 - - [28/Feb/2008:07:03:14 -0600] "GET /papers/python.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:15 -0600] "GET /papers/Python2001 HTTP/1.1" 301 256 -38.98.120.84 - - [28/Feb/2008:07:03:15 -0600] "GET /papers/Python2001/ HTTP/1.1" 403 228 -38.98.120.84 - - [28/Feb/2008:07:03:16 -0600] "GET /papers/Python2001/python HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:17 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:07:03:18 -0600] "GET /Python2001/python.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:19 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 -38.98.120.84 - - [28/Feb/2008:07:03:20 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 -38.98.120.84 - - [28/Feb/2008:07:03:21 -0600] "GET /papers/tcl96.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:22 -0600] "GET /papers HTTP/1.1" 301 245 -38.98.120.84 - - [28/Feb/2008:07:03:22 -0600] "GET /papers/ HTTP/1.1" 403 217 -38.98.120.84 - - [28/Feb/2008:07:03:23 -0600] "GET /papers/Tcl96 HTTP/1.1" 301 251 -38.98.120.84 - - [28/Feb/2008:07:03:23 -0600] "GET /papers/Tcl96/ HTTP/1.1" 403 223 -38.98.120.84 - - [28/Feb/2008:07:03:24 -0600] "GET /papers/Tcl96/tcl96 HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:29 -0600] "GET /tcl96.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:30 -0600] "GET /Tcl96/tcl96.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:31 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -38.98.120.84 - - [28/Feb/2008:07:03:32 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 -38.98.120.84 - - [28/Feb/2008:07:03:33 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:03:34 -0600] "GET /per HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:35 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -38.98.120.84 - - [28/Feb/2008:07:03:38 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -38.98.120.84 - - [28/Feb/2008:07:03:38 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:03:39 -0600] "GET /sysop HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:40 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -38.98.120.84 - - [28/Feb/2008:07:03:41 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -38.98.120.84 - - [28/Feb/2008:07:03:42 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:03:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:03:43 -0600] "GET /dynamic/assign2 HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:44 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:03:45 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 -38.98.120.84 - - [28/Feb/2008:07:03:46 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 -38.98.120.84 - - [28/Feb/2008:07:03:47 -0600] "GET /papers/python96.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:48 -0600] "GET /papers HTTP/1.1" 301 245 -38.98.120.84 - - [28/Feb/2008:07:03:48 -0600] "GET /papers/ HTTP/1.1" 403 217 -38.98.120.84 - - [28/Feb/2008:07:03:49 -0600] "GET /papers/Py96 HTTP/1.1" 301 250 -38.98.120.84 - - [28/Feb/2008:07:03:49 -0600] "GET /papers/Py96/ HTTP/1.1" 403 222 -38.98.120.84 - - [28/Feb/2008:07:03:51 -0600] "GET /papers/Py96/python96 HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:51 -0600] "GET /python96.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:03:52 -0600] "GET /Py96/python96.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:04:04 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -38.98.120.84 - - [28/Feb/2008:07:04:05 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 -38.98.120.84 - - [28/Feb/2008:07:04:06 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:04:06 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:04:07 -0600] "GET /dynamic/assign1 HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:04:08 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:04:09 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -38.98.120.84 - - [28/Feb/2008:07:04:10 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 -38.98.120.84 - - [28/Feb/2008:07:04:11 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:04:11 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:04:26 -0600] "GET /dynamic/soln1 HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:04:27 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:04:28 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 -38.98.120.84 - - [28/Feb/2008:07:04:35 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 -38.98.120.84 - - [28/Feb/2008:07:04:48 -0600] "GET /dynamic HTTP/1.1" 301 246 -38.98.120.84 - - [28/Feb/2008:07:04:48 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -38.98.120.84 - - [28/Feb/2008:07:04:49 -0600] "GET /dynamic/dowstocks HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:04:50 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:04:51 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:04:52 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:04:53 -0600] "GET /ply HTTP/1.1" 301 242 -38.98.120.84 - - [28/Feb/2008:07:04:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:04:56 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 -38.98.120.84 - - [28/Feb/2008:07:04:57 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 -38.98.120.84 - - [28/Feb/2008:07:04:58 -0600] "GET /papers/TclChap.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:04:59 -0600] "GET /papers HTTP/1.1" 301 245 -38.98.120.84 - - [28/Feb/2008:07:04:59 -0600] "GET /papers/ HTTP/1.1" 403 217 -38.98.120.84 - - [28/Feb/2008:07:05:00 -0600] "GET /papers/Tcl98 HTTP/1.1" 301 251 -38.98.120.84 - - [28/Feb/2008:07:05:00 -0600] "GET /papers/Tcl98/ HTTP/1.1" 403 223 -38.98.120.84 - - [28/Feb/2008:07:05:01 -0600] "GET /papers/Tcl98/TclChap HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:02 -0600] "GET /TclChap.html HTTP/1.1" 404 133 -60.50.9.191 - - [28/Feb/2008:07:05:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:11 -0600] "GET /Tcl98/TclChap.html HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:13 -0600] "GET /cv.html HTTP/1.1" 200 31798 -38.98.120.84 - - [28/Feb/2008:07:05:14 -0600] "GET /cv.html HTTP/1.1" 200 31798 -38.98.120.84 - - [28/Feb/2008:07:05:14 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:05:24 -0600] "GET /cv HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:26 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 -200.19.92.58 - - [28/Feb/2008:07:05:27 -0600] "GET /ply/ HTTP/1.0" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:05:27 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 -200.19.92.58 - - [28/Feb/2008:07:05:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -200.19.92.58 - - [28/Feb/2008:07:05:28 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:28 -0600] "GET /swill HTTP/1.1" 301 244 -38.98.120.84 - - [28/Feb/2008:07:05:28 -0600] "GET /swill/ HTTP/1.1" 200 3786 -38.98.120.84 - - [28/Feb/2008:07:05:29 -0600] "GET /swill/exec HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:30 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:05:31 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -38.98.120.84 - - [28/Feb/2008:07:05:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -38.98.120.84 - - [28/Feb/2008:07:05:33 -0600] "GET /ply HTTP/1.1" 301 242 -38.98.120.84 - - [28/Feb/2008:07:05:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:05:34 -0600] "GET /ply/ply HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:35 -0600] "GET /publications.html HTTP/1.1" 200 7758 -38.98.120.84 - - [28/Feb/2008:07:05:36 -0600] "GET /publications.html HTTP/1.1" 200 7758 -38.98.120.84 - - [28/Feb/2008:07:05:37 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:05:38 -0600] "GET /publications HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:07:05:40 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -38.98.120.84 - - [28/Feb/2008:07:05:41 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [28/Feb/2008:07:05:42 -0600] "GET /consulting HTTP/1.1" 404 133 -38.98.120.84 - - [28/Feb/2008:07:05:43 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:07:05:44 -0600] "GET /python.html HTTP/1.1" 200 18870 -38.98.120.84 - - [28/Feb/2008:07:05:47 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply HTTP/1.1" 301 242 -38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 -38.98.120.84 - - [28/Feb/2008:07:05:52 -0600] "GET /ply/example HTTP/1.1" 404 133 -74.6.25.234 - - [28/Feb/2008:07:06:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE003.HTM HTTP/1.0" 304 - -200.19.92.58 - - [28/Feb/2008:07:09:17 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -62.161.167.222 - - [28/Feb/2008:07:09:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET / HTTP/1.1" 200 4447 -68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.252.229.225 - - [28/Feb/2008:07:09:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -68.252.229.225 - - [28/Feb/2008:07:09:28 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -65.55.208.118 - - [28/Feb/2008:07:09:38 -0600] "GET /photos/u505/pages/IMG_1538.htm HTTP/1.1" 404 133 -74.6.24.10 - - [28/Feb/2008:07:11:23 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.0" 304 - -65.55.208.117 - - [28/Feb/2008:07:13:44 -0600] "GET /photos/wind/pages/IMG_1278.htm HTTP/1.1" 404 133 -65.55.208.117 - - [28/Feb/2008:07:13:44 -0600] "GET /photos/wind/pages/IMG_1256.htm HTTP/1.1" 404 133 -65.55.208.117 - - [28/Feb/2008:07:13:45 -0600] "GET /photos/wind/pages/IMG_1304.htm HTTP/1.1" 404 133 -65.55.208.117 - - [28/Feb/2008:07:14:41 -0600] "GET /photos/wind/pages/IMG_1295.htm HTTP/1.1" 404 133 -193.174.238.115 - - [28/Feb/2008:07:14:52 -0600] "GET /ply/ HTTP/1.0" 200 8018 -193.174.238.115 - - [28/Feb/2008:07:14:52 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -193.174.238.115 - - [28/Feb/2008:07:14:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -71.201.41.248 - - [28/Feb/2008:07:17:03 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -71.201.41.248 - - [28/Feb/2008:07:17:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.41.248 - - [28/Feb/2008:07:17:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.41.248 - - [28/Feb/2008:07:17:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -65.214.45.129 - - [28/Feb/2008:07:17:23 -0600] "GET /python/per_secrets.html HTTP/1.0" 404 133 -60.50.9.191 - - [28/Feb/2008:07:19:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -60.50.9.191 - - [28/Feb/2008:07:21:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:07:24:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.161.167.222 - - [28/Feb/2008:07:24:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.161.167.222 - - [28/Feb/2008:07:24:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:07:24:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.127.117.160 - - [28/Feb/2008:07:24:42 -0600] "GET /ply/ HTTP/1.0" 200 8018 -82.127.117.160 - - [28/Feb/2008:07:24:42 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -59.96.12.231 - - [28/Feb/2008:07:26:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -59.96.12.231 - - [28/Feb/2008:07:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.127.117.160 - - [28/Feb/2008:07:27:09 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -82.127.117.160 - - [28/Feb/2008:07:28:11 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -217.10.60.85 - - [28/Feb/2008:07:30:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -217.10.60.85 - - [28/Feb/2008:07:30:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.10.60.85 - - [28/Feb/2008:07:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -217.10.60.85 - - [28/Feb/2008:07:30:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -217.10.60.85 - - [28/Feb/2008:07:31:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -217.10.60.85 - - [28/Feb/2008:07:31:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -217.10.60.85 - - [28/Feb/2008:07:31:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -217.10.60.85 - - [28/Feb/2008:07:31:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 -217.10.60.85 - - [28/Feb/2008:07:31:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -60.50.9.191 - - [28/Feb/2008:07:32:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.10.60.85 - - [28/Feb/2008:07:32:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Tcl HTTP/1.1" 200 1399 -217.10.60.85 - - [28/Feb/2008:07:32:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 -217.10.60.85 - - [28/Feb/2008:07:32:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -65.214.45.129 - - [28/Feb/2008:07:41:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE045.HTM HTTP/1.0" 200 1333 -68.252.229.225 - - [28/Feb/2008:07:44:31 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -128.141.224.186 - - [28/Feb/2008:07:46:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.141.224.186 - - [28/Feb/2008:07:46:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.141.224.186 - - [28/Feb/2008:07:46:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.141.224.186 - - [28/Feb/2008:07:47:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -83.170.97.200 - - [28/Feb/2008:07:47:35 -0600] "GET / HTTP/1.0" 200 4447 -83.170.97.200 - - [28/Feb/2008:07:47:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -83.170.97.200 - - [28/Feb/2008:07:47:36 -0600] "GET /training.html HTTP/1.0" 200 6154 -83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /python.html HTTP/1.0" 200 18870 -83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 -83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /index.html HTTP/1.0" 200 4447 -83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /writing.html HTTP/1.0" 200 2871 -83.170.97.200 - - [28/Feb/2008:07:47:38 -0600] "GET /about.html HTTP/1.0" 200 7890 -83.170.97.200 - - [28/Feb/2008:07:47:38 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 -83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /dynamic/smackdown.py HTTP/1.0" 200 1981 -83.170.97.200 - - [28/Feb/2008:07:47:40 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 -83.170.97.200 - - [28/Feb/2008:07:47:40 -0600] "GET /sysop.html HTTP/1.0" 200 1760 -83.170.97.200 - - [28/Feb/2008:07:47:41 -0600] "GET /cv.html HTTP/1.0" 200 31798 -83.170.97.200 - - [28/Feb/2008:07:47:41 -0600] "GET /dynamic/portfolio.txt HTTP/1.0" 200 100 -82.95.165.247 - - [28/Feb/2008:07:48:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.95.165.247 - - [28/Feb/2008:07:48:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -82.95.165.247 - - [28/Feb/2008:07:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.95.165.247 - - [28/Feb/2008:07:49:08 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -201.236.226.90 - - [28/Feb/2008:07:51:01 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -82.95.165.247 - - [28/Feb/2008:07:53:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -80.200.221.74 - - [28/Feb/2008:07:57:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.200.221.74 - - [28/Feb/2008:07:57:34 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -80.200.221.74 - - [28/Feb/2008:07:57:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -80.200.221.74 - - [28/Feb/2008:07:57:43 -0600] "GET /cgi-bin/wiki.pl?RenameDirective HTTP/1.1" 200 3672 -83.12.146.250 - - [28/Feb/2008:08:00:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.196.43.134 - - [28/Feb/2008:08:05:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.79.171.46 - - [28/Feb/2008:08:07:17 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ModuleDirective HTTP/1.1" 200 2302 -222.221.6.144 - - [28/Feb/2008:08:07:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -24.1.159.241 - - [28/Feb/2008:08:08:05 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -24.1.159.241 - - [28/Feb/2008:08:08:20 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 -80.200.221.74 - - [28/Feb/2008:08:09:20 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 -69.147.83.113 - - [28/Feb/2008:08:10:34 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -194.209.8.145 - - [28/Feb/2008:08:11:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -194.209.8.145 - - [28/Feb/2008:08:11:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.209.8.145 - - [28/Feb/2008:08:11:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -194.209.8.145 - - [28/Feb/2008:08:11:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -219.136.230.60 - - [28/Feb/2008:08:14:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 - -220.150.154.145 - - [28/Feb/2008:08:14:20 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -220.150.154.145 - - [28/Feb/2008:08:14:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.200.221.74 - - [28/Feb/2008:08:16:22 -0600] "GET /cgi-bin/wiki.pl?DefineDirective HTTP/1.1" 200 2760 -80.200.221.74 - - [28/Feb/2008:08:16:39 -0600] "GET /cgi-bin/wiki.pl?FeatureDirective HTTP/1.1" 200 4535 -208.51.93.163 - - [28/Feb/2008:08:16:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -208.51.93.163 - - [28/Feb/2008:08:16:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.200.221.74 - - [28/Feb/2008:08:16:53 -0600] "GET /cgi-bin/wiki.pl?NameDirective HTTP/1.1" 200 1785 -80.200.221.74 - - [28/Feb/2008:08:17:05 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 -80.200.221.74 - - [28/Feb/2008:08:17:08 -0600] "GET /cgi-bin/wiki.pl?TypemapDirective HTTP/1.1" 200 1584 -65.55.212.77 - - [28/Feb/2008:08:19:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.19.101 - - [28/Feb/2008:08:20:10 -0600] "GET /swill/Doc/ HTTP/1.0" 304 - -82.127.117.160 - - [28/Feb/2008:08:21:46 -0600] "GET /ply HTTP/1.0" 301 230 -138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 -138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -138.100.218.23 - - [28/Feb/2008:08:27:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.26.119 - - [28/Feb/2008:08:28:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE087.HTM HTTP/1.0" 200 1575 -193.128.72.68 - - [28/Feb/2008:08:28:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 -193.128.72.68 - - [28/Feb/2008:08:28:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -193.128.72.68 - - [28/Feb/2008:08:28:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -138.100.218.23 - - [28/Feb/2008:08:29:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -138.100.218.23 - - [28/Feb/2008:08:30:01 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -138.100.218.23 - - [28/Feb/2008:08:30:09 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.201.41.248 - - [28/Feb/2008:08:33:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -69.217.73.2 - - [28/Feb/2008:08:34:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 -69.217.73.2 - - [28/Feb/2008:08:34:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -75.21.88.201 - - [28/Feb/2008:08:35:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -75.21.88.201 - - [28/Feb/2008:08:35:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.21.88.201 - - [28/Feb/2008:08:35:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.21.88.201 - - [28/Feb/2008:08:35:04 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -69.217.73.2 - - [28/Feb/2008:08:36:16 -0600] "GET / HTTP/1.1" 200 4447 -69.217.73.2 - - [28/Feb/2008:08:36:17 -0600] "GET /index.html HTTP/1.1" 200 4447 -69.217.73.2 - - [28/Feb/2008:08:36:18 -0600] "GET /training.html HTTP/1.1" 200 6154 -69.217.73.2 - - [28/Feb/2008:08:36:18 -0600] "GET /software.html HTTP/1.1" 200 3163 -69.217.73.2 - - [28/Feb/2008:08:36:20 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -69.217.73.2 - - [28/Feb/2008:08:36:21 -0600] "GET /writing.html HTTP/1.1" 200 2871 -69.217.73.2 - - [28/Feb/2008:08:36:22 -0600] "GET /about.html HTTP/1.1" 200 7890 -69.217.73.2 - - [28/Feb/2008:08:36:22 -0600] "GET /python.html HTTP/1.1" 200 18870 -69.217.73.2 - - [28/Feb/2008:08:36:24 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -69.217.73.2 - - [28/Feb/2008:08:36:25 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.229.38.64 - - [28/Feb/2008:08:44:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.229.38.64 - - [28/Feb/2008:08:44:48 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -80.229.38.64 - - [28/Feb/2008:08:44:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 -64.124.85.71 - - [28/Feb/2008:08:45:09 -0600] "GET /robots.txt HTTP/1.1" 200 71 -82.95.165.247 - - [28/Feb/2008:08:52:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -216.201.139.126 - - [28/Feb/2008:08:54:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -82.127.117.160 - - [28/Feb/2008:08:55:29 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 -208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:09:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.78.145.1 - - [28/Feb/2008:09:03:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -61.48.58.106 - - [28/Feb/2008:09:05:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -61.48.58.106 - - [28/Feb/2008:09:05:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:09:06:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:09:07:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:09:07:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:09:07:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.161.167.222 - - [28/Feb/2008:09:07:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.138.208.6 - - [28/Feb/2008:09:11:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -193.138.208.6 - - [28/Feb/2008:09:11:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -193.138.208.6 - - [28/Feb/2008:09:11:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.138.208.6 - - [28/Feb/2008:09:11:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.174 - - [28/Feb/2008:09:14:22 -0600] "GET /ply/ HTTP/1.0" 304 - -128.135.139.150 - - [28/Feb/2008:09:16:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -128.135.139.150 - - [28/Feb/2008:09:16:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.139.150 - - [28/Feb/2008:09:17:14 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -193.138.208.6 - - [28/Feb/2008:09:17:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -193.0.96.15 - - [28/Feb/2008:09:20:41 -0600] "GET /ply/ HTTP/1.0" 200 8018 -193.0.96.15 - - [28/Feb/2008:09:20:41 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -193.0.96.15 - - [28/Feb/2008:09:20:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -193.0.96.15 - - [28/Feb/2008:09:21:26 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -193.0.96.15 - - [28/Feb/2008:09:21:37 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -128.221.197.20 - - [28/Feb/2008:09:25:33 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -150.210.155.167 - - [28/Feb/2008:09:25:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -62.161.167.222 - - [28/Feb/2008:09:26:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.21.20.162 - - [28/Feb/2008:09:27:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -65.55.208.119 - - [28/Feb/2008:09:28:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE038.HTM HTTP/1.1" 304 - -193.0.96.15 - - [28/Feb/2008:09:32:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -129.194.8.73 - - [28/Feb/2008:09:32:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -129.194.8.73 - - [28/Feb/2008:09:33:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -129.194.8.73 - - [28/Feb/2008:09:33:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.194.8.73 - - [28/Feb/2008:09:33:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.194.8.73 - - [28/Feb/2008:09:33:07 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -84.168.117.112 - - [28/Feb/2008:09:35:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -84.168.117.112 - - [28/Feb/2008:09:35:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /python.html HTTP/1.1" 200 18870 -70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -129.97.120.226 - - [28/Feb/2008:09:37:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 -129.97.120.226 - - [28/Feb/2008:09:37:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.97.120.226 - - [28/Feb/2008:09:37:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -129.97.120.226 - - [28/Feb/2008:09:37:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -84.168.117.112 - - [28/Feb/2008:09:37:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -84.168.117.112 - - [28/Feb/2008:09:37:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -195.80.22.103 - - [28/Feb/2008:09:39:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -195.80.22.103 - - [28/Feb/2008:09:39:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -195.80.22.103 - - [28/Feb/2008:09:39:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -195.80.22.103 - - [28/Feb/2008:09:40:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -195.80.22.103 - - [28/Feb/2008:09:40:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -129.194.8.73 - - [28/Feb/2008:09:40:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [28/Feb/2008:09:41:33 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -98.193.69.179 - - [28/Feb/2008:09:41:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [28/Feb/2008:09:42:51 -0600] "GET / HTTP/1.1" 200 4447 -201.236.226.90 - - [28/Feb/2008:09:42:53 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -201.236.226.90 - - [28/Feb/2008:09:42:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [28/Feb/2008:09:42:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [28/Feb/2008:09:42:56 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -88.80.205.215 - - [28/Feb/2008:09:43:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 -88.80.205.215 - - [28/Feb/2008:09:43:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -98.193.69.179 - - [28/Feb/2008:09:43:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [28/Feb/2008:09:43:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -193.0.96.15 - - [28/Feb/2008:09:44:21 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -193.0.96.15 - - [28/Feb/2008:09:44:24 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -65.55.208.119 - - [28/Feb/2008:09:46:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE018.HTM HTTP/1.1" 304 - -65.55.208.119 - - [28/Feb/2008:09:46:38 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE012.HTM HTTP/1.1" 304 - -65.55.208.119 - - [28/Feb/2008:09:46:58 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE028.HTM HTTP/1.1" 304 - -65.55.208.119 - - [28/Feb/2008:09:47:00 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE119.HTM HTTP/1.1" 304 - -82.119.242.94 - - [28/Feb/2008:09:48:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 -82.119.242.94 - - [28/Feb/2008:09:48:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -82.119.242.94 - - [28/Feb/2008:09:48:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.234.244.162 - - [28/Feb/2008:09:55:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.71.37.92 - - [28/Feb/2008:09:56:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 -212.71.37.92 - - [28/Feb/2008:09:56:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -212.71.37.92 - - [28/Feb/2008:09:56:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.76.29.2 - - [28/Feb/2008:09:56:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -194.76.29.2 - - [28/Feb/2008:09:56:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -194.76.29.2 - - [28/Feb/2008:09:57:21 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.0" 200 1773 -79.77.245.147 - - [28/Feb/2008:09:57:30 -0600] "GET /cv.html HTTP/1.1" 200 31798 -79.77.245.147 - - [28/Feb/2008:09:57:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -194.76.29.2 - - [28/Feb/2008:09:57:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -194.76.29.2 - - [28/Feb/2008:09:58:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.0" 200 2320 -194.76.29.2 - - [28/Feb/2008:09:58:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -194.76.29.2 - - [28/Feb/2008:09:58:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 -194.76.29.2 - - [28/Feb/2008:09:58:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 -194.76.29.2 - - [28/Feb/2008:09:58:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 -194.76.29.2 - - [28/Feb/2008:10:00:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.0" 200 2040 -194.76.29.2 - - [28/Feb/2008:10:01:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAIXSharedLibraries HTTP/1.0" 200 3494 -64.234.244.162 - - [28/Feb/2008:10:02:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.0.96.15 - - [28/Feb/2008:10:04:48 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -193.0.96.15 - - [28/Feb/2008:10:05:08 -0600] "GET /python.html HTTP/1.0" 200 18870 -193.0.96.15 - - [28/Feb/2008:10:05:09 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -64.236.139.6 - - [28/Feb/2008:10:07:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -64.236.139.6 - - [28/Feb/2008:10:07:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.236.139.6 - - [28/Feb/2008:10:07:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -64.236.139.6 - - [28/Feb/2008:10:07:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -193.0.96.15 - - [28/Feb/2008:10:07:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -64.236.139.6 - - [28/Feb/2008:10:07:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 -128.221.197.20 - - [28/Feb/2008:10:08:51 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -64.236.139.6 - - [28/Feb/2008:10:09:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 -64.236.139.6 - - [28/Feb/2008:10:09:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -64.236.139.6 - - [28/Feb/2008:10:10:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 -64.236.139.6 - - [28/Feb/2008:10:11:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 -64.236.139.6 - - [28/Feb/2008:10:13:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 -64.236.139.6 - - [28/Feb/2008:10:14:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -98.193.69.179 - - [28/Feb/2008:10:16:52 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 -89.189.65.147 - - [28/Feb/2008:10:17:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.189.65.147 - - [28/Feb/2008:10:18:09 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -132.168.9.102 - - [28/Feb/2008:10:23:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -132.168.9.102 - - [28/Feb/2008:10:23:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -132.168.9.102 - - [28/Feb/2008:10:23:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -132.168.9.102 - - [28/Feb/2008:10:23:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -132.168.9.102 - - [28/Feb/2008:10:23:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -194.76.29.2 - - [28/Feb/2008:10:24:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 -128.221.197.20 - - [28/Feb/2008:10:29:52 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -70.249.147.120 - - [28/Feb/2008:10:31:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -70.249.147.120 - - [28/Feb/2008:10:31:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -70.249.147.120 - - [28/Feb/2008:10:32:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.221.197.20 - - [28/Feb/2008:10:33:45 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -128.221.197.20 - - [28/Feb/2008:10:33:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -81.52.143.15 - - [28/Feb/2008:10:34:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 -81.52.143.15 - - [28/Feb/2008:10:34:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -193.158.76.56 - - [28/Feb/2008:10:36:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -193.158.76.56 - - [28/Feb/2008:10:36:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.158.76.56 - - [28/Feb/2008:10:36:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.158.76.56 - - [28/Feb/2008:10:36:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -193.158.76.56 - - [28/Feb/2008:10:36:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -193.158.76.56 - - [28/Feb/2008:10:36:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -193.158.76.56 - - [28/Feb/2008:10:36:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -193.158.76.56 - - [28/Feb/2008:10:36:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 -193.158.76.56 - - [28/Feb/2008:10:37:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.158.76.56 - - [28/Feb/2008:10:37:25 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 -207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -207.71.33.174 - - [28/Feb/2008:10:39:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -207.71.33.174 - - [28/Feb/2008:10:39:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -64.234.244.162 - - [28/Feb/2008:10:39:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.234.244.162 - - [28/Feb/2008:10:40:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.215.100.68 - - [28/Feb/2008:10:43:19 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -98.215.100.68 - - [28/Feb/2008:10:43:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -66.1.67.100 - - [28/Feb/2008:10:43:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.1.67.100 - - [28/Feb/2008:10:43:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -66.1.67.100 - - [28/Feb/2008:10:43:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.1.67.100 - - [28/Feb/2008:10:43:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.244.205.42 - - [28/Feb/2008:10:43:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -99.244.205.42 - - [28/Feb/2008:10:43:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -99.244.205.42 - - [28/Feb/2008:10:43:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.215.100.68 - - [28/Feb/2008:10:43:41 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 -99.244.205.42 - - [28/Feb/2008:10:43:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.215.100.68 - - [28/Feb/2008:10:43:49 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -99.244.205.42 - - [28/Feb/2008:10:43:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -99.244.205.42 - - [28/Feb/2008:10:43:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -208.36.144.7 - - [28/Feb/2008:10:44:03 -0600] "GET /robots.txt HTTP/1.0" 200 71 -66.1.67.100 - - [28/Feb/2008:10:44:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.232.113.62 - - [28/Feb/2008:10:45:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -148.233.159.58 - - [28/Feb/2008:10:45:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -66.1.67.100 - - [28/Feb/2008:10:45:45 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -99.244.205.42 - - [28/Feb/2008:10:46:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.149.16.155 - - [28/Feb/2008:10:49:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -80.149.16.155 - - [28/Feb/2008:10:50:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.149.16.155 - - [28/Feb/2008:10:50:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.26.212 - - [28/Feb/2008:10:50:06 -0600] "GET /sysop.html HTTP/1.0" 200 1760 -69.46.29.140 - - [28/Feb/2008:10:50:51 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2732 -80.97.94.178 - - [28/Feb/2008:10:50:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -128.221.197.20 - - [28/Feb/2008:10:52:34 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -189.24.63.240 - - [28/Feb/2008:10:53:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.24.63.240 - - [28/Feb/2008:10:53:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -189.24.63.240 - - [28/Feb/2008:10:53:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.221.197.20 - - [28/Feb/2008:10:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -65.166.139.21 - - [28/Feb/2008:10:55:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -65.166.139.21 - - [28/Feb/2008:10:55:25 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -65.166.139.21 - - [28/Feb/2008:10:55:32 -0600] "GET /cgi-bin/wiki.pl?DavidBeazley HTTP/1.1" 200 2019 -65.166.139.21 - - [28/Feb/2008:10:55:51 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 -65.166.139.21 - - [28/Feb/2008:10:56:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -65.166.139.21 - - [28/Feb/2008:10:56:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -65.166.139.21 - - [28/Feb/2008:10:57:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 -65.166.139.21 - - [28/Feb/2008:10:57:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.1" 200 2861 -65.166.139.21 - - [28/Feb/2008:10:57:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 2455 -65.166.139.21 - - [28/Feb/2008:10:58:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaqInstallSwigInDifferentDirectory HTTP/1.1" 200 2271 -65.166.139.21 - - [28/Feb/2008:10:58:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 -65.166.139.21 - - [28/Feb/2008:10:58:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -64.234.244.162 - - [28/Feb/2008:11:02:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -198.54.202.234 - - [28/Feb/2008:11:05:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -196.25.255.210 - - [28/Feb/2008:11:05:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -196.25.255.246 - - [28/Feb/2008:11:05:57 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 -196.25.255.210 - - [28/Feb/2008:11:06:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -196.25.255.210 - - [28/Feb/2008:11:07:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -196.25.255.210 - - [28/Feb/2008:11:07:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -198.54.202.218 - - [28/Feb/2008:11:07:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 -196.25.255.194 - - [28/Feb/2008:11:08:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -196.25.255.250 - - [28/Feb/2008:11:08:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -196.25.255.210 - - [28/Feb/2008:11:08:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -212.56.88.112 - - [28/Feb/2008:11:11:14 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -212.56.88.112 - - [28/Feb/2008:11:11:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -24.7.210.64 - - [28/Feb/2008:11:11:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.221.197.20 - - [28/Feb/2008:11:11:22 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -24.7.210.64 - - [28/Feb/2008:11:11:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.208.120 - - [28/Feb/2008:11:12:53 -0600] "GET /sysop.html HTTP/1.1" 200 1760 -192.109.190.88 - - [28/Feb/2008:11:13:59 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 -128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.24.249 - - [28/Feb/2008:11:15:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.24.249 - - [28/Feb/2008:11:15:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.208.118 - - [28/Feb/2008:11:20:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE062.HTM HTTP/1.1" 304 - -208.36.144.7 - - [28/Feb/2008:11:21:55 -0600] "GET /robots.txt HTTP/1.0" 200 71 -128.135.139.146 - - [28/Feb/2008:11:30:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - -65.214.45.101 - - [28/Feb/2008:11:34:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.214.45.101 - - [28/Feb/2008:11:34:10 -0600] "GET /ply/ HTTP/1.0" 200 8018 -65.55.208.117 - - [28/Feb/2008:11:34:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.1" 304 - -198.253.24.5 - - [28/Feb/2008:11:34:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -198.253.24.5 - - [28/Feb/2008:11:34:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -198.253.24.5 - - [28/Feb/2008:11:35:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 -76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.223.31.57 - - [28/Feb/2008:11:37:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -64.234.244.162 - - [28/Feb/2008:11:40:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.77.240.100 - - [28/Feb/2008:11:43:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 -151.77.240.100 - - [28/Feb/2008:11:43:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -151.77.240.100 - - [28/Feb/2008:11:43:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -52.128.30.11 - - [28/Feb/2008:11:46:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -67.195.58.158 - - [28/Feb/2008:12:02:22 -0600] "GET /ply/ply.html HTTP/1.0" 304 - -91.121.19.229 - - [28/Feb/2008:12:05:42 -0600] "GET / HTTP/1.0" 200 4447 -91.121.19.229 - - [28/Feb/2008:12:05:42 -0600] "GET /index.html HTTP/1.0" 200 4447 -91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /about.html HTTP/1.0" 200 7890 -91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /writing.html HTTP/1.0" 200 2871 -91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /software.html HTTP/1.0" 200 3163 -91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /python.html HTTP/1.0" 200 18870 -91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /training.html HTTP/1.0" 200 6154 -91.121.19.229 - - [28/Feb/2008:12:05:45 -0600] "GET /publications.html HTTP/1.0" 200 7758 -91.121.19.229 - - [28/Feb/2008:12:05:45 -0600] "GET /cv.html HTTP/1.0" 200 31798 -91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 -91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 -91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -91.121.19.229 - - [28/Feb/2008:12:05:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -91.121.19.229 - - [28/Feb/2008:12:05:48 -0600] "GET /papers/Py97/beazley.html HTTP/1.0" 200 31315 -91.121.19.229 - - [28/Feb/2008:12:05:48 -0600] "GET /swill/software.html HTTP/1.0" 404 133 -192.54.144.229 - - [28/Feb/2008:12:06:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -192.54.144.229 - - [28/Feb/2008:12:07:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -192.54.144.229 - - [28/Feb/2008:12:07:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.54.144.229 - - [28/Feb/2008:12:07:09 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -192.54.144.229 - - [28/Feb/2008:12:07:12 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -128.135.11.245 - - [28/Feb/2008:12:09:58 -0600] "GET /dynamic/ HTTP/1.0" 200 5313 -192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /python.html HTTP/1.0" 200 18870 -192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -64.234.244.162 - - [28/Feb/2008:12:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -64.234.244.162 - - [28/Feb/2008:12:16:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -65.55.232.14 - - [28/Feb/2008:12:18:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 -64.234.244.162 - - [28/Feb/2008:12:22:02 -0600] "GET /ply/README HTTP/1.1" 200 8605 -76.222.192.62 - - [28/Feb/2008:12:26:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 -76.222.192.62 - - [28/Feb/2008:12:26:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -76.222.192.62 - - [28/Feb/2008:12:26:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.56.210.71 - - [28/Feb/2008:12:26:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.56.210.71 - - [28/Feb/2008:12:26:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -74.56.210.71 - - [28/Feb/2008:12:26:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.15.187.198 - - [28/Feb/2008:12:28:18 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -24.15.187.198 - - [28/Feb/2008:12:28:25 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 -76.222.192.62 - - [28/Feb/2008:12:28:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -74.56.210.71 - - [28/Feb/2008:12:28:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.208.118 - - [28/Feb/2008:12:29:27 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE048.HTM HTTP/1.1" 304 - -132.206.52.79 - - [28/Feb/2008:12:31:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 -132.206.52.79 - - [28/Feb/2008:12:31:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -132.206.52.79 - - [28/Feb/2008:12:31:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -132.206.52.79 - - [28/Feb/2008:12:33:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.28.208.159 - - [28/Feb/2008:12:34:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -76.28.208.159 - - [28/Feb/2008:12:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -132.206.52.79 - - [28/Feb/2008:12:35:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 -132.206.52.79 - - [28/Feb/2008:12:35:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -132.206.52.79 - - [28/Feb/2008:12:36:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -132.206.52.79 - - [28/Feb/2008:12:36:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -199.88.143.1 - - [28/Feb/2008:12:36:51 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 200 288790 -74.56.210.71 - - [28/Feb/2008:12:37:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.118.156.49 - - [28/Feb/2008:12:39:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -128.118.156.49 - - [28/Feb/2008:12:39:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.118.156.49 - - [28/Feb/2008:12:39:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -128.118.156.49 - - [28/Feb/2008:12:39:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -71.57.91.136 - - [28/Feb/2008:12:39:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -71.57.91.136 - - [28/Feb/2008:12:39:33 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 248228 -71.57.91.136 - - [28/Feb/2008:12:39:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [28/Feb/2008:12:39:34 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 181019 -128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET / HTTP/1.1" 200 4447 -128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.24.9 - - [28/Feb/2008:12:41:19 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 -128.135.24.9 - - [28/Feb/2008:12:41:23 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /python.html HTTP/1.1" 200 18870 -208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -208.97.218.10 - - [28/Feb/2008:12:50:42 -0600] "GET /python.html HTTP/1.1" 304 - -208.97.218.10 - - [28/Feb/2008:12:50:42 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 304 - -84.110.206.115 - - [28/Feb/2008:12:50:59 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -208.97.218.10 - - [28/Feb/2008:12:51:01 -0600] "GET /software.html HTTP/1.1" 200 3163 -84.110.206.115 - - [28/Feb/2008:12:51:04 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1247 -76.28.208.159 - - [28/Feb/2008:12:51:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.28.208.159 - - [28/Feb/2008:12:52:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 -170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -84.110.189.94 - - [28/Feb/2008:12:56:01 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.189.94 - - [28/Feb/2008:12:56:03 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1283 -151.200.90.2 - - [28/Feb/2008:13:03:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -151.200.90.2 - - [28/Feb/2008:13:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.28.208.159 - - [28/Feb/2008:13:03:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 -76.28.208.159 - - [28/Feb/2008:13:03:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -151.200.90.2 - - [28/Feb/2008:13:04:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -151.200.90.2 - - [28/Feb/2008:13:04:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -76.28.208.159 - - [28/Feb/2008:13:04:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -76.28.208.159 - - [28/Feb/2008:13:04:21 -0600] "GET /ply/README HTTP/1.1" 200 8605 -128.135.11.245 - - [28/Feb/2008:13:04:34 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 -128.135.11.245 - - [28/Feb/2008:13:04:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:13:04:46 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 -67.186.98.20 - - [28/Feb/2008:13:04:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:13:04:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:13:05:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.157.119.197 - - [28/Feb/2008:13:05:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.200.90.2 - - [28/Feb/2008:13:06:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -71.57.91.136 - - [28/Feb/2008:13:07:17 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 127268 -71.57.91.136 - - [28/Feb/2008:13:07:17 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 181019 -74.56.210.71 - - [28/Feb/2008:13:07:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:13:12:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:13:15:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [28/Feb/2008:13:17:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:13:18:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.212.8.60 - - [28/Feb/2008:13:20:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -210.212.8.60 - - [28/Feb/2008:13:20:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -210.212.8.60 - - [28/Feb/2008:13:20:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -210.212.8.60 - - [28/Feb/2008:13:20:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 -68.21.20.162 - - [28/Feb/2008:13:21:40 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 -64.234.244.162 - - [28/Feb/2008:13:21:46 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -68.21.20.162 - - [28/Feb/2008:13:21:53 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -76.28.208.159 - - [28/Feb/2008:13:25:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.28.208.159 - - [28/Feb/2008:13:25:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.167.72.204 - - [28/Feb/2008:13:27:16 -0600] "GET /cv.html HTTP/1.1" 200 31798 -122.167.72.204 - - [28/Feb/2008:13:27:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.167.72.204 - - [28/Feb/2008:13:27:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.234.244.162 - - [28/Feb/2008:13:27:21 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 75085 -122.167.72.204 - - [28/Feb/2008:13:29:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.167.72.204 - - [28/Feb/2008:13:29:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.167.72.204 - - [28/Feb/2008:13:29:22 -0600] "GET / HTTP/1.1" 200 4447 -122.167.72.204 - - [28/Feb/2008:13:29:25 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -122.167.72.204 - - [28/Feb/2008:13:30:28 -0600] "GET /software.html HTTP/1.1" 200 3163 -122.167.72.204 - - [28/Feb/2008:13:30:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 -122.167.72.204 - - [28/Feb/2008:13:30:42 -0600] "GET /writing.html HTTP/1.1" 200 2871 -122.167.72.204 - - [28/Feb/2008:13:30:46 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 -122.167.72.204 - - [28/Feb/2008:13:30:48 -0600] "GET /about.html HTTP/1.1" 200 7890 -76.28.208.159 - - [28/Feb/2008:13:30:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.167.72.204 - - [28/Feb/2008:13:30:56 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 -122.167.72.204 - - [28/Feb/2008:13:30:59 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 -122.167.72.204 - - [28/Feb/2008:13:31:00 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 -64.234.244.162 - - [28/Feb/2008:13:31:01 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -122.167.72.204 - - [28/Feb/2008:13:31:04 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 -122.167.72.204 - - [28/Feb/2008:13:32:11 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 -67.186.98.20 - - [28/Feb/2008:13:32:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.167.72.204 - - [28/Feb/2008:13:32:30 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 -80.121.95.184 - - [28/Feb/2008:13:33:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.121.95.184 - - [28/Feb/2008:13:33:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.121.95.184 - - [28/Feb/2008:13:33:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.121.95.184 - - [28/Feb/2008:13:34:08 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -220.225.53.35 - - [28/Feb/2008:13:34:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -220.225.53.35 - - [28/Feb/2008:13:34:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -220.225.53.35 - - [28/Feb/2008:13:34:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -220.225.53.35 - - [28/Feb/2008:13:35:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -220.225.53.35 - - [28/Feb/2008:13:35:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -220.225.53.35 - - [28/Feb/2008:13:36:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -220.225.53.35 - - [28/Feb/2008:13:36:32 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.112.127.22 - - [28/Feb/2008:13:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.112.127.22 - - [28/Feb/2008:13:46:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -122.167.72.204 - - [28/Feb/2008:13:51:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -76.28.208.159 - - [28/Feb/2008:13:54:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -200.171.224.53 - - [28/Feb/2008:13:54:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 -200.171.224.53 - - [28/Feb/2008:13:54:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -200.171.224.53 - - [28/Feb/2008:13:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.43.246.12 - - [28/Feb/2008:13:55:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.43.246.12 - - [28/Feb/2008:13:55:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -122.167.72.204 - - [28/Feb/2008:13:55:20 -0600] "GET /index.html HTTP/1.1" 200 4447 -122.167.72.204 - - [28/Feb/2008:13:55:27 -0600] "GET /training.html HTTP/1.1" 200 6154 -67.186.98.20 - - [28/Feb/2008:13:57:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.64.212.223 - - [28/Feb/2008:13:59:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -81.64.212.223 - - [28/Feb/2008:13:59:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -81.64.212.223 - - [28/Feb/2008:13:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -198.175.55.5 - - [28/Feb/2008:13:59:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -90.53.107.51 - - [28/Feb/2008:14:01:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.196.43.134 - - [28/Feb/2008:14:06:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.2.96.93 - - [28/Feb/2008:14:12:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -83.2.96.93 - - [28/Feb/2008:14:12:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -65.166.139.21 - - [28/Feb/2008:14:12:33 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -65.166.139.21 - - [28/Feb/2008:14:12:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.166.139.21 - - [28/Feb/2008:14:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -65.166.139.21 - - [28/Feb/2008:14:12:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 -65.166.139.21 - - [28/Feb/2008:14:13:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -61.247.217.38 - - [28/Feb/2008:14:17:31 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.186.98.20 - - [28/Feb/2008:14:17:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [28/Feb/2008:14:17:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -67.186.98.20 - - [28/Feb/2008:14:17:56 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -67.186.98.20 - - [28/Feb/2008:14:19:13 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -91.49.96.242 - - [28/Feb/2008:14:21:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -91.49.96.242 - - [28/Feb/2008:14:21:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.49.96.242 - - [28/Feb/2008:14:21:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -91.49.96.242 - - [28/Feb/2008:14:21:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 -91.49.96.242 - - [28/Feb/2008:14:22:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 -64.233.178.136 - - [28/Feb/2008:14:23:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 -190.18.132.71 - - [28/Feb/2008:14:24:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.105.175.11 - - [28/Feb/2008:14:25:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.105.175.11 - - [28/Feb/2008:14:25:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -128.105.175.11 - - [28/Feb/2008:14:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [28/Feb/2008:14:26:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.10.16.193 - - [28/Feb/2008:14:26:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [28/Feb/2008:14:27:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [28/Feb/2008:14:27:48 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 -84.110.127.32 - - [28/Feb/2008:14:29:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.127.32 - - [28/Feb/2008:14:29:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 996 -74.6.31.151 - - [28/Feb/2008:14:31:17 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -209.17.146.129 - - [28/Feb/2008:14:34:43 -0600] "GET /ply/ HTTP/1.1" 304 - -200.55.140.181 - - [28/Feb/2008:14:37:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -200.55.140.181 - - [28/Feb/2008:14:37:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.135.11.245 - - [28/Feb/2008:14:38:05 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.11.245 - - [28/Feb/2008:14:38:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.194.8.73 - - [28/Feb/2008:14:46:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 -129.194.8.73 - - [28/Feb/2008:14:46:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -129.194.8.73 - - [28/Feb/2008:14:46:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:49:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:49:16 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.11.245 - - [28/Feb/2008:14:49:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:49:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:51:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.37.149.27 - - [28/Feb/2008:14:52:42 -0600] "GET /ply/ HTTP/1.1" 304 - -128.135.11.245 - - [28/Feb/2008:14:55:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:56:34 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.11.245 - - [28/Feb/2008:14:56:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:56:40 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -128.135.11.245 - - [28/Feb/2008:14:56:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:14:56:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -38.99.44.101 - - [28/Feb/2008:14:57:33 -0600] "GET /robots.txt HTTP/1.0" 200 71 -79.180.31.79 - - [28/Feb/2008:14:58:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -79.180.31.79 - - [28/Feb/2008:14:58:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -79.180.31.79 - - [28/Feb/2008:14:58:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.28.232 - - [28/Feb/2008:15:00:11 -0600] "GET /papers/SIAM97/ HTTP/1.0" 403 212 -128.135.11.245 - - [28/Feb/2008:15:04:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -205.238.131.78 - - [28/Feb/2008:15:04:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -205.238.131.78 - - [28/Feb/2008:15:04:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -205.238.131.78 - - [28/Feb/2008:15:04:46 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 -128.135.11.245 - - [28/Feb/2008:15:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -205.238.131.78 - - [28/Feb/2008:15:05:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -205.238.131.78 - - [28/Feb/2008:15:05:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ocaml HTTP/1.1" 200 1697 -128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -205.238.131.78 - - [28/Feb/2008:15:05:15 -0600] "GET /cgi-bin/wiki.pl?OcamlArrayHandling HTTP/1.1" 200 3852 -128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:15:05:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.125.239 - - [28/Feb/2008:15:05:23 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -128.135.125.239 - - [28/Feb/2008:15:05:29 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -205.238.131.78 - - [28/Feb/2008:15:05:50 -0600] "GET /cgi-bin/wiki.pl?OcamlValueExtraction HTTP/1.1" 200 1965 -74.6.20.207 - - [28/Feb/2008:15:06:26 -0600] "GET /writing.html HTTP/1.0" 304 - -205.238.131.78 - - [28/Feb/2008:15:08:34 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 -205.238.131.78 - - [28/Feb/2008:15:08:37 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4762 -67.186.98.20 - - [28/Feb/2008:15:17:40 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -128.135.24.9 - - [28/Feb/2008:15:18:05 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.24.9 - - [28/Feb/2008:15:18:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.24.9 - - [28/Feb/2008:15:18:06 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -128.135.24.9 - - [28/Feb/2008:15:18:11 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -128.135.24.9 - - [28/Feb/2008:15:19:37 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -128.135.125.239 - - [28/Feb/2008:15:26:47 -0600] "GET /dynamic/ HTTP/1.1" 304 - -128.135.125.239 - - [28/Feb/2008:15:26:51 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - -128.135.11.245 - - [28/Feb/2008:15:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.179.180.54 - - [28/Feb/2008:15:30:02 -0600] "GET /robots.txt HTTP/1.1" 200 71 -202.179.180.54 - - [28/Feb/2008:15:30:02 -0600] "GET /index.html HTTP/1.1" 200 4447 -62.59.179.107 - - [28/Feb/2008:15:33:39 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12562 -62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.59.179.107 - - [28/Feb/2008:15:33:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -62.59.179.107 - - [28/Feb/2008:15:34:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -24.15.187.198 - - [28/Feb/2008:15:35:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 -68.61.68.187 - - [28/Feb/2008:15:35:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 -98.193.69.179 - - [28/Feb/2008:15:39:21 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -98.193.69.179 - - [28/Feb/2008:15:39:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [28/Feb/2008:15:39:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -98.193.69.179 - - [28/Feb/2008:15:39:32 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -24.10.16.193 - - [28/Feb/2008:15:39:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.10.16.193 - - [28/Feb/2008:15:39:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [28/Feb/2008:15:39:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -192.88.162.35 - - [28/Feb/2008:15:43:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 -134.163.255.20 - - [28/Feb/2008:15:44:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -134.163.255.20 - - [28/Feb/2008:15:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.174 - - [28/Feb/2008:15:45:54 -0600] "GET /ply/ HTTP/1.0" 304 - -192.94.94.106 - - [28/Feb/2008:15:51:40 -0600] "GET / HTTP/1.1" 200 4447 -192.94.94.106 - - [28/Feb/2008:15:51:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -24.10.16.193 - - [28/Feb/2008:16:03:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -24.10.16.193 - - [28/Feb/2008:16:03:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [28/Feb/2008:16:03:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.212.77 - - [28/Feb/2008:16:07:22 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.55.212.77 - - [28/Feb/2008:16:07:23 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -65.55.208.118 - - [28/Feb/2008:16:15:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE035.HTM HTTP/1.1" 304 - -65.55.208.118 - - [28/Feb/2008:16:15:50 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE058.HTM HTTP/1.1" 304 - -128.135.125.239 - - [28/Feb/2008:16:16:49 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - -82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.224.122.212 - - [28/Feb/2008:16:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.224.122.212 - - [28/Feb/2008:16:17:18 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -82.224.122.212 - - [28/Feb/2008:16:17:21 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -82.224.122.212 - - [28/Feb/2008:16:17:24 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -139.82.24.80 - - [28/Feb/2008:16:18:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 -139.82.24.80 - - [28/Feb/2008:16:18:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -139.82.24.80 - - [28/Feb/2008:16:18:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.212.77 - - [28/Feb/2008:16:20:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -74.6.19.115 - - [28/Feb/2008:16:21:48 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.31.170 - - [28/Feb/2008:16:21:48 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.170 - - [28/Feb/2008:16:21:48 -0600] "GET /ply/ HTTP/1.0" 200 8018 -128.135.125.239 - - [28/Feb/2008:16:23:01 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - -128.135.164.162 - - [28/Feb/2008:16:27:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.164.162 - - [28/Feb/2008:16:27:31 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -66.232.113.62 - - [28/Feb/2008:16:28:34 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -85.185.11.131 - - [28/Feb/2008:16:28:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -128.135.164.162 - - [28/Feb/2008:16:30:06 -0600] "HEAD /dynamic/07Functional.pdf HTTP/1.1" 200 0 -128.135.164.162 - - [28/Feb/2008:16:30:38 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -65.55.208.118 - - [28/Feb/2008:16:30:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE025.HTM HTTP/1.1" 304 - -65.55.208.118 - - [28/Feb/2008:16:30:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE072.HTM HTTP/1.1" 304 - -65.55.208.118 - - [28/Feb/2008:16:30:51 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE120.HTM HTTP/1.1" 304 - -65.55.208.118 - - [28/Feb/2008:16:30:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.1" 304 - -128.135.164.162 - - [28/Feb/2008:16:31:00 -0600] "HEAD /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 0 -128.135.164.162 - - [28/Feb/2008:16:31:09 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -66.146.214.212 - - [28/Feb/2008:16:36:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -66.146.214.212 - - [28/Feb/2008:16:36:48 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.201.197.70 - - [28/Feb/2008:16:37:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.201.197.70 - - [28/Feb/2008:16:38:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.57.91.136 - - [28/Feb/2008:16:40:33 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -71.57.91.136 - - [28/Feb/2008:16:40:38 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -71.57.91.136 - - [28/Feb/2008:16:40:48 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -151.112.127.22 - - [28/Feb/2008:16:42:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -151.112.127.22 - - [28/Feb/2008:16:42:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -151.112.127.22 - - [28/Feb/2008:16:42:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -66.146.214.212 - - [28/Feb/2008:16:43:10 -0600] "GET /dynamic/ HTTP/1.1" 304 - -80.229.34.140 - - [28/Feb/2008:16:43:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.10.16.193 - - [28/Feb/2008:16:44:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -38.99.13.122 - - [28/Feb/2008:16:45:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 -134.173.200.39 - - [28/Feb/2008:16:52:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 -134.173.200.39 - - [28/Feb/2008:16:52:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -134.173.200.39 - - [28/Feb/2008:16:52:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.173.200.39 - - [28/Feb/2008:16:53:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -128.135.11.245 - - [28/Feb/2008:16:55:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:16:55:46 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -128.135.11.245 - - [28/Feb/2008:16:55:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.25.20 - - [28/Feb/2008:16:55:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.22.26 - - [28/Feb/2008:16:55:54 -0600] "GET /dynamic/syllabus.html HTTP/1.0" 200 4589 -86.101.114.10 - - [28/Feb/2008:16:57:39 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -86.101.114.10 - - [28/Feb/2008:16:57:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.101.114.10 - - [28/Feb/2008:16:57:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.143.35.16 - - [28/Feb/2008:16:58:07 -0600] "GET /ply/ HTTP/1.1" 304 - -210.143.35.13 - - [28/Feb/2008:16:58:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.11.245 - - [28/Feb/2008:16:58:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.145.54.15 - - [28/Feb/2008:17:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -216.145.54.15 - - [28/Feb/2008:17:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.145.54.15 - - [28/Feb/2008:17:02:22 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -216.145.54.15 - - [28/Feb/2008:17:02:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -216.145.54.15 - - [28/Feb/2008:17:02:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1461 -216.145.54.15 - - [28/Feb/2008:17:02:35 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -74.6.31.165 - - [28/Feb/2008:17:02:36 -0600] "GET /ply HTTP/1.0" 301 230 -74.6.31.165 - - [28/Feb/2008:17:02:36 -0600] "GET /ply/ HTTP/1.0" 200 8018 -209.203.68.2 - - [28/Feb/2008:17:02:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -209.203.68.2 - - [28/Feb/2008:17:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.145.54.15 - - [28/Feb/2008:17:02:51 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -209.203.68.2 - - [28/Feb/2008:17:02:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -209.203.68.2 - - [28/Feb/2008:17:03:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -216.145.54.15 - - [28/Feb/2008:17:03:15 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 -216.145.54.15 - - [28/Feb/2008:17:03:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -128.135.164.162 - - [28/Feb/2008:17:08:15 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -128.135.164.162 - - [28/Feb/2008:17:08:47 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - -68.37.149.27 - - [28/Feb/2008:17:10:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - -128.135.194.70 - - [28/Feb/2008:17:18:39 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.194.70 - - [28/Feb/2008:17:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.70 - - [28/Feb/2008:17:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.70 - - [28/Feb/2008:17:18:46 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -82.254.123.7 - - [28/Feb/2008:17:19:22 -0600] "GET /ply/ HTTP/1.1" 304 - -128.135.239.73 - - [28/Feb/2008:17:20:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 -128.135.239.73 - - [28/Feb/2008:17:20:46 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.239.73 - - [28/Feb/2008:17:21:01 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -128.135.164.173 - - [28/Feb/2008:17:22:01 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.164.173 - - [28/Feb/2008:17:22:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -163.181.251.10 - - [28/Feb/2008:17:22:07 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -163.181.251.10 - - [28/Feb/2008:17:22:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.207.228.84 - - [28/Feb/2008:17:22:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -68.37.149.27 - - [28/Feb/2008:17:23:57 -0600] "GET / HTTP/1.1" 200 4447 -68.37.149.27 - - [28/Feb/2008:17:23:57 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -68.37.149.27 - - [28/Feb/2008:17:24:00 -0600] "GET /software.html HTTP/1.1" 200 3163 -68.37.149.27 - - [28/Feb/2008:17:24:27 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 -128.135.230.179 - - [28/Feb/2008:17:26:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.230.179 - - [28/Feb/2008:17:26:35 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -207.176.224.244 - - [28/Feb/2008:17:26:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 -207.176.224.244 - - [28/Feb/2008:17:26:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 -68.37.149.27 - - [28/Feb/2008:17:26:44 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 -68.37.149.27 - - [28/Feb/2008:17:26:49 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 -88.191.19.81 - - [28/Feb/2008:17:27:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.135.164.178 - - [28/Feb/2008:17:28:38 -0600] "GET / HTTP/1.1" 200 4447 -128.135.164.178 - - [28/Feb/2008:17:28:38 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -128.135.164.178 - - [28/Feb/2008:17:28:43 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.164.178 - - [28/Feb/2008:17:28:52 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -202.213.221.97 - - [28/Feb/2008:17:28:55 -0600] "GET /robots.txt? HTTP/1.0" 200 71 -202.213.221.97 - - [28/Feb/2008:17:30:29 -0600] "GET / HTTP/1.0" 200 4447 -75.42.252.241 - - [28/Feb/2008:17:31:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -75.42.252.241 - - [28/Feb/2008:17:31:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -75.42.252.241 - - [28/Feb/2008:17:31:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -75.42.252.241 - - [28/Feb/2008:17:31:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.20.135 - - [28/Feb/2008:17:31:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE095.HTM HTTP/1.0" 200 1654 -71.201.41.248 - - [28/Feb/2008:17:33:24 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -74.6.20.205 - - [28/Feb/2008:17:35:59 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 -128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.178 - - [28/Feb/2008:17:37:27 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.194.178 - - [28/Feb/2008:17:37:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.178 - - [28/Feb/2008:17:37:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.178 - - [28/Feb/2008:17:37:41 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -130.207.228.84 - - [28/Feb/2008:17:38:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -130.207.228.84 - - [28/Feb/2008:17:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.207.228.84 - - [28/Feb/2008:17:38:17 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -128.135.197.57 - - [28/Feb/2008:17:39:41 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - -210.9.32.205 - - [28/Feb/2008:17:39:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -210.9.32.205 - - [28/Feb/2008:17:40:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.9.32.205 - - [28/Feb/2008:17:40:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.230.193 - - [28/Feb/2008:17:40:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.230.193 - - [28/Feb/2008:17:40:25 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -67.195.58.168 - - [28/Feb/2008:17:42:57 -0600] "GET /consulting.html HTTP/1.0" 304 - -128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET / HTTP/1.1" 200 4447 -128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.63 - - [28/Feb/2008:17:43:18 -0600] "GET /python.html HTTP/1.1" 200 18870 -128.135.194.63 - - [28/Feb/2008:17:43:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -70.128.13.131 - - [28/Feb/2008:17:43:33 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 -70.128.13.131 - - [28/Feb/2008:17:43:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.63 - - [28/Feb/2008:17:43:44 -0600] "GET /training.html HTTP/1.1" 200 6154 -128.135.194.63 - - [28/Feb/2008:17:43:47 -0600] "GET /index.html HTTP/1.1" 200 4447 -128.135.194.63 - - [28/Feb/2008:17:43:57 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.194.63 - - [28/Feb/2008:17:44:03 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -193.47.80.43 - - [28/Feb/2008:17:44:19 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.47.80.43 - - [28/Feb/2008:17:44:20 -0600] "GET / HTTP/1.1" 200 4447 -67.195.58.188 - - [28/Feb/2008:17:44:27 -0600] "GET /training.html HTTP/1.0" 200 6154 -71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 -71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.178 - - [28/Feb/2008:17:45:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 145188 -128.135.194.178 - - [28/Feb/2008:17:45:53 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 206 327437 -67.195.44.110 - - [28/Feb/2008:17:50:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 -128.135.230.204 - - [28/Feb/2008:17:51:27 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -128.135.197.195 - - [28/Feb/2008:17:53:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.197.195 - - [28/Feb/2008:17:53:44 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [28/Feb/2008:18:11:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.143.136.157 - - [28/Feb/2008:18:11:21 -0600] "GET /ply/README HTTP/1.1" 200 8605 -128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [28/Feb/2008:18:11:45 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -128.143.136.157 - - [28/Feb/2008:18:11:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.143.136.157 - - [28/Feb/2008:18:11:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [28/Feb/2008:18:11:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -201.236.226.90 - - [28/Feb/2008:18:14:23 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 -202.179.180.53 - - [28/Feb/2008:18:16:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 -202.179.180.53 - - [28/Feb/2008:18:16:15 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 -80.58.205.45 - - [28/Feb/2008:18:17:47 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -80.58.205.45 - - [28/Feb/2008:18:17:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.58.205.45 - - [28/Feb/2008:18:17:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.196.43.134 - - [28/Feb/2008:18:25:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.135.197.195 - - [28/Feb/2008:18:29:16 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -128.135.197.195 - - [28/Feb/2008:18:29:24 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -128.135.197.57 - - [28/Feb/2008:18:29:43 -0600] "GET /dynamic HTTP/1.1" 301 246 -128.135.197.57 - - [28/Feb/2008:18:29:43 -0600] "GET /dynamic/ HTTP/1.1" 304 - -128.135.197.57 - - [28/Feb/2008:18:29:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 -189.13.67.152 - - [28/Feb/2008:18:30:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 -189.13.67.152 - - [28/Feb/2008:18:30:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -218.111.4.247 - - [28/Feb/2008:18:30:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -211.127.232.14 - - [28/Feb/2008:18:31:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -211.127.232.14 - - [28/Feb/2008:18:31:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.194.70 - - [28/Feb/2008:18:31:24 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -211.127.232.14 - - [28/Feb/2008:18:31:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -128.135.194.70 - - [28/Feb/2008:18:31:29 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 -211.127.232.14 - - [28/Feb/2008:18:31:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 -211.127.232.14 - - [28/Feb/2008:18:31:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 -211.127.232.14 - - [28/Feb/2008:18:32:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1161 -221.189.180.200 - - [28/Feb/2008:18:32:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -211.127.232.14 - - [28/Feb/2008:18:32:54 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 -65.55.212.77 - - [28/Feb/2008:18:33:57 -0600] "GET /robots.txt HTTP/1.0" 200 71 -65.55.212.77 - - [28/Feb/2008:18:33:57 -0600] "GET /ply/README HTTP/1.0" 200 8605 -161.45.160.30 - - [28/Feb/2008:18:33:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 -161.45.160.30 - - [28/Feb/2008:18:34:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -161.45.160.30 - - [28/Feb/2008:18:34:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -161.45.160.30 - - [28/Feb/2008:18:35:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -161.45.160.30 - - [28/Feb/2008:18:35:28 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -161.45.160.30 - - [28/Feb/2008:18:35:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 -189.13.67.152 - - [28/Feb/2008:18:36:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -189.13.67.152 - - [28/Feb/2008:18:36:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.195.58.172 - - [28/Feb/2008:18:42:17 -0600] "GET /index.html HTTP/1.0" 200 4447 -128.135.230.204 - - [28/Feb/2008:18:53:45 -0600] "GET /old/index.html HTTP/1.1" 404 133 -128.135.230.204 - - [28/Feb/2008:18:53:49 -0600] "GET /old/ HTTP/1.1" 404 133 -128.135.230.204 - - [28/Feb/2008:18:53:56 -0600] "GET /old/cource18.html HTTP/1.1" 404 133 -128.135.230.204 - - [28/Feb/2008:18:53:59 -0600] "GET / HTTP/1.1" 200 4447 -128.135.230.204 - - [28/Feb/2008:18:54:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -67.195.58.177 - - [28/Feb/2008:18:55:27 -0600] "GET /python.html HTTP/1.0" 304 - -61.135.190.17 - - [28/Feb/2008:18:56:59 -0600] "GET /ply/ HTTP/1.1" 304 - -64.124.85.74 - - [28/Feb/2008:18:59:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 -64.124.85.74 - - [28/Feb/2008:19:02:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 -128.135.197.195 - - [28/Feb/2008:19:02:31 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -128.135.197.195 - - [28/Feb/2008:19:02:38 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - -128.135.164.162 - - [28/Feb/2008:19:08:39 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -68.81.244.149 - - [28/Feb/2008:19:12:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -128.135.230.3 - - [28/Feb/2008:19:15:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.230.3 - - [28/Feb/2008:19:15:40 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.230.3 - - [28/Feb/2008:19:15:47 -0600] "GET /dynamic/ HTTP/1.1" 304 - -128.135.230.3 - - [28/Feb/2008:19:15:49 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -128.135.230.161 - - [28/Feb/2008:19:17:07 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -128.135.230.161 - - [28/Feb/2008:19:17:37 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -98.215.100.68 - - [28/Feb/2008:19:19:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -98.215.100.68 - - [28/Feb/2008:19:20:12 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -212.56.88.112 - - [28/Feb/2008:19:20:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.34.108 - - [28/Feb/2008:19:22:54 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -74.6.19.115 - - [28/Feb/2008:19:24:20 -0600] "GET /robots.txt HTTP/1.0" 200 71 -74.6.31.145 - - [28/Feb/2008:19:24:20 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 -213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -213.224.182.16 - - [28/Feb/2008:19:24:38 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -212.56.88.112 - - [28/Feb/2008:19:26:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -161.45.160.30 - - [28/Feb/2008:19:26:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.34.114 - - [28/Feb/2008:19:26:57 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -161.45.162.52 - - [28/Feb/2008:19:26:58 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -213.224.182.16 - - [28/Feb/2008:19:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.9.32.205 - - [28/Feb/2008:19:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.165 - - [28/Feb/2008:19:32:19 -0600] "GET /writing.html HTTP/1.0" 200 2871 -67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.83.111.28 - - [28/Feb/2008:19:33:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.83.111.28 - - [28/Feb/2008:19:33:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -67.83.111.28 - - [28/Feb/2008:19:33:34 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -128.135.194.70 - - [28/Feb/2008:19:34:23 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -161.45.162.52 - - [28/Feb/2008:19:38:50 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -202.213.221.97 - - [28/Feb/2008:19:39:18 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -210.9.32.205 - - [28/Feb/2008:19:39:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.56.88.112 - - [28/Feb/2008:19:40:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -67.195.34.111 - - [28/Feb/2008:19:44:14 -0600] "GET /ply HTTP/1.0" 301 230 -67.195.34.111 - - [28/Feb/2008:19:44:17 -0600] "GET /ply/ HTTP/1.0" 200 8018 -67.195.58.158 - - [28/Feb/2008:19:45:40 -0600] "GET /ply/ply.html HTTP/1.0" 304 - -134.159.131.34 - - [28/Feb/2008:19:45:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 -134.159.131.34 - - [28/Feb/2008:19:45:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -134.159.131.34 - - [28/Feb/2008:19:45:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.159.131.34 - - [28/Feb/2008:19:45:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -134.159.131.34 - - [28/Feb/2008:19:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.34.97 - - [28/Feb/2008:19:46:39 -0600] "GET /ply HTTP/1.0" 301 230 -67.195.34.97 - - [28/Feb/2008:19:46:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 -67.195.58.181 - - [28/Feb/2008:19:50:24 -0600] "GET /ply/support.html HTTP/1.0" 200 739 -67.195.58.151 - - [28/Feb/2008:19:50:37 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 -67.195.58.170 - - [28/Feb/2008:19:50:56 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 -122.152.129.53 - - [28/Feb/2008:19:51:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 -212.56.88.112 - - [28/Feb/2008:19:52:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -128.135.230.204 - - [28/Feb/2008:19:57:16 -0600] "GET /dynamic/lecture8 HTTP/1.1" 404 133 -128.135.230.204 - - [28/Feb/2008:19:57:21 -0600] "GET /dynamic/lecture8/ HTTP/1.1" 404 133 -128.135.230.204 - - [28/Feb/2008:19:57:30 -0600] "GET /dynamic/Lecture8/ HTTP/1.1" 404 133 -128.135.230.179 - - [28/Feb/2008:20:03:24 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.230.179 - - [28/Feb/2008:20:03:29 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -212.56.88.112 - - [28/Feb/2008:20:15:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -66.232.113.194 - - [28/Feb/2008:20:22:08 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -213.227.137.187 - - [28/Feb/2008:20:22:09 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -80.97.94.178 - - [28/Feb/2008:20:22:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -195.50.147.66 - - [28/Feb/2008:20:25:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -195.50.147.66 - - [28/Feb/2008:20:25:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.116.72.114 - - [28/Feb/2008:20:27:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -66.116.72.114 - - [28/Feb/2008:20:28:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -122.152.140.206 - - [28/Feb/2008:20:32:56 -0600] "GET / HTTP/1.1" 200 4447 -201.16.201.21 - - [28/Feb/2008:20:38:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 -201.16.201.21 - - [28/Feb/2008:20:38:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -201.16.201.21 - - [28/Feb/2008:20:38:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.16.201.21 - - [28/Feb/2008:20:38:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.164.154 - - [28/Feb/2008:20:40:17 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -128.135.164.154 - - [28/Feb/2008:20:40:33 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -128.135.164.154 - - [28/Feb/2008:20:42:02 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -204.246.129.196 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -65.46.48.194 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -202.44.78.196 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -202.44.78.196 - - [28/Feb/2008:20:43:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -202.44.78.196 - - [28/Feb/2008:20:43:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 -206.51.237.114 - - [28/Feb/2008:20:44:26 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -80.191.131.2 - - [28/Feb/2008:20:44:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -213.185.116.11 - - [28/Feb/2008:20:44:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -128.135.164.154 - - [28/Feb/2008:20:46:24 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 304 - -82.189.46.148 - - [28/Feb/2008:20:46:59 -0600] "GET / HTTP/1.0" 200 4447 -82.189.46.148 - - [28/Feb/2008:20:47:00 -0600] "GET /about.html HTTP/1.0" 200 7890 -82.189.46.148 - - [28/Feb/2008:20:47:10 -0600] "GET /training.html HTTP/1.0" 200 6154 -82.189.46.148 - - [28/Feb/2008:20:47:19 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -82.189.46.148 - - [28/Feb/2008:20:47:21 -0600] "GET /python.html HTTP/1.0" 200 18870 -82.189.46.148 - - [28/Feb/2008:20:47:23 -0600] "GET /consulting.html HTTP/1.0" 200 8728 -82.189.46.148 - - [28/Feb/2008:20:47:28 -0600] "GET /writing.html HTTP/1.0" 200 2871 -82.189.46.148 - - [28/Feb/2008:20:47:29 -0600] "GET /software.html HTTP/1.0" 200 3163 -82.189.46.148 - - [28/Feb/2008:20:47:30 -0600] "GET /index.html HTTP/1.0" 200 4447 -82.189.46.148 - - [28/Feb/2008:20:47:32 -0600] "GET /sysop.html HTTP/1.0" 200 1760 -82.189.46.148 - - [28/Feb/2008:20:47:35 -0600] "GET /cv.html HTTP/1.0" 200 31798 -82.189.46.148 - - [28/Feb/2008:20:47:38 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -82.189.46.148 - - [28/Feb/2008:20:47:40 -0600] "GET /ply/README HTTP/1.0" 200 8605 -82.189.46.148 - - [28/Feb/2008:20:47:42 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 -82.189.46.148 - - [28/Feb/2008:20:47:43 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 -82.189.46.148 - - [28/Feb/2008:20:47:45 -0600] "GET /swill/exec.html HTTP/1.0" 200 12540 -67.195.44.110 - - [28/Feb/2008:20:49:43 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.195.44.109 - - [28/Feb/2008:20:49:43 -0600] "GET /ply/ HTTP/1.0" 200 8018 -74.6.28.237 - - [28/Feb/2008:20:50:01 -0600] "GET /photos/wind/pages/IMG_1309.htm HTTP/1.0" 404 133 -128.135.164.154 - - [28/Feb/2008:20:50:18 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - -128.135.164.154 - - [28/Feb/2008:20:50:59 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -74.6.25.125 - - [28/Feb/2008:20:51:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1298 -128.135.164.154 - - [28/Feb/2008:20:55:56 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -219.142.125.240 - - [28/Feb/2008:20:55:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -219.142.125.240 - - [28/Feb/2008:20:56:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -219.142.125.240 - - [28/Feb/2008:20:56:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -201.16.201.21 - - [28/Feb/2008:20:57:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -128.135.164.154 - - [28/Feb/2008:20:59:38 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 304 - -128.135.164.154 - - [28/Feb/2008:20:59:44 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 206 416530 -219.142.125.240 - - [28/Feb/2008:21:01:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -65.55.208.117 - - [28/Feb/2008:21:03:24 -0600] "GET /robots.txt HTTP/1.1" 200 71 -217.196.43.134 - - [28/Feb/2008:21:05:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -58.217.219.85 - - [28/Feb/2008:21:17:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -202.179.180.52 - - [28/Feb/2008:21:18:31 -0600] "GET /robots.txt HTTP/1.1" 200 71 -202.179.180.52 - - [28/Feb/2008:21:18:33 -0600] "GET /papers/Perl98/swigperl.pdf HTTP/1.1" 200 151655 -77.91.224.3 - - [28/Feb/2008:21:20:30 -0600] "GET /robots.txt HTTP/1.1" 200 71 -71.57.91.136 - - [28/Feb/2008:21:20:30 -0600] "GET /dynamic/ HTTP/1.1" 304 - -77.91.224.3 - - [28/Feb/2008:21:20:31 -0600] "GET / HTTP/1.1" 200 4447 -77.91.224.13 - - [28/Feb/2008:21:22:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 -77.91.224.13 - - [28/Feb/2008:21:22:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 -211.127.232.14 - - [28/Feb/2008:21:22:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 -84.110.148.125 - - [28/Feb/2008:21:23:39 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.187.74 - - [28/Feb/2008:21:23:40 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.148.125 - - [28/Feb/2008:21:23:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 -84.110.187.74 - - [28/Feb/2008:21:23:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 -24.37.1.68 - - [28/Feb/2008:21:23:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -24.37.1.68 - - [28/Feb/2008:21:23:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.37.1.68 - - [28/Feb/2008:21:23:51 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -211.127.232.14 - - [28/Feb/2008:21:25:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 -67.176.197.254 - - [28/Feb/2008:21:27:07 -0600] "GET / HTTP/1.1" 200 4447 -207.47.98.129 - - [28/Feb/2008:21:27:11 -0600] "GET /cgi-bin/wiki.pl?S HTTP/1.1" 200 1163 -207.47.98.129 - - [28/Feb/2008:21:27:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.176.197.254 - - [28/Feb/2008:21:27:12 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -67.176.197.254 - - [28/Feb/2008:21:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.176.197.254 - - [28/Feb/2008:21:27:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -207.47.98.129 - - [28/Feb/2008:21:27:28 -0600] "GET /cgi-bin/wiki.pl? HTTP/1.1" 200 2883 -207.47.98.129 - - [28/Feb/2008:21:27:36 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 -211.127.232.14 - - [28/Feb/2008:21:27:40 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1869 -207.47.98.129 - - [28/Feb/2008:21:27:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 4177 -211.127.232.14 - - [28/Feb/2008:21:27:48 -0600] "GET /cgi-bin/wiki.pl?action=history&id=SwigFaqDLLUsingMingw HTTP/1.1" 200 2787 -207.47.98.129 - - [28/Feb/2008:21:28:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 -207.47.98.129 - - [28/Feb/2008:21:28:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 2665 -71.57.91.136 - - [28/Feb/2008:21:28:56 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - -207.47.98.129 - - [28/Feb/2008:21:28:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 -71.57.91.136 - - [28/Feb/2008:21:28:57 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - -70.41.192.171 - - [28/Feb/2008:21:34:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -70.41.192.171 - - [28/Feb/2008:21:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.41.192.171 - - [28/Feb/2008:21:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.176.197.254 - - [28/Feb/2008:21:39:46 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -67.195.58.164 - - [28/Feb/2008:21:43:01 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5653 -193.252.149.16 - - [28/Feb/2008:21:44:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.16 - - [28/Feb/2008:21:44:07 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 75085 -84.110.177.245 - - [28/Feb/2008:21:45:48 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.177.245 - - [28/Feb/2008:21:45:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 981 -70.41.192.171 - - [28/Feb/2008:21:52:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.232.113.62 - - [28/Feb/2008:22:03:29 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -83.238.23.214 - - [28/Feb/2008:22:03:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -84.110.153.190 - - [28/Feb/2008:22:05:21 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.153.190 - - [28/Feb/2008:22:05:26 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1205 -24.12.12.183 - - [28/Feb/2008:22:08:26 -0600] "GET / HTTP/1.1" 200 4447 -24.12.12.183 - - [28/Feb/2008:22:08:27 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 -24.12.12.183 - - [28/Feb/2008:22:08:29 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -24.12.12.183 - - [28/Feb/2008:22:08:33 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - -64.135.175.82 - - [28/Feb/2008:22:09:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -64.135.175.82 - - [28/Feb/2008:22:09:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.135.175.82 - - [28/Feb/2008:22:09:54 -0600] "GET /cgi-bin/wiki.pl?back=/SharedLibraries HTTP/1.1" 200 1111 -210.245.31.3 - - [28/Feb/2008:22:10:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 -210.245.31.3 - - [28/Feb/2008:22:10:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -210.245.31.3 - - [28/Feb/2008:22:10:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.135.175.82 - - [28/Feb/2008:22:11:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -24.12.12.183 - - [28/Feb/2008:22:11:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 -70.41.192.171 - - [28/Feb/2008:22:19:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.24.133.241 - - [28/Feb/2008:22:20:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 -64.24.133.241 - - [28/Feb/2008:22:20:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -64.24.133.241 - - [28/Feb/2008:22:20:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.24.133.241 - - [28/Feb/2008:22:20:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.24.133.241 - - [28/Feb/2008:22:20:47 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -74.6.24.112 - - [28/Feb/2008:22:26:05 -0600] "GET /diversions.html HTTP/1.0" 200 2427 -67.195.58.186 - - [28/Feb/2008:22:32:25 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 -67.176.197.254 - - [28/Feb/2008:22:35:53 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -67.176.197.254 - - [28/Feb/2008:22:35:54 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -131.215.42.190 - - [28/Feb/2008:22:41:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -131.215.42.190 - - [28/Feb/2008:22:41:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -131.215.42.190 - - [28/Feb/2008:22:41:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -71.110.220.16 - - [28/Feb/2008:22:44:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.110.220.16 - - [28/Feb/2008:22:44:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -131.215.42.190 - - [28/Feb/2008:22:46:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -131.215.42.190 - - [28/Feb/2008:22:46:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 -216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.19.182.199 - - [28/Feb/2008:22:49:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [28/Feb/2008:22:53:36 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -24.1.247.118 - - [28/Feb/2008:22:53:47 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -216.19.182.199 - - [28/Feb/2008:22:54:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -216.19.182.199 - - [28/Feb/2008:22:57:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [28/Feb/2008:23:02:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -24.1.159.241 - - [28/Feb/2008:23:02:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [28/Feb/2008:23:02:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [28/Feb/2008:23:02:52 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -67.195.58.164 - - [28/Feb/2008:23:02:55 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 -24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.1.159.241 - - [28/Feb/2008:23:08:43 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 -68.37.149.27 - - [28/Feb/2008:23:11:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.1.159.241 - - [28/Feb/2008:23:12:57 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 -24.1.159.241 - - [28/Feb/2008:23:12:57 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 206 96799 -68.37.149.27 - - [28/Feb/2008:23:13:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 -24.10.16.193 - - [28/Feb/2008:23:19:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -24.15.187.198 - - [28/Feb/2008:23:22:45 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -131.215.42.190 - - [28/Feb/2008:23:24:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -131.215.42.190 - - [28/Feb/2008:23:24:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -131.215.42.190 - - [28/Feb/2008:23:26:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -68.37.149.27 - - [28/Feb/2008:23:28:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 -131.215.42.190 - - [28/Feb/2008:23:29:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -65.55.208.122 - - [28/Feb/2008:23:30:41 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.122 - - [28/Feb/2008:23:30:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.1" 200 1466 -131.215.42.190 - - [28/Feb/2008:23:32:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -71.132.65.31 - - [28/Feb/2008:23:34:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 -71.132.65.31 - - [28/Feb/2008:23:34:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -71.132.65.31 - - [28/Feb/2008:23:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -68.37.149.27 - - [28/Feb/2008:23:35:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -131.215.42.190 - - [28/Feb/2008:23:36:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.195.58.182 - - [28/Feb/2008:23:37:18 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 -24.1.159.241 - - [28/Feb/2008:23:42:48 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 206 339721 -131.215.42.190 - - [28/Feb/2008:23:43:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -131.215.42.190 - - [28/Feb/2008:23:43:20 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqDLLForWindows HTTP/1.1" 200 1145 -131.215.42.190 - - [28/Feb/2008:23:43:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -131.215.42.190 - - [28/Feb/2008:23:43:28 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 -131.215.42.190 - - [28/Feb/2008:23:43:32 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqDLLForWindows HTTP/1.1" 200 1152 -131.215.42.190 - - [28/Feb/2008:23:43:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -131.215.42.190 - - [28/Feb/2008:23:43:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 -131.215.42.190 - - [28/Feb/2008:23:43:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 -203.20.35.28 - - [28/Feb/2008:23:55:52 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 200 15264 -203.20.35.28 - - [28/Feb/2008:23:55:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -203.20.35.28 - - [28/Feb/2008:23:55:55 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 206 188184 -61.14.187.142 - - [29/Feb/2008:00:04:15 -0600] "GET / HTTP/1.0" 200 4447 -61.14.187.142 - - [29/Feb/2008:00:04:16 -0600] "GET /python.html HTTP/1.0" 200 18870 -61.14.187.142 - - [29/Feb/2008:00:04:16 -0600] "GET /index.html HTTP/1.0" 200 4447 -61.14.187.142 - - [29/Feb/2008:00:04:17 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5653 -61.14.187.142 - - [29/Feb/2008:00:04:18 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 -61.14.187.142 - - [29/Feb/2008:00:04:18 -0600] "GET /writing.html HTTP/1.0" 200 2871 -61.14.187.142 - - [29/Feb/2008:00:04:19 -0600] "GET /about.html HTTP/1.0" 200 7890 -61.14.187.142 - - [29/Feb/2008:00:04:20 -0600] "GET /software.html HTTP/1.0" 200 3163 -61.14.187.142 - - [29/Feb/2008:00:04:20 -0600] "GET /training.html HTTP/1.0" 200 6154 -61.14.187.142 - - [29/Feb/2008:00:04:21 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 -61.14.187.142 - - [29/Feb/2008:00:04:21 -0600] "GET /dynamic/assign3.html HTTP/1.0" 200 6798 -217.172.44.82 - - [29/Feb/2008:00:04:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 -61.14.187.142 - - [29/Feb/2008:00:04:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -61.14.187.142 - - [29/Feb/2008:00:04:24 -0600] "GET /ply/README HTTP/1.0" 200 8605 -61.14.187.142 - - [29/Feb/2008:00:04:24 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 -61.14.187.142 - - [29/Feb/2008:00:04:25 -0600] "GET /publications.html HTTP/1.0" 200 7758 -61.14.187.142 - - [29/Feb/2008:00:04:26 -0600] "GET /sysop.html HTTP/1.0" 200 1760 -61.14.187.142 - - [29/Feb/2008:00:04:26 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 -61.14.187.142 - - [29/Feb/2008:00:04:27 -0600] "GET /dynamic/portfolio.txt HTTP/1.0" 200 100 -61.14.187.142 - - [29/Feb/2008:00:04:27 -0600] "GET /dynamic/sd.html HTTP/1.0" 200 1873 -61.14.187.142 - - [29/Feb/2008:00:04:28 -0600] "GET /papers/Py96/python96.html HTTP/1.0" 200 22442 -61.14.187.142 - - [29/Feb/2008:00:04:29 -0600] "GET /swill/about.html HTTP/1.0" 404 133 -67.97.80.5 - - [29/Feb/2008:00:16:24 -0600] "GET /python.html HTTP/1.0" 200 18870 -67.97.80.5 - - [29/Feb/2008:00:16:25 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 -67.97.80.5 - - [29/Feb/2008:00:17:23 -0600] "GET /python.html HTTP/1.0" 304 - -67.97.80.5 - - [29/Feb/2008:00:17:23 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 304 - -67.97.80.5 - - [29/Feb/2008:00:17:27 -0600] "GET /training.html HTTP/1.0" 200 6154 -213.145.165.82 - - [29/Feb/2008:00:17:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -84.48.187.205 - - [29/Feb/2008:00:24:52 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -125.19.42.35 - - [29/Feb/2008:00:26:35 -0600] "GET /cv.html HTTP/1.1" 200 31798 -125.19.42.35 - - [29/Feb/2008:00:26:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -125.19.42.35 - - [29/Feb/2008:00:26:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -125.19.42.35 - - [29/Feb/2008:00:29:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -125.16.133.35 - - [29/Feb/2008:00:32:07 -0600] "GET /python.html HTTP/1.1" 200 18870 -125.16.133.35 - - [29/Feb/2008:00:32:10 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 -125.19.42.35 - - [29/Feb/2008:00:32:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.252.149.15 - - [29/Feb/2008:00:33:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.15 - - [29/Feb/2008:00:33:36 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -125.16.133.35 - - [29/Feb/2008:00:38:11 -0600] "GET /python.html HTTP/1.1" 304 - -125.16.133.35 - - [29/Feb/2008:00:38:11 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 304 - -67.186.98.20 - - [29/Feb/2008:00:54:58 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 -67.186.98.20 - - [29/Feb/2008:00:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [29/Feb/2008:00:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.26.100 - - [29/Feb/2008:00:58:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE115.HTM HTTP/1.0" 200 1596 -74.6.29.23 - - [29/Feb/2008:01:15:57 -0600] "GET /dynamic/ HTTP/1.0" 200 5653 -61.57.149.13 - - [29/Feb/2008:01:17:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -61.57.149.13 - - [29/Feb/2008:01:17:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.57.149.13 - - [29/Feb/2008:01:17:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -61.57.149.13 - - [29/Feb/2008:01:18:00 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1869 -74.6.28.122 - - [29/Feb/2008:01:22:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.0" 200 343614 -193.252.149.16 - - [29/Feb/2008:01:24:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 -193.252.149.16 - - [29/Feb/2008:01:24:44 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -78.99.69.34 - - [29/Feb/2008:01:29:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 -78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -78.99.69.34 - - [29/Feb/2008:01:29:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -65.55.208.116 - - [29/Feb/2008:01:30:04 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.116 - - [29/Feb/2008:01:30:04 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE003.HTM HTTP/1.1" 304 - -38.98.120.84 - - [29/Feb/2008:01:49:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 -38.98.120.84 - - [29/Feb/2008:01:49:22 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [29/Feb/2008:01:49:23 -0600] "GET /about.html HTTP/1.1" 200 7890 -38.98.120.84 - - [29/Feb/2008:01:49:24 -0600] "GET / HTTP/1.1" 200 4447 -38.98.120.84 - - [29/Feb/2008:01:49:25 -0600] "GET /sitemap.html HTTP/1.1" 404 133 -38.98.120.84 - - [29/Feb/2008:01:49:27 -0600] "GET / HTTP/1.1" 200 4447 -80.58.205.45 - - [29/Feb/2008:01:53:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -80.58.205.45 - - [29/Feb/2008:01:53:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -221.239.94.225 - - [29/Feb/2008:02:02:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 -221.239.94.225 - - [29/Feb/2008:02:02:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.112.65.218 - - [29/Feb/2008:02:03:04 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -91.112.65.218 - - [29/Feb/2008:02:03:07 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -91.112.65.218 - - [29/Feb/2008:02:03:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -91.112.65.218 - - [29/Feb/2008:02:03:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -91.112.65.218 - - [29/Feb/2008:02:04:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 -140.128.18.187 - - [29/Feb/2008:02:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -140.128.18.187 - - [29/Feb/2008:02:09:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -140.128.18.187 - - [29/Feb/2008:02:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -140.128.18.187 - - [29/Feb/2008:02:09:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -65.55.208.120 - - [29/Feb/2008:02:09:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.120 - - [29/Feb/2008:02:09:34 -0600] "GET /photos/wind/pages/IMG_1267.htm HTTP/1.1" 404 133 -91.49.96.242 - - [29/Feb/2008:02:12:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -91.49.96.242 - - [29/Feb/2008:02:12:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -91.49.96.242 - - [29/Feb/2008:02:12:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.75.252.165 - - [29/Feb/2008:02:14:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -210.75.252.165 - - [29/Feb/2008:02:14:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -210.75.252.165 - - [29/Feb/2008:02:14:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -64.81.229.55 - - [29/Feb/2008:02:16:54 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -64.81.229.55 - - [29/Feb/2008:02:16:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -64.81.229.55 - - [29/Feb/2008:02:18:11 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 -210.75.252.165 - - [29/Feb/2008:02:18:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -91.112.65.218 - - [29/Feb/2008:02:26:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -67.195.58.174 - - [29/Feb/2008:02:28:27 -0600] "GET /ply/ HTTP/1.0" 304 - -91.112.65.218 - - [29/Feb/2008:02:28:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -67.195.58.188 - - [29/Feb/2008:02:28:54 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 -67.195.58.188 - - [29/Feb/2008:02:28:54 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 -67.195.58.160 - - [29/Feb/2008:02:29:13 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 -67.195.58.178 - - [29/Feb/2008:02:29:25 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 -85.97.128.179 - - [29/Feb/2008:02:36:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -85.97.128.179 - - [29/Feb/2008:02:36:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -85.97.128.179 - - [29/Feb/2008:02:37:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -85.97.128.179 - - [29/Feb/2008:02:37:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -65.55.208.118 - - [29/Feb/2008:02:39:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.208.118 - - [29/Feb/2008:02:39:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.1" 200 1337 -67.195.58.168 - - [29/Feb/2008:02:43:46 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 -67.195.58.175 - - [29/Feb/2008:02:49:18 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 -221.239.94.225 - - [29/Feb/2008:02:53:09 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 9416 -221.239.94.225 - - [29/Feb/2008:02:53:11 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 42184 -221.239.94.225 - - [29/Feb/2008:02:53:12 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 74952 -221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 29862 -221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 -221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 -221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 -221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 107720 -221.239.94.225 - - [29/Feb/2008:02:53:15 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 107720 -121.14.96.153 - - [29/Feb/2008:02:53:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 -58.60.14.236 - - [29/Feb/2008:02:53:19 -0600] "GET / HTTP/1.1" 200 4447 -124.115.1.67 - - [29/Feb/2008:02:53:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -121.14.96.152 - - [29/Feb/2008:02:53:22 -0600] "GET /ply/README HTTP/1.1" 200 8605 -58.60.13.231 - - [29/Feb/2008:02:53:32 -0600] "GET /ply/support.html HTTP/1.1" 200 739 -202.58.71.138 - - [29/Feb/2008:02:54:53 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -202.58.71.138 - - [29/Feb/2008:02:54:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -202.58.71.138 - - [29/Feb/2008:02:58:55 -0600] "GET /ply/ HTTP/1.0" 200 8018 -202.58.71.138 - - [29/Feb/2008:02:59:02 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -202.58.71.138 - - [29/Feb/2008:02:59:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -210.217.95.253 - - [29/Feb/2008:03:00:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -210.217.95.253 - - [29/Feb/2008:03:00:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 -82.127.117.160 - - [29/Feb/2008:03:05:32 -0600] "GET /ply/ HTTP/1.0" 200 8018 -82.127.117.160 - - [29/Feb/2008:03:05:33 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -82.127.117.160 - - [29/Feb/2008:03:05:42 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -84.110.143.135 - - [29/Feb/2008:03:08:58 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.143.135 - - [29/Feb/2008:03:09:08 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 -210.51.195.13 - - [29/Feb/2008:03:13:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 -210.51.195.13 - - [29/Feb/2008:03:14:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 -210.51.195.13 - - [29/Feb/2008:03:14:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 -210.51.195.13 - - [29/Feb/2008:03:14:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 -60.28.17.44 - - [29/Feb/2008:03:15:03 -0600] "GET / HTTP/1.1" 200 4447 -210.51.195.13 - - [29/Feb/2008:03:19:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 -220.220.204.13 - - [29/Feb/2008:03:21:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -220.220.204.13 - - [29/Feb/2008:03:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.220.204.13 - - [29/Feb/2008:03:21:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -220.220.204.13 - - [29/Feb/2008:03:21:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 -221.239.94.225 - - [29/Feb/2008:03:21:38 -0600] "GET /ply/ HTTP/1.1" 304 - -221.239.94.225 - - [29/Feb/2008:03:21:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - -220.220.204.13 - - [29/Feb/2008:03:21:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1323 -220.220.204.13 - - [29/Feb/2008:03:21:52 -0600] "GET /cgi-bin/wiki.pl?CallbackDirective HTTP/1.1" 200 3269 -220.220.204.13 - - [29/Feb/2008:03:21:57 -0600] "GET /cgi-bin/wiki.pl?AddmethodsDirective HTTP/1.1" 200 1671 -220.220.204.13 - - [29/Feb/2008:03:22:04 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 -67.195.58.178 - - [29/Feb/2008:03:29:56 -0600] "GET /software.html HTTP/1.0" 304 - -67.186.98.20 - - [29/Feb/2008:03:31:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 -67.186.98.20 - - [29/Feb/2008:03:31:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -67.186.98.20 - - [29/Feb/2008:03:31:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.99.30.64 - - [29/Feb/2008:03:32:41 -0600] "GET /robots.txt HTTP/1.0" 200 71 -91.103.40.50 - - [29/Feb/2008:03:34:38 -0600] "HEAD /ply/ HTTP/1.1" 200 0 -80.58.205.45 - - [29/Feb/2008:03:36:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.249.147.120 - - [29/Feb/2008:03:36:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 -70.249.147.120 - - [29/Feb/2008:03:36:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -70.249.147.120 - - [29/Feb/2008:03:36:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.58.205.45 - - [29/Feb/2008:03:38:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.58.205.45 - - [29/Feb/2008:03:40:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.58.205.45 - - [29/Feb/2008:03:47:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.10.60.85 - - [29/Feb/2008:03:48:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 -217.10.60.85 - - [29/Feb/2008:03:49:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 -194.2.41.91 - - [29/Feb/2008:03:56:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -194.2.41.91 - - [29/Feb/2008:03:56:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -194.2.41.91 - - [29/Feb/2008:03:56:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 -117.47.114.193 - - [29/Feb/2008:03:59:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 -117.47.114.193 - - [29/Feb/2008:03:59:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -194.2.41.91 - - [29/Feb/2008:03:59:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -218.186.13.1 - - [29/Feb/2008:04:03:53 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE002.HTM HTTP/1.1" 200 1352 -218.186.13.1 - - [29/Feb/2008:04:03:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.196.43.134 - - [29/Feb/2008:04:05:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 -66.232.113.62 - - [29/Feb/2008:04:06:28 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 -202.183.216.180 - - [29/Feb/2008:04:06:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -201.62.170.203 - - [29/Feb/2008:04:06:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -213.227.137.187 - - [29/Feb/2008:04:06:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -80.227.1.101 - - [29/Feb/2008:04:07:01 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -74.6.25.70 - - [29/Feb/2008:04:20:50 -0600] "GET /gifplot/index.html HTTP/1.0" 200 40215 -74.6.26.75 - - [29/Feb/2008:04:23:42 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 -193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 -193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12706 -193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -193.206.186.101 - - [29/Feb/2008:04:30:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -193.206.186.101 - - [29/Feb/2008:04:32:37 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -74.6.19.72 - - [29/Feb/2008:04:39:31 -0600] "GET /dynamic/assign2.html HTTP/1.0" 200 4907 -74.6.23.12 - - [29/Feb/2008:04:43:13 -0600] "GET /dynamic/dowstocks.csv HTTP/1.0" 200 589814 -206.51.226.87 - - [29/Feb/2008:04:43:17 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 -218.64.214.110 - - [29/Feb/2008:04:43:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -200.195.243.236 - - [29/Feb/2008:04:43:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -196.217.249.190 - - [29/Feb/2008:04:43:43 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -203.199.177.121 - - [29/Feb/2008:04:43:47 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -80.97.94.178 - - [29/Feb/2008:04:43:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -87.18.28.170 - - [29/Feb/2008:04:44:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 -41.232.219.113 - - [29/Feb/2008:04:44:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 -87.18.28.170 - - [29/Feb/2008:04:44:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -87.18.28.170 - - [29/Feb/2008:04:44:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -41.232.219.113 - - [29/Feb/2008:04:44:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -41.232.219.113 - - [29/Feb/2008:04:44:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -129.217.150.45 - - [29/Feb/2008:04:46:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 -129.217.150.45 - - [29/Feb/2008:04:46:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -129.217.150.45 - - [29/Feb/2008:04:46:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -70.90.215.85 - - [29/Feb/2008:04:48:43 -0600] "GET /robots.txt HTTP/1.0" 200 71 -193.0.96.15 - - [29/Feb/2008:04:51:09 -0600] "GET /ply/ HTTP/1.0" 304 - -193.0.96.15 - - [29/Feb/2008:04:51:09 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - -193.0.96.15 - - [29/Feb/2008:04:51:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -84.110.221.201 - - [29/Feb/2008:04:52:22 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 -84.110.221.201 - - [29/Feb/2008:04:52:22 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1071 -81.80.245.157 - - [29/Feb/2008:04:53:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -81.80.245.157 - - [29/Feb/2008:04:54:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -84.165.112.79 - - [29/Feb/2008:04:55:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -84.165.112.79 - - [29/Feb/2008:04:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -86.125.158.237 - - [29/Feb/2008:04:58:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -74.6.7.146 - - [29/Feb/2008:04:58:16 -0600] "GET /swill/software.html HTTP/1.0" 404 133 -74.6.25.23 - - [29/Feb/2008:05:03:37 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE052.HTM HTTP/1.0" 200 1459 -74.6.20.166 - - [29/Feb/2008:05:11:50 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 -67.195.58.174 - - [29/Feb/2008:05:20:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 -74.6.26.209 - - [29/Feb/2008:05:23:06 -0600] "GET /python/consulting.html HTTP/1.0" 404 133 -68.37.149.27 - - [29/Feb/2008:05:24:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 -89.165.73.226 - - [29/Feb/2008:05:32:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -89.165.73.226 - - [29/Feb/2008:05:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.226 - - [29/Feb/2008:05:33:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.165.73.226 - - [29/Feb/2008:05:33:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -91.103.40.50 - - [29/Feb/2008:05:42:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -62.96.202.33 - - [29/Feb/2008:05:45:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -62.96.202.33 - - [29/Feb/2008:05:45:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.96.202.33 - - [29/Feb/2008:05:45:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.96.202.33 - - [29/Feb/2008:05:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -62.96.202.33 - - [29/Feb/2008:05:45:53 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 -210.197.158.144 - - [29/Feb/2008:05:56:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 -217.127.12.71 - - [29/Feb/2008:06:10:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -217.127.12.71 - - [29/Feb/2008:06:10:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.127.12.71 - - [29/Feb/2008:06:10:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -217.127.12.71 - - [29/Feb/2008:06:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 -125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -125.238.252.69 - - [29/Feb/2008:06:16:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -201.236.226.90 - - [29/Feb/2008:06:20:12 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - -82.211.198.146 - - [29/Feb/2008:06:20:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -82.211.198.146 - - [29/Feb/2008:06:20:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -82.211.198.146 - - [29/Feb/2008:06:20:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 -82.211.198.146 - - [29/Feb/2008:06:20:27 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 -81.255.174.7 - - [29/Feb/2008:06:24:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 -81.255.174.7 - - [29/Feb/2008:06:24:51 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -81.255.174.7 - - [29/Feb/2008:06:24:51 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -62.160.169.7 - - [29/Feb/2008:06:24:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -81.255.174.7 - - [29/Feb/2008:06:24:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -217.128.46.55 - - [29/Feb/2008:06:27:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 -217.128.46.55 - - [29/Feb/2008:06:27:08 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -65.55.104.13 - - [29/Feb/2008:06:29:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 -65.55.104.13 - - [29/Feb/2008:06:29:25 -0600] "GET / HTTP/1.1" 200 4447 -74.6.25.20 - - [29/Feb/2008:06:34:27 -0600] "GET /robots.txt HTTP/1.0" 200 71 -67.195.58.169 - - [29/Feb/2008:06:34:42 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 -212.246.151.62 - - [29/Feb/2008:06:38:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -212.246.151.62 - - [29/Feb/2008:06:38:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -212.246.151.62 - - [29/Feb/2008:06:38:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.58.205.45 - - [29/Feb/2008:06:43:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -74.6.28.166 - - [29/Feb/2008:06:44:56 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE106.HTM HTTP/1.0" 304 - -89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [29/Feb/2008:06:58:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 -66.249.65.37 - - [29/Feb/2008:06:58:26 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 304 - -200.19.92.10 - - [29/Feb/2008:06:58:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 -200.19.92.10 - - [29/Feb/2008:06:58:30 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -200.19.92.10 - - [29/Feb/2008:06:58:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -131.111.113.139 - - [29/Feb/2008:07:03:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -131.111.113.139 - - [29/Feb/2008:07:03:40 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -190.198.190.239 - - [29/Feb/2008:07:04:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 -190.198.190.239 - - [29/Feb/2008:07:05:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -66.249.65.37 - - [29/Feb/2008:07:05:18 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 304 - -66.249.65.37 - - [29/Feb/2008:07:06:23 -0600] "GET /dynamic/soln1.html HTTP/1.1" 304 - -80.120.2.52 - - [29/Feb/2008:07:09:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 -80.120.2.52 - - [29/Feb/2008:07:09:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -66.249.65.37 - - [29/Feb/2008:07:11:32 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 304 - -66.249.65.37 - - [29/Feb/2008:07:13:19 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 -66.249.65.37 - - [29/Feb/2008:07:14:41 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 304 - -66.232.113.194 - - [29/Feb/2008:07:19:49 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -80.227.1.101 - - [29/Feb/2008:07:19:52 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 -201.25.119.178 - - [29/Feb/2008:07:20:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -201.25.119.178 - - [29/Feb/2008:07:20:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -220.225.196.123 - - [29/Feb/2008:07:20:26 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -202.97.149.167 - - [29/Feb/2008:07:20:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -87.249.53.100 - - [29/Feb/2008:07:20:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -206.51.237.114 - - [29/Feb/2008:07:23:32 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 -123.190.193.8 - - [29/Feb/2008:07:23:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -194.177.202.247 - - [29/Feb/2008:07:23:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 -66.249.65.37 - - [29/Feb/2008:07:23:52 -0600] "GET /dynamic/assign2.html HTTP/1.1" 304 - -66.249.65.37 - - [29/Feb/2008:07:25:09 -0600] "GET /python/python.html HTTP/1.1" 404 133 -66.249.65.37 - - [29/Feb/2008:07:27:52 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 -66.249.65.37 - - [29/Feb/2008:07:27:56 -0600] "GET /dynamic/assign3.html HTTP/1.1" 304 - -200.19.92.58 - - [29/Feb/2008:07:31:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 -200.19.92.58 - - [29/Feb/2008:07:31:08 -0600] "GET /favicon.ico HTTP/1.0" 404 133 -200.19.92.58 - - [29/Feb/2008:07:31:09 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 -66.249.65.37 - - [29/Feb/2008:07:31:24 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 304 - -81.222.64.10 - - [29/Feb/2008:07:31:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 -74.6.26.11 - - [29/Feb/2008:07:31:48 -0600] "GET /photos/wind/pages/IMG_1321.htm HTTP/1.0" 404 133 -130.208.225.81 - - [29/Feb/2008:07:40:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 -130.208.225.81 - - [29/Feb/2008:07:40:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.208.225.81 - - [29/Feb/2008:07:40:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 -130.208.225.81 - - [29/Feb/2008:07:40:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -130.208.225.81 - - [29/Feb/2008:07:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -130.208.225.81 - - [29/Feb/2008:07:40:52 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -80.161.85.77 - - [29/Feb/2008:07:44:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 -80.161.85.77 - - [29/Feb/2008:07:44:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -80.161.85.77 - - [29/Feb/2008:07:44:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.161.85.77 - - [29/Feb/2008:07:45:07 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 -80.161.85.77 - - [29/Feb/2008:07:45:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.161.85.77 - - [29/Feb/2008:07:47:40 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 -156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 -156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 -156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 -80.161.85.77 - - [29/Feb/2008:07:52:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0108.bz2 b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0108.bz2 deleted file mode 100644 index 805c9ebbf1292ea56e2db7d7a1e1939d20de6240..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46105 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 diff --git a/chef/cookbooks/python/src/4/creating_new_iteration_patterns_with_generators/example.py b/chef/cookbooks/python/src/4/creating_new_iteration_patterns_with_generators/example.py deleted file mode 100644 index b4e5cf7..0000000 --- a/chef/cookbooks/python/src/4/creating_new_iteration_patterns_with_generators/example.py +++ /dev/null @@ -1,8 +0,0 @@ -def frange(start, stop, increment): - x = start - while x < stop: - yield x - x += increment - -for n in frange(0, 4, 0.5): - print(n) diff --git a/chef/cookbooks/python/src/4/delegating-iteration/example.py b/chef/cookbooks/python/src/4/delegating-iteration/example.py deleted file mode 100644 index ebbca8d..0000000 --- a/chef/cookbooks/python/src/4/delegating-iteration/example.py +++ /dev/null @@ -1,26 +0,0 @@ -# Example of delegating iteration to an internal container - -class Node: - def __init__(self, value): - self._value = value - self._children = [] - - def __repr__(self): - return 'Node({!r})'.format(self._value) - - def add_child(self, node): - self._children.append(node) - - def __iter__(self): - return iter(self._children) - -# Example -if __name__ == '__main__': - root = Node(0) - child1 = Node(1) - child2 = Node(2) - root.add_child(child1) - root.add_child(child2) - for ch in root: - print(ch) - # Outputs: Node(1), Node(2) diff --git a/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/example.py b/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/example.py deleted file mode 100644 index df9f0ed..0000000 --- a/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/example.py +++ /dev/null @@ -1,37 +0,0 @@ -# example.py -# -# Example of depth-first search using a generator - -class Node: - def __init__(self, value): - self._value = value - self._children = [] - - def __repr__(self): - return 'Node({!r})'.format(self._value) - - def add_child(self, node): - self._children.append(node) - - def __iter__(self): - return iter(self._children) - - def depth_first(self): - yield self - for c in self: - yield from c.depth_first() - -# Example -if __name__ == '__main__': - root = Node(0) - child1 = Node(1) - child2 = Node(2) - root.add_child(child1) - root.add_child(child2) - child1.add_child(Node(3)) - child1.add_child(Node(4)) - child2.add_child(Node(5)) - - for ch in root.depth_first(): - print(ch) - # Outputs: Node(0), Node(1), Node(3), Node(4), Node(2), Node(5) diff --git a/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/hardexample.py b/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/hardexample.py deleted file mode 100644 index 71de3d6..0000000 --- a/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/hardexample.py +++ /dev/null @@ -1,66 +0,0 @@ -# Hard example of depth-first iteration using an iterator object - -class Node: - def __init__(self, value): - self._value = value - self._children = [] - - def __repr__(self): - return 'Node(%r)' % self._value - - def add_child(self, other_node): - self._children.append(other_node) - - def __iter__(self): - return iter(self._children) - - def depth_first(self): - return DepthFirstIterator(self) - -class DepthFirstIterator(object): - ''' - Depth-first traversal - ''' - def __init__(self, start_node): - self._node = start_node - self._children_iter = None - self._child_iter = None - - def __iter__(self): - return self - - def __next__(self): - # Return myself if just started. Create an iterator for children - if self._children_iter is None: - self._children_iter = iter(self._node) - return self._node - - # If processing a child, return its next item - elif self._child_iter: - try: - nextchild = next(self._child_iter) - return nextchild - except StopIteration: - self._child_iter = None - return next(self) - - # Advance to the next child and start its iteration - else: - self._child_iter = next(self._children_iter).depth_first() - return next(self) - - -# Example -if __name__ == '__main__': - root = Node(0) - child1 = Node(1) - child2 = Node(2) - root.add_child(child1) - root.add_child(child2) - child1.add_child(Node(3)) - child1.add_child(Node(4)) - child2.add_child(Node(5)) - - for ch in root.depth_first(): - print(ch) - # Outputs: Node(0), Node(1), Node(3), Node(4), Node(2), Node(5) diff --git a/chef/cookbooks/python/src/4/generators_with_state/example.py b/chef/cookbooks/python/src/4/generators_with_state/example.py deleted file mode 100644 index 32f55d8..0000000 --- a/chef/cookbooks/python/src/4/generators_with_state/example.py +++ /dev/null @@ -1,29 +0,0 @@ -# Example of a generator with extra state that can be -# accessed. Simply define as a class! - -from collections import deque - -class linehistory: - def __init__(self, lines, histlen=3): - self.lines = lines - self.history = deque(maxlen=histlen) - - def __iter__(self): - for lineno, line in enumerate(self.lines,1): - self.history.append((lineno, line)) - yield line - - def clear(self): - self.history.clear() - -with open('somefile.txt') as f: - lines = linehistory(f) - for line in lines: - if 'python' in line: - for lineno, hline in lines.history: - print('{}:{}'.format(lineno, hline), end='') - - - - - diff --git a/chef/cookbooks/python/src/4/generators_with_state/somefile.txt b/chef/cookbooks/python/src/4/generators_with_state/somefile.txt deleted file mode 100644 index 01841a2..0000000 --- a/chef/cookbooks/python/src/4/generators_with_state/somefile.txt +++ /dev/null @@ -1,4 +0,0 @@ -hello world -this is a test -of iterating over lines with a history -python is fun diff --git a/chef/cookbooks/python/src/4/how_to_flatten_a_nested_sequence/example.py b/chef/cookbooks/python/src/4/how_to_flatten_a_nested_sequence/example.py deleted file mode 100644 index 5f69236..0000000 --- a/chef/cookbooks/python/src/4/how_to_flatten_a_nested_sequence/example.py +++ /dev/null @@ -1,20 +0,0 @@ -# Example of flattening a nested sequence using subgenerators - -from collections import Iterable - -def flatten(items, ignore_types=(str, bytes)): - for x in items: - if isinstance(x, Iterable) and not isinstance(x, ignore_types): - yield from flatten(x) - else: - yield x - -items = [1, 2, [3, 4, [5, 6], 7], 8] - -# Produces 1 2 3 4 5 6 7 8 -for x in flatten(items): - print(x) - -items = ['Dave', 'Paula', ['Thomas', 'Lewis']] -for x in flatten(items): - print(x) diff --git a/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/example.py b/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/example.py deleted file mode 100644 index 580a13b..0000000 --- a/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/example.py +++ /dev/null @@ -1,11 +0,0 @@ -# Example of iterating over lines of a file with an extra lineno attribute -def parse_data(filename): - with open(filename, 'rt') as f: - for lineno, line in enumerate(f, 1): - fields = line.split() - try: - count = int(fields[1]) - except ValueError as e: - print('Line {}: Parse error: {}'.format(lineno, e)) - -parse_data('sample.dat') diff --git a/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/sample.dat b/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/sample.dat deleted file mode 100644 index 493cc58..0000000 --- a/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/sample.dat +++ /dev/null @@ -1,6 +0,0 @@ -0 1 -2 3 -4 N/A -5 6 -7 8 -9 10 diff --git a/chef/cookbooks/python/src/4/iterating_in_reverse/example.py b/chef/cookbooks/python/src/4/iterating_in_reverse/example.py deleted file mode 100644 index f116252..0000000 --- a/chef/cookbooks/python/src/4/iterating_in_reverse/example.py +++ /dev/null @@ -1,28 +0,0 @@ -# Example of an object implementing both forward and reversed iterators - -class Countdown: - def __init__(self, start): - self.start = start - - # Forward iterator - def __iter__(self): - n = self.start - while n > 0: - yield n - n -= 1 - - # Reverse iterator - def __reversed__(self): - n = 1 - while n <= self.start: - yield n - n += 1 - -c = Countdown(5) -print("Forward:") -for x in c: - print(x) - -print("Reverse:") -for x in reversed(c): - print(x) diff --git a/chef/cookbooks/python/src/4/iterating_in_sorted_order_over_merged_sorted_iterables/example.py b/chef/cookbooks/python/src/4/iterating_in_sorted_order_over_merged_sorted_iterables/example.py deleted file mode 100644 index 642ace9..0000000 --- a/chef/cookbooks/python/src/4/iterating_in_sorted_order_over_merged_sorted_iterables/example.py +++ /dev/null @@ -1,8 +0,0 @@ -# Iterating over merged sorted iterables - -import heapq -a = [1, 4, 7, 10] -b = [2, 5, 6, 11] -for c in heapq.merge(a, b): - print(c) - diff --git a/chef/cookbooks/python/src/4/iterating_on_items_in_separate_containers/example.py b/chef/cookbooks/python/src/4/iterating_on_items_in_separate_containers/example.py deleted file mode 100644 index 43e4f1b..0000000 --- a/chef/cookbooks/python/src/4/iterating_on_items_in_separate_containers/example.py +++ /dev/null @@ -1,8 +0,0 @@ -# Example of iterating over two sequences as one - -from itertools import chain -a = [1, 2, 3, 4] -b = ['x', 'y', 'z'] -for x in chain(a, b): - print(x) - diff --git a/chef/cookbooks/python/src/5/adding_or_changing_the_encoding_of_an_already_open_file/example.py b/chef/cookbooks/python/src/5/adding_or_changing_the_encoding_of_an_already_open_file/example.py deleted file mode 100644 index fea4899..0000000 --- a/chef/cookbooks/python/src/5/adding_or_changing_the_encoding_of_an_already_open_file/example.py +++ /dev/null @@ -1,11 +0,0 @@ -# Example of adding a text encoding to existing file-like object - -import urllib.request -import io - -u = urllib.request.urlopen('http://www.python.org') -f = io.TextIOWrapper(u, encoding='utf-8') -text = f.read() - -print(text) - diff --git a/chef/cookbooks/python/src/5/getting_a_directory_listing/example.py b/chef/cookbooks/python/src/5/getting_a_directory_listing/example.py deleted file mode 100644 index 6f25844..0000000 --- a/chef/cookbooks/python/src/5/getting_a_directory_listing/example.py +++ /dev/null @@ -1,19 +0,0 @@ -# Example of getting a directory listing - -import os -import os.path -import glob - -pyfiles = glob.glob('*.py') - -# Get file sizes and modification dates -name_sz_date = [(name, os.path.getsize(name), os.path.getmtime(name)) - for name in pyfiles] - -for r in name_sz_date: - print(r) - -# Get file metadata -file_metadata = [(name, os.stat(name)) for name in pyfiles] -for name, meta in file_metadata: - print(name, meta.st_size, meta.st_mtime) diff --git a/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/data.bin b/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/data.bin deleted file mode 100644 index 90d9316..0000000 --- a/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/data.bin +++ /dev/null @@ -1 +0,0 @@ - 0 5412 N CLARK 3 5148 N CLARK 10 5800 E 58TH 4 2122 N CLARK 1 5645 N RAVENSWOOD 7 1060 W ADDISON 6 4801 N BROADWAY 1 1039 W GRANVILLE \ No newline at end of file diff --git a/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/example.py b/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/example.py deleted file mode 100644 index 0c543f2..0000000 --- a/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/example.py +++ /dev/null @@ -1,13 +0,0 @@ -# Example of iterating of fixed-size records -# -# The file 'data.bin' contains 32-byte fixed size records -# that consist of a 4-digit number followed by a 28-byte string. - -from functools import partial -RECORD_SIZE = 32 - -with open('data.bin', 'rb') as f: - records = iter(partial(f.read, RECORD_SIZE), b'') - for r in records: - print(r) - diff --git a/chef/cookbooks/python/src/5/reading_and_writing_text_data/example.py b/chef/cookbooks/python/src/5/reading_and_writing_text_data/example.py deleted file mode 100644 index 98f3ada..0000000 --- a/chef/cookbooks/python/src/5/reading_and_writing_text_data/example.py +++ /dev/null @@ -1,30 +0,0 @@ -# Some examples of reading text files with different options -# -# The file sample.txt is a UTF-8 encoded text file with Windows -# line-endings (\r\n). - -# (a) Reading a basic text file (UTF-8 default encoding) - -print("Reading a simple text file (UTF-8)") -with open('sample.txt', 'rt') as f: - for line in f: - print(repr(line)) - -# (b) Reading a text file with universal newlines turned off -print("Reading text file with universal newlines off") -with open('sample.txt', 'rt', newline='') as f: - for line in f: - print(repr(line)) - -# (c) Reading text file as ASCII with replacement error handling -print("Reading text as ASCII with replacement error handling") -with open('sample.txt', 'rt', encoding='ascii', errors='replace') as f: - for line in f: - print(repr(line)) - -# (d) Reading text file as ASCII with ignore error handling -print("Reading text as ASCII with ignore error handling") -with open('sample.txt', 'rt', encoding='ascii', errors='ignore') as f: - for line in f: - print(repr(line)) - diff --git a/chef/cookbooks/python/src/5/reading_and_writing_text_data/sample.txt b/chef/cookbooks/python/src/5/reading_and_writing_text_data/sample.txt deleted file mode 100644 index 3fe0544..0000000 --- a/chef/cookbooks/python/src/5/reading_and_writing_text_data/sample.txt +++ /dev/null @@ -1,2 +0,0 @@ -Hello World -Spicy Jalapeño diff --git a/chef/cookbooks/python/src/5/wrapping_an_existing_file_descriptor_as_a_file_object/echo.py b/chef/cookbooks/python/src/5/wrapping_an_existing_file_descriptor_as_a_file_object/echo.py deleted file mode 100644 index c68fb09..0000000 --- a/chef/cookbooks/python/src/5/wrapping_an_existing_file_descriptor_as_a_file_object/echo.py +++ /dev/null @@ -1,26 +0,0 @@ -from socket import socket, AF_INET, SOCK_STREAM - -def echo_client(client_sock, addr): - print("Got connection from", addr) - - # Make text-mode file wrappers for socket reading/writing - client_in = open(client_sock.fileno(), 'rt', encoding='latin-1', closefd=False) - client_out = open(client_sock.fileno(), 'wt', encoding='latin-1', closefd=False) - - # Echo lines back to the client using file I/O - for line in client_in: - client_out.write(line) - client_out.flush() - client_sock.close() - -def echo_server(address): - sock = socket(AF_INET, SOCK_STREAM) - sock.bind(address) - sock.listen(1) - while True: - client, addr = sock.accept() - echo_client(client, addr) - -if __name__ == '__main__': - print('Echo serving running on localhost:25000') - echo_server(('', 25000)) diff --git a/chef/cookbooks/python/src/5/writing_bytes_to_a_text_file/example.py b/chef/cookbooks/python/src/5/writing_bytes_to_a_text_file/example.py deleted file mode 100644 index 45274ae..0000000 --- a/chef/cookbooks/python/src/5/writing_bytes_to_a_text_file/example.py +++ /dev/null @@ -1,9 +0,0 @@ -# Example of writing raw bytes on a file opened in text mode - -import sys - -# A byte string -data = b'Hello World\n' - -# Write onto the buffer attribute (bypassing text encoding) -sys.stdout.buffer.write(data) diff --git a/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/example.py b/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/example.py deleted file mode 100644 index 428b192..0000000 --- a/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/example.py +++ /dev/null @@ -1,42 +0,0 @@ -# Example of incremental XML parsing -# -# The file 'potholes.xml' is a greatly condensed version of a larger -# file available for download at -# -# https://data.cityofchicago.org/api/views/7as2-ds3y/rows.xml?accessType=DOWNLOAD - -from xml.etree.ElementTree import iterparse - -def parse_and_remove(filename, path): - path_parts = path.split('/') - doc = iterparse(filename, ('start', 'end')) - # Skip the root element - next(doc) - - tag_stack = [] - elem_stack = [] - for event, elem in doc: - if event == 'start': - tag_stack.append(elem.tag) - elem_stack.append(elem) - elif event == 'end': - if tag_stack == path_parts: - yield elem - elem_stack[-2].remove(elem) - try: - tag_stack.pop() - elem_stack.pop() - except IndexError: - pass - -# Find zip code with most potholes - -from collections import Counter -potholes_by_zip = Counter() - -data = parse_and_remove('potholes.xml', 'row/row') -for pothole in data: - potholes_by_zip[pothole.findtext('zip')] += 1 - -for zipcode, num in potholes_by_zip.most_common(): - print(zipcode, num) diff --git a/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/potholes.xml b/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/potholes.xml deleted file mode 100644 index 50cff26..0000000 --- a/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/potholes.xml +++ /dev/null @@ -1 +0,0 @@ -2012-12-19T00:00:00Open12-02020904Pot Hole in Street2920 W BERTEAU AVE606181156066.361768711927765.9484328233171641.957581829809655-87.701641097511192012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019619Pot Hole in StreetFinal OutcomePothole Patched43100 E 91ST ST606171198477.930077221845218.041123861044641.7301045945226-87.548498614855972012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019580Pot Hole in StreetFinal OutcomePothole Patched27955 S YATES BLVD606171193574.97740621852682.85992567744641.7507099762241-87.566215496320072012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019611Pot Hole in StreetFinal OutcomePothole Patched32432 E 85TH ST606171193978.129408951849090.7837193744641.74084314846648-87.564855740304172012-12-19T00:00:00Completed2012-12-19T00:00:0012-02020737Pot Hole in StreetFinal OutcomeCDOT Street Cut Complaints Transfer Outcome1045 N LEAVITT ST606221161546.433317581907023.3319016532132441.90055013339103-87.682073769876582012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019763Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome2920 N WOLCOTT AVE606571163167.176581291919489.616577563219541.93472453511495-87.675769614882972012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019689Pot Hole in StreetFinal OutcomePothole Patched1879 E 87TH ST606191183832.149967161847487.15981401864441.736685241835445-87.602079173420082012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019673Pot Hole in StreetFinal OutcomePothole Patched1113 E 89TH ST606191178769.99988161846008.70128963664441.73274470299228-87.620669965886962012-12-19T00:00:00Completed2012-12-19T00:00:0012-02020693Pot Hole in StreetFinal OutcomeCDOT Street Cut Complaints Transfer Outcome3939 N GREENVIEW AVE606131165370.643006821926241.561024174719641.95320554373681-87.667479116856472012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019897Pot Hole in StreetFinal OutcomePothole Patched35146 W MADISON ST606441142015.870007911899534.9240188728152541.88038548666047-87.753996959426392012-12-19T00:00:00Completed2012-12-19T00:00:0012-02020428Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome729 W OHIO ST606541171188.825614231904077.4996863527132441.89226026539505-87.646743537803082012-12-19T00:00:00Open12-02016542Pot Hole in Street6733 N RAVENSWOOD AVE606261163128.210831711944874.41000854924142.00438228733084-87.675196177342162012-12-19T00:00:00Open12-02016538Pot Hole in Street1757 W WALLEN AVE606261163421.962229821944218.250110524024142.002575567056205-87.674134048781872012-12-19T00:00:00Open12-02018384Pot Hole in Street5832 N WAYNE AVE606601166416.780030511938817.4000296548207741.98769171278258-87.663271883457142012-12-19T00:00:00Open12-02017827Pot Hole in Street1601 W 108TH PL606431167584.129828971832775.5134824319227541.69667762299553-87.662026852236352012-12-19T00:00:00Open12-02016893Pot Hole in Street5739 W IRVING PARK RD606341137257.650074861926013.1959721538161541.95313174383805-87.770831231016232012-12-19T00:00:00Open12-02018381Pot Hole in Street5611 S ELIZABETH ST606361168947.618451867520.6816751676741.79199392572382-87.656033246291732012-12-19T00:00:00Open12-02017861Pot Hole in Street3100 W MOFFAT ST606471155214.690690931912130.789971526142241.91469498906821-87.70519328561312012-12-19T00:00:00Open12-02017942Pot Hole in Street1600 N HAMLIN AVE606471150804.150121361910366.0473089630252341.90993986745698-87.72144336821242012-12-19T00:00:00Open12-02017890Pot Hole in Street3000 W WABANSIA AVE606471156017.528910481911152.9897076426142341.91199565103413-87.702270187839022012-12-19T00:00:00Open12-02020051Pot Hole in Street4900 S KEDVALE AVE606321149522.712482521871811.643677752385741.80416684102511-87.727150814610172012-12-19T00:00:00Open12-02018862Pot Hole in Street1932 N KARLOV AVE606391148741.744007131912523.708419730252041.91590082226891-87.728964013987122012-12-19T00:00:00Open12-02018812Pot Hole in Street4967 N KOLMAR AVE606301144492.799490361932611.0693273639171441.97110345287727-87.744067178315122012-12-19T00:00:00Open12-02019299Pot Hole in Street634 W BUENA AVE606131171069.607805941928227.029870574619341.958530321478996-87.646470932735872012-12-19T00:00:00Open12-02018897Pot Hole in Street2514 W FOSTER AVE606251158477.848807461934446.829913884020441.97586539743432-87.692591987383822012-12-19T00:00:00Open12-02018977Pot Hole in Street5561 N CAMPBELL AVE606251158662.175603251936854.696367994020441.98246890846856-87.691847835569082012-12-19T00:00:00Open12-02018907Pot Hole in Street9500 S STONY ISLAND AVE606171188504.434302771842299.31671488855141.72233904922372-87.585126874465392012-12-19T00:00:00Open12-02020260Pot Hole in Street3700 N PINE GROVE AVE606131171046.075995151925024.455267124619641.94974286922904-87.646651783685942012-12-19T00:00:00Completed2012-12-19T00:00:0012-02017879Pot Hole in StreetFinal OutcomePothole Patched239800 S PARNELL AVE606281174361.22248291839902.9701241421227341.71608877775062-87.637002185453452012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019438Pot Hole in StreetFinal OutcomePothole Patched12300 S TROY ST606231155719.367228041888546.8520800424103041.84996817801136-87.703974573182552012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018921Pot Hole in StreetFinal OutcomePothole Patched282700 E 81ST ST606171195580.718124521851783.73762544744641.748193407358585-87.55889531375412012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018900Pot Hole in StreetFinal OutcomePothole Patched74846 S WOLCOTT AVE606091164503.20981471872542.118332252096141.80586828661782-87.672188548470092012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018451Pot Hole in StreetFinal OutcomePothole Patched15400 N CLAREMONT AVE606121160670.990029981902655.1801259127132441.8885817539312-87.685410481665672012-12-19T00:00:00Open12-02020574Pot Hole in Street2432 S NB LSD OB STEVENSON ER606161180671.865912361888412.47283406213341.84906109948375-87.612399963070962012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018351Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome2523 W MARQUETTE RD606291160614.628816371860143.032324791586641.77192472731556-87.68679243658212012-12-19T00:00:00Completed2012-12-19T00:00:0012-02017916Pot Hole in StreetFinal OutcomePothole Patched89800 S LOWE AVE606281173699.674325471839888.4627214621227341.716063624039734-87.639425532132482012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018523Pot Hole in StreetFinal OutcomePothole Patched102600 N RACINE AVE606141167861.483594151917411.986392253219741.92892331669152-87.658578137042272012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018867Pot Hole in StreetFinal OutcomePothole Patched184850 S WOOD ST606091165156.790717451872863.873075212096141.80673739778276-87.66978234464462012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018949Pot Hole in StreetFinal OutcomePothole Patched22650 E 75TH ST606491195304.849483621855816.62177611734341.75926675632701-87.559773296799722012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018953Pot Hole in StreetFinal OutcomePothole Patched202901 E 82ND ST606171196930.818209271851157.41767415744641.746441286589324-87.553968993024012012-12-19T00:00:00Open12-02018663Pot Hole in Street8600 S ESCANABA AVE606171196975.311603061848507.592496371044641.73916885584215-87.553893907909332012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018922Pot Hole in StreetFinal OutcomePothole Patched158100 S MANISTEE AVE606171195911.029523481851792.62908157744641.74820964166295-87.557684674779212012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019450Pot Hole in StreetFinal OutcomePothole Patched73240 W 23RD ST606231155023.809111451888530.476505922103041.84993720174623-87.706527830931832012-12-19T00:00:00Open12-02020655Pot Hole in Street4700 N LEAVITT ST606251160841.911505311931178.761984084719441.966848819565755-87.68398952242462012-12-19T00:00:00Open12-02016521Pot Hole in Street1738 W ALBION AVE606261163600.381885511943891.479477394024142.0016751289695-87.673486924070252012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019461Pot Hole in StreetFinal OutcomePothole Patched42252 S SPAULDING AVE606231154715.840027621888841.0699707422103041.850795669247546-87.707649825027432012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018943Pot Hole in StreetFinal OutcomePothole Patched697500 S COLES AVE606491195618.733434211856037.07646802744341.75986394886502-87.558615671729112012-12-19T00:00:00Open12-02020182Pot Hole in Street2937 N KILPATRICK AVE606411144538.435867811919116.5525243931251941.93407248160905-87.744240446607142012-12-19T00:00:00Open12-02020379Pot Hole in Street3600 W 86TH ST606521153829.165088231847310.569993351887041.736847628628695-87.712005771594732012-12-19T00:00:00Open12-02016541Pot Hole in Street6723 N RAVENSWOOD AVE606261163130.910831711944784.51000854924142.004135542542144-87.675188786476712012-12-19T00:00:00Open12-02016714Pot Hole in Street6800 W DICKENS AVE607071130743.647000131913216.180002736252541.91813004984083-87.795072980484482012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018886Pot Hole in StreetFinal OutcomePothole Patched14931 S WOLCOTT AVE606091164519.226469771871953.963776681696141.80425398259015-87.67214639971472012-12-19T00:00:00Open12-02020903Pot Hole in Street4201 N SACRAMENTO AVE606181155630.228005161927768.1339811333171641.957596637909006-87.703244414243762012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019614Pot Hole in StreetFinal OutcomePothole Patched42900 E 85TH ST606171196961.347305751849170.485810921044641.74098823420314-87.553923073057622012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019318Pot Hole in StreetFinal OutcomeGAS Peoples Gas Transfer Outcome04812 N WESTERN AVE606251159495.937051931954.017154720441.96900404348566-87.688917043899022012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019667Pot Hole in StreetFinal OutcomePothole Patched175 E 89TH ST606191178522.49988161846000.50128963664441.732727821186984-87.621576913168412012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019539Pot Hole in StreetFinal OutcomePothole Patched472216 S CHRISTIANA AVE606231154374.522970681889069.4257695722103041.851429121666584-87.708896440135332012-12-19T00:00:00Open12-02016533Pot Hole in Street1727 W WALLEN AVE606261163618.009573751944224.556016474024142.002588726496306-87.673412629962682012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019551Pot Hole in StreetFinal OutcomePothole Patched263299 W 23RD ST606231154727.740027621888531.2699707422103041.84994530413951-87.707614434444762012-12-19T00:00:00Open12-02016505Pot Hole in Street6709 N RAVENSWOOD AVE606261163135.159676811944636.209994114924142.00372851396752-87.67517734909632012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019333Pot Hole in StreetFinal OutcomePothole Patched391900 W 56TH ST606361164643.077510331867534.738994171576741.79212447623052-87.671816899249412012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019314Pot Hole in StreetFinal OutcomePothole Patched272500 W MARQUETTE RD606291160863.126714041860150.359428591586641.77193970401319-87.685881313787492012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019288Pot Hole in StreetFinal OutcomeNo Potholes Found0600 N UNION AVE606541171402.85882131904217.0849169827132441.89263859350436-87.645953383388362012-12-19T00:00:00Open12-02016535Pot Hole in Street1734 W WALLEN AVE606261163572.209573751944223.056016474924142.00258557900276-87.673581166473232012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019339Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome650 W BUENA AVE606131171006.807805941928225.129870574619341.95852648885717-87.646701863647042012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019215Pot Hole in StreetFinal OutcomePothole Patched281011 S AUSTIN BLVD606441136525.838472871895323.1099261129152541.86892761647423-87.774256847137012012-12-19T00:00:00Open12-02020315Pot Hole in Street6500 W BRYN MAWR AVE606311131870.811919781936822.0363017341161041.98288759839332-87.790382187702892012-12-19T00:00:00Completed2012-12-19T00:00:0012-02017865Pot Hole in StreetFinal OutcomePothole Patched55399 W WARNER AVE606411139607.3389031927064.680002838161541.95597446983892-87.762167718529472012-12-18T00:00:00Open12-02015927Pot Hole in Street2300 E 89TH ST606171193050.075296681846409.73700587844841.733508808623625-87.568343302097692012-12-18T00:00:00Open12-02016049Pot Hole in Street6200 S SPRINGFIELD AVE606291151435.905601031863206.10228361386541.78051469899573-87.720359079303492012-12-18T00:00:00Open12-02014821Pot Hole in Street10100 S LOWE AVE606281173753.184877911837902.3415178134227341.71061223782308-87.639288184652412012-12-18T00:00:00Open12-02014829Pot Hole in Street10050 S PARNELL AVE606281174396.554091521838582.828098619227341.71246533613287-87.636911891281512012-12-18T00:00:00Open12-02015911Pot Hole in Street8743 S CORNELL AVE606171188767.339885591847238.24499717844841.73588570326689-87.584006416345992012-12-18T00:00:00Open12-02015921Pot Hole in Street1640 E 88TH ST606171188820.351914971846958.7811206844841.735117559761015-87.583821118987972012-12-18T00:00:00Open12-02011376Pot Hole in Street2400 S KEELER AVE606231148769.329986821887578.649984422103041.847448249396926-87.729507482569322012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012781Pot Hole in StreetFinal OutcomePothole Patched22047 N KIMBALL AVE606471153376.144988391913496.1300875335142241.91847835197719-87.711911540612952012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013038Pot Hole in StreetFinal OutcomePothole Patched53517 W HIRSCH ST606511152660.557413871909086.5946263126142341.90639238848314-87.71465754470772012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012767Pot Hole in StreetFinal OutcomePothole Patched122025 N SPAULDING AVE606471153820.938814681913260.2759227335142241.91782229222199-87.710283619170242012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012799Pot Hole in StreetFinal OutcomeNo Potholes Found01306 N KEDZIE AVE606511154789.017428651908532.7498680426142341.9048301975562-87.706853705952812012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012793Pot Hole in StreetFinal OutcomePothole Patched72014 N KEDZIE AVE606471154719.202980981913269.686198635142241.91783015979474-87.706983089911472012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013422Pot Hole in StreetFinal OutcomeNo Potholes Found01300 N RIDGEWAY AVE606511151136.649992491908396.2502657426252341.904528041033295-87.720273608888082012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012703Pot Hole in StreetFinal OutcomePothole Patched102350 W 66TH ST606361161779.702725481860829.594685531586641.77378463656647-87.682502573007452012-12-18T00:00:00Completed2012-12-19T00:00:0012-02011828Pot Hole in StreetFinal OutcomePothole Patched104934 W BELLE PLAINE AVE606411142625.125208961926800.1816916845161541.95519293466957-87.751080091429032012-12-18T00:00:00Completed2012-12-19T00:00:0012-02011990Pot Hole in StreetFinal OutcomePothole Patched147045 W 63RD PL606381130526.933317781861618.680083012386441.776541941755795-87.797053042389662012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012946Pot Hole in StreetFinal OutcomePothole Patched13555 W LE MOYNE ST606511152383.73850931909738.9377678426142341.90818795288391-87.715657158490972012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013057Pot Hole in StreetFinal OutcomePothole Patched23555 W HIRSCH ST606511152405.20051631909081.6442083626142341.90638385412921-87.715595706278182012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012647Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome06728 S MARSHFIELD AVE606361166496.597510371860091.892007431576741.77166102840039-87.665232380471882012-12-18T00:00:00Completed2012-12-18T00:00:0012-02015136Pot Hole in StreetFinal OutcomePothole Patched211100 S AVENUE J606171202538.004899431831972.930300591045241.6936564167099-87.53407609309342012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013356Pot Hole in StreetFinal OutcomePothole Patched61535 N LAWNDALE AVE606511151483.580199071909955.3056113126252341.908799426285036-87.718958210476832012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013347Pot Hole in StreetFinal OutcomeNo Potholes Found02919 W DIVISION ST606221156641.350105591907841.885094426132441.90289708164128-87.700068245626852012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012834Pot Hole in StreetFinal OutcomeNo Potholes Found03201 W LE MOYNE ST606511154743.831371691909792.2999952326142341.90828742043949-87.706985904904042012-12-18T00:00:00Open12-02016327Pot Hole in Street1948 W HENDERSON ST606571162762.312910441922254.030120073219541.942318756367634-87.677179732082252012-12-18T00:00:00Completed2012-12-18T00:00:0012-02015091Pot Hole in StreetFinal OutcomePothole Patched92850 N ASHLAND AVE606571165165.035675121919079.091499753219641.93355574831535-87.668439176802042012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013330Pot Hole in StreetFinal OutcomePothole Patched55813 S NOTTINGHAM AVE606381130038.055081511865180.835327462385641.78632553696458-87.79876410815342012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014737Pot Hole in StreetFinal OutcomePothole Patched24560 N FORESTVIEW AVE606561116892.730089761929173.2899099636167641.96214582973867-87.84562986179722012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013331Pot Hole in StreetFinal OutcomePothole Patched101220 N HUMBOLDT DR606221156061.657875541908054.7095913926142441.903492813736975-87.702191819695492012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014725Pot Hole in StreetFinal OutcomePothole Patched28729 W LELAND AVE606561117080.109967611930271.6832908436167641.96515703075439-87.844917904276162012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014629Pot Hole in StreetFinal OutcomePothole Patched66505 N ASHLAND AVE606261164432.099473831943294.236827754024142.0000186516124-87.670444167290362012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014591Pot Hole in StreetFinal OutcomePothole Patched154700 S PAULINA ST606091165802.342478951873540.894569322096141.80858152134749-87.667395428440782012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014626Pot Hole in StreetFinal OutcomePothole Patched311500 E 68TH ST606371187397.750120371860201.13658902534341.7714897122845-87.5886129339915 \ No newline at end of file diff --git a/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/example.py b/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/example.py deleted file mode 100644 index cd43c22..0000000 --- a/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/example.py +++ /dev/null @@ -1,21 +0,0 @@ -# example.py -# -# Example of reading an XML document, making changes, and writing it back out - -from xml.etree.ElementTree import parse, Element -doc = parse('pred.xml') -root = doc.getroot() - -# Remove a few elements -root.remove(root.find('sri')) -root.remove(root.find('cr')) - -# Insert a new element after ... -nm_index = root.getchildren().index(root.find('nm')) - -e = Element('spam') -e.text = 'This is a test' -root.insert(nm_index + 1, e) - -# Write back to a file -doc.write('newpred.xml', xml_declaration=True) diff --git a/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/pred.xml b/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/pred.xml deleted file mode 100644 index b460dec..0000000 --- a/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/pred.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - 14791 - Clark & Balmoral - - 22 - North Bound -
    North Bound
    -
    - 22 -
    -       5 MIN
    -       Howard
    -       1378
    -       22
    -   
    -
    -       15 MIN
    -       Howard
    -       1867
    -       22
    -   
    -
    diff --git a/chef/cookbooks/python/src/6/parsing_simple_xml_data/example.py b/chef/cookbooks/python/src/6/parsing_simple_xml_data/example.py deleted file mode 100644 index 148f08d..0000000 --- a/chef/cookbooks/python/src/6/parsing_simple_xml_data/example.py +++ /dev/null @@ -1,17 +0,0 @@ -from urllib.request import urlopen -from xml.etree.ElementTree import parse - -# Download the RSS feed and parse it -u = urlopen('http://planet.python.org/rss20.xml') -doc = parse(u) - -# Extract and output tags of interest -for item in doc.iterfind('channel/item'): - title = item.findtext('title') - date = item.findtext('pubDate') - link = item.findtext('link') - - print(title) - print(date) - print(link) - print() diff --git a/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/example.py b/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/example.py deleted file mode 100644 index 946c565..0000000 --- a/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/example.py +++ /dev/null @@ -1,26 +0,0 @@ -# example.py -# -# Example of XML namespace handling - -from xml.etree.ElementTree import parse - -class XMLNamespaces: - def __init__(self, **kwargs): - self.namespaces = {} - for name, uri in kwargs.items(): - self.register(name, uri) - def register(self, name, uri): - self.namespaces[name] = '{'+uri+'}' - def __call__(self, path): - return path.format_map(self.namespaces) - -doc = parse('sample.xml') -ns = XMLNamespaces(html='http://www.w3.org/1999/xhtml') - -e = doc.find(ns('content/{html}html')) -print(e) - -text = doc.findtext(ns('content/{html}html/{html}head/{html}title')) -print(text) - - diff --git a/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/sample.xml b/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/sample.xml deleted file mode 100644 index 89d0b95..0000000 --- a/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/sample.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - David Beazley - - - - Hello World - - -

    Hello World!

    - - -
    -
    diff --git a/chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/readrecords.py b/chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/readrecords.py deleted file mode 100644 index 4819e39..0000000 --- a/chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/readrecords.py +++ /dev/null @@ -1,14 +0,0 @@ -from struct import Struct - -def read_records(format, f): - record_struct = Struct(format) - chunks = iter(lambda: f.read(record_struct.size), b'') - return (record_struct.unpack(chunk) for chunk in chunks) - -# Example -if __name__ == '__main__': - with open('data.b','rb') as f: - for rec in read_records('','!','@')): - byte_order = format[0] - format = format[1:] - format = byte_order + format - setattr(self, fieldname, StructField(format, offset)) - offset += struct.calcsize(format) - setattr(self, 'struct_size', offset) - -class Structure(metaclass=StructureMeta): - def __init__(self, bytedata): - self._buffer = memoryview(bytedata) - - @classmethod - def from_file(cls, f): - return cls(f.read(cls.struct_size)) - -if __name__ == '__main__': - class PolyHeader(Structure): - _fields_ = [ - ('','!','@')): - byte_order = format[0] - format = format[1:] - format = byte_order + format - setattr(self, fieldname, StructField(format, offset)) - offset += struct.calcsize(format) - setattr(self, 'struct_size', offset) - -class Structure(metaclass=StructureMeta): - def __init__(self, bytedata): - self._buffer = memoryview(bytedata) - - @classmethod - def from_file(cls, f): - return cls(f.read(cls.struct_size)) - -if __name__ == '__main__': - class Point(Structure): - _fields_ = [ - ('','!','@')): - byte_order = format[0] - format = format[1:] - format = byte_order + format - setattr(self, fieldname, StructField(format, offset)) - offset += struct.calcsize(format) - setattr(self, 'struct_size', offset) - -class Structure(metaclass=StructureMeta): - def __init__(self, bytedata): - self._buffer = memoryview(bytedata) - - @classmethod - def from_file(cls, f): - return cls(f.read(cls.struct_size)) - -class SizedRecord: - def __init__(self, bytedata): - self._buffer = memoryview(bytedata) - - @classmethod - def from_file(cls, f, size_fmt, includes_size=True): - sz_nbytes = struct.calcsize(size_fmt) - sz_bytes = f.read(sz_nbytes) - sz, = struct.unpack(size_fmt, sz_bytes) - buf = f.read(sz - includes_size * sz_nbytes) - return cls(buf) - - def iter_as(self, code): - if isinstance(code, str): - s = struct.Struct(code) - for off in range(0, len(self._buffer), s.size): - yield s.unpack_from(self._buffer, off) - elif isinstance(code, StructureMeta): - size = code.struct_size - for off in range(0, len(self._buffer), size): - data = self._buffer[off:off+size] - yield code(data) - -if __name__ == '__main__': - class Point(Structure): - _fields_ = [ - ('Albatross' -print(make_element('item', 'Albatross', size='large', quantity=6)) -print(make_element('p','')) diff --git a/chef/cookbooks/python/src/7/functions_that_only_accept_keyword_arguments/example.py b/chef/cookbooks/python/src/7/functions_that_only_accept_keyword_arguments/example.py deleted file mode 100644 index b649fd3..0000000 --- a/chef/cookbooks/python/src/7/functions_that_only_accept_keyword_arguments/example.py +++ /dev/null @@ -1,21 +0,0 @@ -# examples of keyword-only argument functions - -# A simple keyword-only argument -def recv(maxsize, *, block=True): - print(maxsize, block) - -recv(8192, block=False) # Works -try: - recv(8192, False) # Fails -except TypeError as e: - print(e) - -# Adding keyword-only args to *args functions -def minimum(*values, clip=None): - m = min(values) - if clip is not None: - m = clip if clip > m else m - return m - -print(minimum(1, 5, 2, -5, 10)) -print(minimum(1, 5, 2, -5, 10, clip=0)) diff --git a/chef/cookbooks/python/src/7/functions_with_default_arguments/example.py b/chef/cookbooks/python/src/7/functions_with_default_arguments/example.py deleted file mode 100644 index 4ec1df7..0000000 --- a/chef/cookbooks/python/src/7/functions_with_default_arguments/example.py +++ /dev/null @@ -1,42 +0,0 @@ -# Examples of a function with default arguments - -# (a) Dangers of using a mutable default argument - -def spam(b=[]): - return b - -a = spam() -print(a) -a.append(1) -a.append(2) -b = spam() -print(b) # Carefully observe result -print('-'*10) - -# (b) Better alternative for mutable defaults -def spam(b=None): - if b is None: - b = [] - return b - -a = spam() -print(a) -a.append(1) -a.append(2) -b = spam() -print(b) -print('-'*10) - -# (c) Example of testing if an argument was supplied or not - -_no_value = object() -def spam(b=_no_value): - if b is _no_value: - print("No b value supplied") - else: - print("b=", b) - -spam() -spam(None) -spam(0) -spam([]) diff --git a/chef/cookbooks/python/src/7/inlining_callback_functions/example.py b/chef/cookbooks/python/src/7/inlining_callback_functions/example.py deleted file mode 100644 index 330836d..0000000 --- a/chef/cookbooks/python/src/7/inlining_callback_functions/example.py +++ /dev/null @@ -1,61 +0,0 @@ -# Example of implementing an inlined-callback function - -# Sample function to illustrate callback control flow - -def apply_async(func, args, *, callback): - # Compute the result - result = func(*args) - - # Invoke the callback with the result - callback(result) - -# Inlined callback implementation -from queue import Queue -from functools import wraps - -class Async: - def __init__(self, func, args): - self.func = func - self.args = args - -def inlined_async(func): - @wraps(func) - def wrapper(*args): - f = func(*args) - result_queue = Queue() - result_queue.put(None) - while True: - result = result_queue.get() - try: - a = f.send(result) - apply_async(a.func, a.args, callback=result_queue.put) - except StopIteration: - break - return wrapper - -# Sample use -def add(x, y): - return x + y - -@inlined_async -def test(): - r = yield Async(add, (2, 3)) - print(r) - r = yield Async(add, ('hello', 'world')) - print(r) - for n in range(10): - r = yield Async(add, (n, n)) - print(r) - print('Goodbye') - -if __name__ == '__main__': - # Simple test - print('# --- Simple test') - test() - - print('# --- Multiprocessing test') - import multiprocessing - pool = multiprocessing.Pool() - apply_async = pool.apply_async - test() - diff --git a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example1.py b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example1.py deleted file mode 100644 index 85f6517..0000000 --- a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example1.py +++ /dev/null @@ -1,13 +0,0 @@ -# Example of using partial() with sorting a list of (x,y) coordinates - -points = [ (1, 2), (3, 4), (5, 6), (7, 7) ] - -import math -def distance(p1, p2): - x1, y1 = p1 - x2, y2 = p2 - return math.hypot(x2 - x1, y2 - y1) - -pt = (4,3) -points.sort(key=partial(distance, pt)) -print(points) diff --git a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example2.py b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example2.py deleted file mode 100644 index bfe4f0e..0000000 --- a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example2.py +++ /dev/null @@ -1,22 +0,0 @@ -# Using partial to supply extra arguments to a callback function - -def output_result(result, log=None): - if log is not None: - log.debug('Got: %r', result) - -# A sample function -def add(x, y): - return x + y - -if __name__ == '__main__': - import logging - from multiprocessing import Pool - from functools import partial - - logging.basicConfig(level=logging.DEBUG) - log = logging.getLogger('test') - - p = Pool() - p.apply_async(add, (3, 4), callback=partial(output_result, log=log)) - p.close() - p.join() diff --git a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example3.py b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example3.py deleted file mode 100644 index 5e6b31c..0000000 --- a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example3.py +++ /dev/null @@ -1,18 +0,0 @@ -# Using partial to supply extra arguments to a class constructor -from socketserver import StreamRequestHandler, TCPServer - -class EchoHandler(StreamRequestHandler): - # ack is added keyword-only argument. *args, **kwargs are - # any normal parameters supplied (which are passed on) - def __init__(self, *args, ack, **kwargs): - self.ack = ack - super().__init__(*args, **kwargs) - def handle(self): - for line in self.rfile: - self.wfile.write(self.ack + line) - -if __name__ == '__main__': - from functools import partial - serv = TCPServer(('', 15000), partial(EchoHandler, ack=b'RECEIVED:')) - print('Echo server running on port 15000') - serv.serve_forever() diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example1.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example1.py deleted file mode 100644 index d029e62..0000000 --- a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example1.py +++ /dev/null @@ -1,12 +0,0 @@ -class A: - def spam(self): - print('A.spam') - -class B(A): - def spam(self): - print('B.spam') - super().spam() # Call parent spam() - -if __name__ == '__main__': - b = B() - b.spam() diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example2.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example2.py deleted file mode 100644 index 275dac2..0000000 --- a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example2.py +++ /dev/null @@ -1,12 +0,0 @@ -class A: - def __init__(self): - self.x = 0 - -class B(A): - def __init__(self): - super().__init__() - self.y = 1 - -if __name__ == '__main__': - b = B() - print(b.x, b.y) diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example3.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example3.py deleted file mode 100644 index 29de53a..0000000 --- a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example3.py +++ /dev/null @@ -1,31 +0,0 @@ -class Proxy: - def __init__(self, obj): - self._obj = obj - - # Delegate attribute lookup to internal obj - def __getattr__(self, name): - return getattr(self._obj, name) - - # Delegate attribute assignment - def __setattr__(self, name, value): - if name.startswith('_'): - super().__setattr__(name, value) # Call original __setattr__ - else: - setattr(self._obj, name, value) - -if __name__ == '__main__': - class A: - def __init__(self, x): - self.x = x - def spam(self): - print('A.spam') - - a = A(42) - p = Proxy(a) - print(p.x) - print(p.spam()) - p.x = 37 - print('Should be 37:', p.x) - print('Should be 37:', a.x) - - diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example4.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example4.py deleted file mode 100644 index cb47a16..0000000 --- a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example4.py +++ /dev/null @@ -1,26 +0,0 @@ -# Tricky initialization problem involving multiple inheritance. -# Does NOT use super() - -class Base: - def __init__(self): - print('Base.__init__') - -class A(Base): - def __init__(self): - Base.__init__(self) - print('A.__init__') - -class B(Base): - def __init__(self): - Base.__init__(self) - print('B.__init__') - -class C(A,B): - def __init__(self): - A.__init__(self) - B.__init__(self) - print('C.__init__') - -if __name__ == '__main__': - # Please observe double call of Base.__init__ - c = C() diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example5.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example5.py deleted file mode 100644 index 15503c2..0000000 --- a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example5.py +++ /dev/null @@ -1,25 +0,0 @@ -# Tricky initialization problem involving multiple inheritance. -# Uses super() - -class Base: - def __init__(self): - print('Base.__init__') - -class A(Base): - def __init__(self): - super().__init__() - print('A.__init__') - -class B(Base): - def __init__(self): - super().__init__() - print('B.__init__') - -class C(A,B): - def __init__(self): - super().__init__() # Only one call to super() here - print('C.__init__') - -if __name__ == '__main__': - # Observe that each class initialized only once - c = C() diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_an_object_given_the_name_as_a_string/example.py b/chef/cookbooks/python/src/8/calling_a_method_on_an_object_given_the_name_as_a_string/example.py deleted file mode 100644 index 87ab944..0000000 --- a/chef/cookbooks/python/src/8/calling_a_method_on_an_object_given_the_name_as_a_string/example.py +++ /dev/null @@ -1,40 +0,0 @@ -# Example of calling methods by name - -import math -class Point: - def __init__(self, x, y): - self.x = x - self.y = y - - def __repr__(self): - return 'Point({!r:},{!r:})'.format(self.x, self.y) - - def distance(self, x, y): - return math.hypot(self.x - x, self.y - y) - -p = Point(2,3) - -# Method 1 : Use getattr -d = getattr(p, 'distance')(0, 0) # Calls p.distance(0, 0) -print(d) - -# Method 2: Use methodcaller -import operator -d = operator.methodcaller('distance', 0, 0)(p) -print(d) - -# Application in sorting -points = [ - Point(1, 2), - Point(3, 0), - Point(10, -3), - Point(-5, -7), - Point(-1, 8), - Point(3, 2) -] - -# Sort by distance from origin (0, 0) -points.sort(key=operator.methodcaller('distance', 0, 0)) -for p in points: - print(p) - diff --git a/chef/cookbooks/python/src/8/changing_the_string_representation_of_instances/example.py b/chef/cookbooks/python/src/8/changing_the_string_representation_of_instances/example.py deleted file mode 100644 index c08a049..0000000 --- a/chef/cookbooks/python/src/8/changing_the_string_representation_of_instances/example.py +++ /dev/null @@ -1,9 +0,0 @@ -class Pair: - def __init__(self, x, y): - self.x = x - self.y = y - def __repr__(self): - return 'Pair({0.x!r}, {0.y!r})'.format(self) - def __str__(self): - return '({0.x}, {0.y})'.format(self) - diff --git a/chef/cookbooks/python/src/8/creating_a_new_kind_of_class_or_instance_attribute/example1.py b/chef/cookbooks/python/src/8/creating_a_new_kind_of_class_or_instance_attribute/example1.py deleted file mode 100644 index b15a6e7..0000000 --- a/chef/cookbooks/python/src/8/creating_a_new_kind_of_class_or_instance_attribute/example1.py +++ /dev/null @@ -1,34 +0,0 @@ -# Descriptor attribute for an integer type-checked attribute -class Integer: - def __init__(self, name): - self.name = name - - def __get__(self, instance, cls): - if instance is None: - return self - else: - return instance.__dict__[self.name] - - def __set__(self, instance, value): - if not isinstance(value, int): - raise TypeError('Expected an int') - instance.__dict__[self.name] = value - - def __delete__(self, instance): - del instance.__dict__[self.name] - -class Point: - x = Integer('x') - y = Integer('y') - def __init__(self, x, y): - self.x = x - self.y = y - -if __name__ == '__main__': - p = Point(2, 3) - print(p.x) - p.y = 5 - try: - p.x = 2.3 - except TypeError as e: - print(e) diff --git a/chef/cookbooks/python/src/8/creating_an_instance_without_invoking_init/example.py b/chef/cookbooks/python/src/8/creating_an_instance_without_invoking_init/example.py deleted file mode 100644 index b02204a..0000000 --- a/chef/cookbooks/python/src/8/creating_an_instance_without_invoking_init/example.py +++ /dev/null @@ -1,35 +0,0 @@ -from time import localtime - -class Date: - def __init__(self, year, month, day): - self.year = year - self.month = month - self.day = day - - # Class method that bypasses __init__ - @classmethod - def today(cls): - d = cls.__new__(cls) - t = localtime() - d.year = t.tm_year - d.month = t.tm_mon - d.day = t.tm_mday - return d - -d = Date.__new__(Date) -print(d) -print(hasattr(d,'year')) - -data = { - 'year' : 2012, - 'month' : 8, - 'day' : 29 -} - -d.__dict__.update(data) -print(d.year) -print(d.month) - -d = Date.today() -print(d.year, d.month, d.day) - diff --git a/chef/cookbooks/python/test/cookbooks/python_test/README.md b/chef/cookbooks/python/test/cookbooks/python_test/README.md deleted file mode 100644 index cc621f0..0000000 --- a/chef/cookbooks/python/test/cookbooks/python_test/README.md +++ /dev/null @@ -1,15 +0,0 @@ -python_test Cookbook -==================== - -This cookbook tests the pip and virtualenv providers - -Requirements ------------- - -#### packages -- `python` - Version *2.5* or higher - -License and Authors -------------------- -Authors: Scott Likens - Sean Porter diff --git a/chef/cookbooks/python/test/cookbooks/python_test/files/default/tests/minitest/cook-3084_test.rb b/chef/cookbooks/python/test/cookbooks/python_test/files/default/tests/minitest/cook-3084_test.rb deleted file mode 100644 index 5642e62..0000000 --- a/chef/cookbooks/python/test/cookbooks/python_test/files/default/tests/minitest/cook-3084_test.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'minitest/spec' - -describe_recipe 'python_test::cook-3084' do - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - it "created a virtualenv in cook-3084" do - result = assert_sh("cook-3084/bin/python -c 'import sys; from os.path import basename; print basename(sys.prefix)'") - assert_match /cook-3084\n/, result - end - - it "created a virtualenv in cook-3084-interpreter" do - result = assert_sh("cook-3084-interpreter/bin/python -c 'import sys; from os.path import basename; print basename(sys.prefix)'") - assert_match /cook-3084-interpreter\n/, result - end -end diff --git a/chef/cookbooks/python/test/cookbooks/python_test/metadata.rb b/chef/cookbooks/python/test/cookbooks/python_test/metadata.rb deleted file mode 100644 index e5055d8..0000000 --- a/chef/cookbooks/python/test/cookbooks/python_test/metadata.rb +++ /dev/null @@ -1,7 +0,0 @@ -name 'python_test' -maintainer 'Scott Likens' -maintainer_email 'scott@mopub.com' -license 'Apache 2.0' -description 'Installs/Configures python_test' -long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version '0.1.0' diff --git a/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_exert.rb b/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_exert.rb deleted file mode 100644 index b0ad314..0000000 --- a/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_exert.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Author:: Scott M. Likens -# Cookbook Name:: python -# Recipe:: test_exert -# -# Copyright 2013, MoPub, Inc. -# -# 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. -# - -python_virtualenv "#{Chef::Config[:file_cache_path]}/virtualenv" do - interpreter "python" - owner "root" - group "root" - action :create -end - -python_pip "boto" do - action :install - virtualenv "#{Chef::Config[:file_cache_path]}/virtualenv" -end - -python_pip "psutil" do - action :install -end diff --git a/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_virtualenv.rb b/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_virtualenv.rb deleted file mode 100644 index 66e1a64..0000000 --- a/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_virtualenv.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Author:: Sean Porter -# Cookbook Name:: python -# Recipe:: test_virtualenv -# -# Copyright 2013, Heavy Water Operations, LLC. -# -# 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. -# - -python_virtualenv "/tmp/virtualenv" do - owner "root" - group "root" - action :create -end - -python_virtualenv "isolated python environment" do - path "/tmp/tobedestroyed" - action :create -end - -python_virtualenv "deleting the isolated python environment" do - path "/tmp/tobedestroyed" - action :delete -end diff --git a/chef/cookbooks/python/test/integration/exert/bats/exert.bats b/chef/cookbooks/python/test/integration/exert/bats/exert.bats deleted file mode 100644 index 714e3e1..0000000 --- a/chef/cookbooks/python/test/integration/exert/bats/exert.bats +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bats - -@test "virtualenv test environment should exist" { - [ -f "/tmp/kitchen-chef-solo/cache/virtualenv/bin/activate" ] -} - -@test "virtualenv test environment should be owned by root" { - ls -l /tmp/kitchen-chef-solo/cache/virtualenv | grep "root root" -} - -@test "virtualenv test environment should have boto working" { - /tmp/kitchen-chef-solo/cache/virtualenv/bin/python -c 'import boto; boto.Version' -} diff --git a/chef/cookbooks/python/test/integration/source/bats/source.bats b/chef/cookbooks/python/test/integration/source/bats/source.bats deleted file mode 100644 index ae1fe98..0000000 --- a/chef/cookbooks/python/test/integration/source/bats/source.bats +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bats - -@test "python bin should exist" { - [ -x "/usr/local/bin/python" ] -} - -@test "python should be version 2.7.5" { - /usr/local/bin/python -c 'import sys; print sys.version' | grep '2.7.5' -} diff --git a/chef/cookbooks/python/test/integration/virtualenv/bats/virtualenv.bats b/chef/cookbooks/python/test/integration/virtualenv/bats/virtualenv.bats deleted file mode 100644 index f5910af..0000000 --- a/chef/cookbooks/python/test/integration/virtualenv/bats/virtualenv.bats +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bats - -@test "virtualenv test environment should exist" { - [ -f "/tmp/virtualenv/bin/activate" ] -} - -@test "virtualenv test environment should be owned by root" { - ls -l /tmp/virtualenv | grep "root root" -} - -@test "virtualenv test environment should have a working python" { - /tmp/virtualenv/bin/python -c 'import sys; print sys.version' -} - -@test "virtualenv resource should be able to delete an environment" { - [ ! -d "/tmp/tobedestroyed" ] -} diff --git a/chef/cookbooks/rabbitmq/.kitchen.yml b/chef/cookbooks/rabbitmq/.kitchen.yml deleted file mode 100644 index 464b6c8..0000000 --- a/chef/cookbooks/rabbitmq/.kitchen.yml +++ /dev/null @@ -1,192 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-13.04 - driver_config: - box: opscode-ubuntu-13.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-13.04_provisionerless.box - run_list: - - recipe[apt] - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - run_list: - - recipe[yum::epel] - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - run_list: - - recipe[yum::epel] - -- name: fedora-18 - driver_config: - box: opscode-fedora-18 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode-fedora-18_provisionerless.box - run_list: {} - -# add once the bento image is fixed -# - name: debian-7.1 -# driver_config: -# box: opscode-debian-7.1 -# box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-7.1.0_provisionerless.box -# run_list: -# - recipe[apt] - -# image not downloading -# - name: smartos-13.1 -# driver_config: -# box: smartos-base1310 -# box_url: http://dlc-int.openindiana.org/aszeszo/vagrant/smartos-base1310-64-virtualbox-20130806.box -# run_list: -# - recipe[rabbitmq] - -# http://tickets.opscode.com/browse/COOK-2467 -# - name: omnios-r151002 -# driver_config: -# box: omnios-r151002 -# box_url: http://omnios.omniti.com/media/omnios-latest.box -# run_list: -# - recipe[rabbitmq] - -suites: -- name: default - excludes: - - omnios-r151002 - - smartos-13.1 - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::default] - attributes: {} - -- name: default-esl - excludes: - - centos-5.9 - - fedora-18 - - omnios-r151002 - - smartos-13.1 - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::default] - attributes: - erlang: - install_method: 'esl' - -- name: upstart - excludes: - - centos-5.9 - - centos-6.4 - - fedora-18 - - ubuntu-13.04 - - omnios-r151002 - - smartos-13.1 - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::default] - attributes: - rabbitmq: - job_control: 'upstart' - -- name: distro-version - excludes: - - centos-5.9 - - fedora-18 - - ubuntu-10.04 - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::default] - attributes: - rabbitmq: - use_distro_version: true - -- name: mgmt_console - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::mgmt_console] - attributes: {} - -- name: cook-2151-3489 - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::cook-2151-3489] - attributes: - rabbitmq: - disk_free_limit_relative: 1.0 - vm_memory_high_watermark: 0.5 - max_file_descriptors: 2048 - open_file_limit: 102400 - -- name: cook-2705 - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::default] - attributes: - rabbitmq: - tcp_listen_keepalive: true - -# stress test for lwrps -- name: lwrps-default - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::lwrps] - attributes: - rabbitmq: - enabled_plugins: ["rabbitmq_stomp", "rabbitmq_shovel", "rabbitmq_stomp"] - disabled_plugins: ["nonexistant_plugin", "rabbitmq_shovel"] - enabled_users: [ - {name: "kitchen1", password: "test", tag: "tag1", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, - {name: "kitchen2", password: "test", tag: "tag2", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}, {vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, - {name: "kitchen3", password: "test", tag: "tag3", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, - {name: "kitchen1", password: "test", tag: "tag4", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]} - ] - disabled_users: ["nonexistant_user", "kitchen2"] - disabled_policies: ["nonexistant_policy"] - virtualhosts: ["kitchen", "kitchen"] - disabled_virtualhosts: ["nonexistant_vhost"] - -# stress test for lwrps with distro packages -- name: lwrps-distro - excludes: - - centos-5.9 - - centos-6.4 - - fedora-18 - - ubuntu-10.04 - - ubuntu-12.04 - run_list: - - recipe[minitest-handler] - - recipe[rabbitmq_test::lwrps] - attributes: - rabbitmq: - use_distro_version: true - enabled_plugins: ["rabbitmq_stomp", "rabbitmq_shovel", "rabbitmq_stomp"] - disabled_plugins: ["nonexistant_plugin", "rabbitmq_shovel"] - enabled_users: [ - {name: "kitchen1", password: "test", tag: "tag1", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, - {name: "kitchen2", password: "test", tag: "tag2", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}, {vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, - {name: "kitchen3", password: "test", tag: "tag3", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, - {name: "kitchen1", password: "test", tag: "tag4", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]} - ] - disabled_users: ["nonexistant_user", "kitchen2"] - disabled_policies: ["nonexistant_policy"] - virtualhosts: ["kitchen", "kitchen"] - disabled_virtualhosts: ["nonexistant_vhost"] diff --git a/chef/cookbooks/rabbitmq/Berksfile b/chef/cookbooks/rabbitmq/Berksfile deleted file mode 100644 index f3ef5a6..0000000 --- a/chef/cookbooks/rabbitmq/Berksfile +++ /dev/null @@ -1,10 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook "minitest-handler" - cookbook "apt" - cookbook "yum" - cookbook "rabbitmq_test", :path => "./test/cookbooks/rabbitmq_test" -end diff --git a/chef/cookbooks/rabbitmq/CHANGELOG.md b/chef/cookbooks/rabbitmq/CHANGELOG.md index 128c5fa..cca726f 100644 --- a/chef/cookbooks/rabbitmq/CHANGELOG.md +++ b/chef/cookbooks/rabbitmq/CHANGELOG.md @@ -3,6 +3,45 @@ rabbitmq Cookbook CHANGELOG This file is used to list changes made in each version of the rabbitmq cookbook. +v3.0.4 (2014-03-19) +------------------- +- [COOK-4431] - RPM / DEB package installs now use the rabbit version you specify +- [COOK-4438] - rabbitmq_policy resource breaks if you use rabbitmq version >= 3.2 + + +v3.0.2 (2014-02-27) +------------------- +- [COOK-4384] Add ChefSpec Custom Matchers for LWRPs + + +v3.0.0 (2014-02-27) +------------------- +[COOK-4369] - use_inline_resources + + +v2.4.2 (2014-02-27) +------------------- +[COOK-4280] Upstart script properly waits until the server is started + + +v2.4.0 (2014-02-14) +------------------- +- [COOK-4050] - Do not force failure in rabbitmq_user +- [COOK-4088] - Update rabbitmq.config for port ranges +- Updating test harness. Fixing style cops + + +v2.3.2 +------ +### Bug +- **[COOK-3678](https://tickets.opscode.com/browse/COOK-3678)** - Fix an issue where a RabbitMQ policy resource with vhost arguments emits unexpected restart notification +- **[COOK-3606](https://tickets.opscode.com/browse/COOK-3606)** - Fix erlang cookie comparison +- **[COOK-3512](https://tickets.opscode.com/browse/COOK-3512)** - Define rabbitmq service on SUSE + +### New Feature +- **[COOK-3538](https://tickets.opscode.com/browse/COOK-3538)** - Configure web management console to use SSL + + v2.3.0 ------ ### Improvement diff --git a/chef/cookbooks/rabbitmq/CONTRIBUTING b/chef/cookbooks/rabbitmq/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/rabbitmq/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/rabbitmq/LICENSE b/chef/cookbooks/rabbitmq/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/rabbitmq/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/rabbitmq/README.md b/chef/cookbooks/rabbitmq/README.md index f2571f5..15a61ce 100644 --- a/chef/cookbooks/rabbitmq/README.md +++ b/chef/cookbooks/rabbitmq/README.md @@ -31,6 +31,7 @@ To enable SSL turn `ssl` to `true` and set the paths to your cacert, cert and ke ### mgmt_console Installs the `rabbitmq_management` and `rabbitmq_management_visualiser` plugins. +To use https connection to management console, turn `['rabbitmq']['web_console_ssl']` to true. The SSL port for web management console can be configured by setting attribute `['rabbitmq']['web_console_ssl_port']`, whose default value is 15671. ### plugin_management Enables any plugins listed in the `node['rabbitmq']['enabled_plugins']` and disables any listed in `node['rabbitmq'][disabled_plugins']` attributes. diff --git a/chef/cookbooks/rabbitmq/TESTING.md b/chef/cookbooks/rabbitmq/TESTING.md deleted file mode 100644 index 8380623..0000000 --- a/chef/cookbooks/rabbitmq/TESTING.md +++ /dev/null @@ -1,39 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test - -This cookbook has the following Test-Kitchen coverage: - -| Test Coverage | Ubuntu 10.04 | Ubuntu 12.04 | Ubuntu 13.04 | Centos 5.9 | Centos 6.4 | Fedora 18 | Debian 7.1 | SmartOS 13.1 | OmniOS r151002 | -| -------------- |:-------------:|:------------:|:------------:|:----------:|:----------:|:---------:|:----------:|:------------:|:--------------:| -| default | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | -| default-esl | **Y** | **Y** | **Y** | **N** | **Y** | **N** | **N** | **N** | **N** | -| upstart | **Y** | **Y** | **Y** | **N** | **N** | **N** | **N** | **N** | **N** | -| distro-version | **N** | **Y** | **Y** | **N** | **Y** | **N** | **N** | **N** | **N** | -| mgmt_console | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | -| cook-2151-3489 | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | -| cook-2705 | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | -| lwrps-default | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | -| lwrps-distro | **N** | **N** | **Y** | **N** | **N** | **N** | **N** | **N** | **N** | diff --git a/chef/cookbooks/rabbitmq/attributes/default.rb b/chef/cookbooks/rabbitmq/attributes/default.rb index aac6eb3..a31247e 100644 --- a/chef/cookbooks/rabbitmq/attributes/default.rb +++ b/chef/cookbooks/rabbitmq/attributes/default.rb @@ -1,7 +1,7 @@ # Latest RabbitMQ.com version to install default['rabbitmq']['version'] = '3.1.5' # The distro versions may be more stable and have back-ported patches -default['rabbitmq']['use_distro_version'] = false +default['rabbitmq']['use_distro_version'] = true # being nil, the rabbitmq defaults will be used default['rabbitmq']['nodename'] = nil @@ -9,27 +9,29 @@ default['rabbitmq']['address'] = nil default['rabbitmq']['port'] = nil default['rabbitmq']['config'] = nil default['rabbitmq']['logdir'] = nil -default['rabbitmq']['mnesiadir'] = "/var/lib/rabbitmq/mnesia" +default['rabbitmq']['mnesiadir'] = '/var/lib/rabbitmq/mnesia' default['rabbitmq']['service_name'] = 'rabbitmq-server' # config file location # http://www.rabbitmq.com/configure.html#define-environment-variables # "The .config extension is automatically appended by the Erlang runtime." -default['rabbitmq']['config_root'] = "/etc/rabbitmq" -default['rabbitmq']['config'] = "/etc/rabbitmq/rabbitmq" +default['rabbitmq']['config_root'] = '/etc/rabbitmq' +default['rabbitmq']['config'] = '/etc/rabbitmq/rabbitmq' default['rabbitmq']['erlang_cookie_path'] = '/var/lib/rabbitmq/.erlang.cookie' # rabbitmq.config defaults default['rabbitmq']['default_user'] = 'guest' default['rabbitmq']['default_pass'] = 'guest' -# bind erlang networking to localhost -default['rabbitmq']['local_erl_networking'] = false +# Erlang kernel application options +# See http://www.erlang.org/doc/man/kernel_app.html +default['rabbitmq']['kernel']['inet_dist_listen_min'] = nil +default['rabbitmq']['kernel']['inet_dist_listen_max'] = nil -# bind rabbit and erlang networking to an address -default['rabbitmq']['erl_networking_bind_address'] = nil +# Tell Erlang what IP to bind to +default['rabbitmq']['kernel']['inet_dist_use_interface'] = nil -#clustering +# clustering default['rabbitmq']['cluster'] = false default['rabbitmq']['cluster_disk_nodes'] = [] default['rabbitmq']['erlang_cookie'] = 'AnyAlphaNumericStringWillDo' @@ -43,7 +45,7 @@ default['rabbitmq']['open_file_limit'] = nil # job control default['rabbitmq']['job_control'] = 'initd' -#ssl +# ssl default['rabbitmq']['ssl'] = false default['rabbitmq']['ssl_port'] = 5671 default['rabbitmq']['ssl_cacert'] = '/path/to/cacert.pem' @@ -51,8 +53,10 @@ default['rabbitmq']['ssl_cert'] = '/path/to/cert.pem' default['rabbitmq']['ssl_key'] = '/path/to/key.pem' default['rabbitmq']['ssl_verify'] = 'verify_none' default['rabbitmq']['ssl_fail_if_no_peer_cert'] = false +default['rabbitmq']['web_console_ssl'] = false +default['rabbitmq']['web_console_ssl_port'] = 15_671 -#tcp listen options +# tcp listen options default['rabbitmq']['tcp_listen_packet'] = 'raw' default['rabbitmq']['tcp_listen_reuseaddr'] = true default['rabbitmq']['tcp_listen_backlog'] = 128 @@ -60,27 +64,23 @@ default['rabbitmq']['tcp_listen_nodelay'] = true default['rabbitmq']['tcp_listen_exit_on_close'] = false default['rabbitmq']['tcp_listen_keepalive'] = false -#virtualhosts +# virtualhosts default['rabbitmq']['virtualhosts'] = [] default['rabbitmq']['disabled_virtualhosts'] = [] -#users +# users default['rabbitmq']['enabled_users'] = - [{ :name => "guest", :password => "guest", :rights => - [{:vhost => nil , :conf => ".*", :write => ".*", :read => ".*"}] + [{ :name => 'guest', :password => 'guest', :rights => + [{ :vhost => nil , :conf => '.*', :write => '.*', :read => '.*' }] }] -default['rabbitmq']['disabled_users'] =[] +default['rabbitmq']['disabled_users'] = [] -#plugins +# plugins default['rabbitmq']['enabled_plugins'] = [] default['rabbitmq']['disabled_plugins'] = [] -#platform specific settings +# platform specific settings case node['platform_family'] -when 'debian' - default['rabbitmq']['package'] = "https://www.rabbitmq.com/releases/rabbitmq-server/v#{node['rabbitmq']['version']}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb" -when 'rhel','fedora' - default['rabbitmq']['package'] = "https://www.rabbitmq.com/releases/rabbitmq-server/v#{node['rabbitmq']['version']}/rabbitmq-server-#{node['rabbitmq']['version']}-1.noarch.rpm" when 'smartos' default['rabbitmq']['service_name'] = 'rabbitmq' default['rabbitmq']['config_root'] = '/opt/local/etc/rabbitmq' @@ -89,12 +89,12 @@ when 'smartos' end # Example HA policies -default['rabbitmq']['policies']['ha-all']['pattern'] = "^(?!amq\\.).*" -default['rabbitmq']['policies']['ha-all']['params'] = { "ha-mode" => "all" } +default['rabbitmq']['policies']['ha-all']['pattern'] = '^(?!amq\\.).*' +default['rabbitmq']['policies']['ha-all']['params'] = { 'ha-mode' => 'all' } default['rabbitmq']['policies']['ha-all']['priority'] = 0 default['rabbitmq']['policies']['ha-two']['pattern'] = "^two\." -default['rabbitmq']['policies']['ha-two']['params'] = { "ha-mode" => "exactly", "ha-params" => 2 } +default['rabbitmq']['policies']['ha-two']['params'] = { 'ha-mode' => 'exactly', 'ha-params' => 2 } default['rabbitmq']['policies']['ha-two']['priority'] = 1 default['rabbitmq']['disabled_policies'] = [] diff --git a/chef/cookbooks/rabbitmq/libraries/default.rb b/chef/cookbooks/rabbitmq/libraries/default.rb new file mode 100644 index 0000000..bcdbc49 --- /dev/null +++ b/chef/cookbooks/rabbitmq/libraries/default.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: rabbitmq +# Library:: default +# Author:: Jake Davis () +# +# 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. +# + +module Opscode + # module rabbit + module RabbitMQ + # This method does some of the yuckiness of formatting parameters properly + # for rendering into the rabbit.config template. + def format_kernel_parameters + rendered = [] + kernel = node['rabbitmq']['kernel'].dup + + # This parameter is special and needs commas instead of periods. + rendered << "{inet_dist_use_interface, {#{kernel[:inet_dist_use_interface].gsub(/\./, ',')}}}" if kernel[:inet_dist_use_interface] + kernel.delete(:inet_dist_use_interface) + + # Otherwise, we can just render it nicely as Erlang wants. This + # theoretically opens the door for arbitrary kernel_app parameters to be + # declared. + kernel.select { |k, v| !v.nil? }.each_pair do |param, val| + rendered << "{#{param}, #{val}}" + end + + rendered.each { |r| r.prepend(' ') }.join(",\n") + end + end +end diff --git a/chef/cookbooks/rabbitmq/libraries/matchers.rb b/chef/cookbooks/rabbitmq/libraries/matchers.rb new file mode 100644 index 0000000..26a60bc --- /dev/null +++ b/chef/cookbooks/rabbitmq/libraries/matchers.rb @@ -0,0 +1,57 @@ +if defined?(ChefSpec) + def add_rabbitmq_user(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_user, :add, resource_name) + end + + def delete_rabbitmq_user(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_user, :delete, resource_name) + end + + def set_permissions_rabbitmq_user(resource_name) # rubocop:disable AccessorMethodName + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_user, :set_permissions, resource_name) + end + + def clear_permissions_rabbitmq_user(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_user, :clear_permissions, resource_name) + end + + def set_tags_rabbitmq_user(resource_name) # rubocop:disable AccessorMethodName + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_user, :set_tags, resource_name) + end + + def clear_tags_rabbitmq_user(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_user, :clear_tags, resource_name) + end + + def change_password_rabbitmq_user(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_user, :change_password, resource_name) + end + + def add_rabbitmq_vhost(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_vhost, :add, resource_name) + end + + def delete_rabbitmq_vhost(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_vhost, :delete, resource_name) + end + + def set_rabbitmq_policy(resource_name) # rubocop:disable AccessorMethodName + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_policy, :set, resource_name) + end + + def clear_rabbitmq_policy(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_policy, :clear, resource_name) + end + + def list_rabbitmq_policy(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_policy, :list, resource_name) + end + + def enable_rabbitmq_plugin(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_plugin, :enable, resource_name) + end + + def disable_rabbitmq_plugin(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:rabbitmq_plugin, :disable, resource_name) + end +end diff --git a/chef/cookbooks/rabbitmq/metadata.json b/chef/cookbooks/rabbitmq/metadata.json new file mode 100644 index 0000000..9702e40 --- /dev/null +++ b/chef/cookbooks/rabbitmq/metadata.json @@ -0,0 +1,155 @@ +{ + "name": "rabbitmq", + "version": "3.0.4", + "description": "Installs and configures RabbitMQ server", + "long_description": "", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "debian": ">= 0.0.0", + "ubuntu": ">= 0.0.0", + "redhat": ">= 0.0.0", + "centos": ">= 0.0.0", + "scientific": ">= 0.0.0", + "amazon": ">= 0.0.0", + "oracle": ">= 0.0.0", + "smartos": ">= 0.0.0", + "suse": ">= 0.0.0" + }, + "dependencies": { + "erlang": ">= 0.9.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + "rabbitmq": { + "display_name": "RabbitMQ", + "description": "Hash of RabbitMQ attributes", + "type": "hash" + }, + "rabbitmq/nodename": { + "display_name": "RabbitMQ Erlang node name", + "description": "The Erlang node name for this server.", + "default": "node['hostname']" + }, + "rabbitmq/address": { + "display_name": "RabbitMQ server IP address", + "description": "IP address to bind." + }, + "rabbitmq/port": { + "display_name": "RabbitMQ server port", + "description": "TCP port to bind." + }, + "rabbitmq/config": { + "display_name": "RabbitMQ config file to load", + "description": "Path to the rabbitmq.config file, if any." + }, + "rabbitmq/logdir": { + "display_name": "RabbitMQ log directory", + "description": "Path to the directory for log files." + }, + "rabbitmq/mnesiadir": { + "display_name": "RabbitMQ Mnesia database directory", + "description": "Path to the directory for Mnesia database files." + }, + "rabbitmq/cluster": { + "display_name": "RabbitMQ clustering", + "description": "Whether to activate clustering.", + "default": "no" + }, + "rabbitmq/cluster_config": { + "display_name": "RabbitMQ clustering configuration file", + "description": "Path to the clustering configuration file, if cluster is yes.", + "default": "/etc/rabbitmq/rabbitmq_cluster.config" + }, + "rabbitmq/cluster_disk_nodes": { + "display_name": "RabbitMQ cluster disk nodes", + "description": "Array of member Erlang nodenames for the disk-based storage nodes in the cluster.", + "default": [ + + ], + "type": "array" + }, + "rabbitmq/erlang_cookie": { + "display_name": "RabbitMQ Erlang cookie", + "description": "Access cookie for clustering nodes. There is no default." + }, + "rabbitmq/virtualhosts": { + "display_name": "Virtualhosts on rabbitmq instance", + "description": "List all virtualhosts that will exist", + "default": [ + + ], + "type": "array" + }, + "rabbitmq/enabled_users": { + "display_name": "Users and their rights on rabbitmq instance", + "description": "Users and description of their rights", + "default": [ + { + "name": "guest", + "password": "guest", + "rights": [ + { + "vhost": null, + "conf": ".*", + "write": ".*", + "read": ".*" + } + ] + } + ], + "type": "array" + }, + "rabbitmq/disabled_users": { + "display_name": "Disabled users", + "description": "List all users that will be deactivated", + "default": [ + + ], + "type": "array" + }, + "rabbitmq/enabled_plugins": { + "display_name": "Enabled plugins", + "description": "List all plugins that will be activated", + "default": [ + + ], + "type": "array" + }, + "rabbitmq/disabled_plugins": { + "display_name": "Disabled plugins", + "description": "List all plugins that will be deactivated", + "default": [ + + ], + "type": "array" + }, + "rabbitmq/local_erl_networking": { + "display_name": "Local Erlang networking", + "description": "Bind erlang networking to localhost" + }, + "rabbitmq/erl_networking_bind_address": { + "display_name": "Erl Networking Bind Address", + "description": "Bind Rabbit and erlang networking to an address" + } + }, + "groupings": { + }, + "recipes": { + "rabbitmq": "Install and configure RabbitMQ", + "rabbitmq::cluster": "Set up RabbitMQ clustering.", + "rabbitmq::plugin_management": "Manage plugins with node attributes", + "rabbitmq::virtualhost_management": "Manage virtualhost with node attributes", + "rabbitmq::user_management": "Manage users with node attributes" + } +} \ No newline at end of file diff --git a/chef/cookbooks/rabbitmq/metadata.rb b/chef/cookbooks/rabbitmq/metadata.rb index 4def5bf..f84b42b 100644 --- a/chef/cookbooks/rabbitmq/metadata.rb +++ b/chef/cookbooks/rabbitmq/metadata.rb @@ -1,104 +1,110 @@ -name "rabbitmq" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Installs and configures RabbitMQ server" -version "2.3.0" -recipe "rabbitmq", "Install and configure RabbitMQ" -recipe "rabbitmq::cluster", "Set up RabbitMQ clustering." -recipe "rabbitmq::plugin_management", "Manage plugins with node attributes" -recipe "rabbitmq::virtualhost_management", "Manage virtualhost with node attributes" -recipe "rabbitmq::user_management", "Manage users with node attributes" -depends "erlang", ">= 0.9" +name 'rabbitmq' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Installs and configures RabbitMQ server' +version '3.0.4' +recipe 'rabbitmq', 'Install and configure RabbitMQ' +recipe 'rabbitmq::cluster', 'Set up RabbitMQ clustering.' +recipe 'rabbitmq::plugin_management', 'Manage plugins with node attributes' +recipe 'rabbitmq::virtualhost_management', 'Manage virtualhost with node attributes' +recipe 'rabbitmq::user_management', 'Manage users with node attributes' +depends 'erlang', '>= 0.9' -%w{ubuntu debian linuxmint redhat centos scientific amazon fedora oracle smartos suse}.each do |os| - supports os -end +supports 'debian' +supports 'ubuntu' +supports 'redhat' +supports 'centos' +supports 'scientific' +supports 'amazon' +supports 'oracle' +supports 'smartos' +supports 'suse' -attribute "rabbitmq", - :display_name => "RabbitMQ", - :description => "Hash of RabbitMQ attributes", - :type => "hash" +attribute 'rabbitmq', + :display_name => 'RabbitMQ', + :description => 'Hash of RabbitMQ attributes', + :type => 'hash' -attribute "rabbitmq/nodename", - :display_name => "RabbitMQ Erlang node name", - :description => "The Erlang node name for this server.", +attribute 'rabbitmq/nodename', + :display_name => 'RabbitMQ Erlang node name', + :description => 'The Erlang node name for this server.', :default => "node['hostname']" -attribute "rabbitmq/address", - :display_name => "RabbitMQ server IP address", - :description => "IP address to bind." +attribute 'rabbitmq/address', + :display_name => 'RabbitMQ server IP address', + :description => 'IP address to bind.' -attribute "rabbitmq/port", - :display_name => "RabbitMQ server port", - :description => "TCP port to bind." +attribute 'rabbitmq/port', + :display_name => 'RabbitMQ server port', + :description => 'TCP port to bind.' -attribute "rabbitmq/config", - :display_name => "RabbitMQ config file to load", - :description => "Path to the rabbitmq.config file, if any." +attribute 'rabbitmq/config', + :display_name => 'RabbitMQ config file to load', + :description => 'Path to the rabbitmq.config file, if any.' -attribute "rabbitmq/logdir", - :display_name => "RabbitMQ log directory", - :description => "Path to the directory for log files." +attribute 'rabbitmq/logdir', + :display_name => 'RabbitMQ log directory', + :description => 'Path to the directory for log files.' -attribute "rabbitmq/mnesiadir", - :display_name => "RabbitMQ Mnesia database directory", - :description => "Path to the directory for Mnesia database files." +attribute 'rabbitmq/mnesiadir', + :display_name => 'RabbitMQ Mnesia database directory', + :description => 'Path to the directory for Mnesia database files.' -attribute "rabbitmq/cluster", - :display_name => "RabbitMQ clustering", - :description => "Whether to activate clustering.", - :default => "no" +attribute 'rabbitmq/cluster', + :display_name => 'RabbitMQ clustering', + :description => 'Whether to activate clustering.', + :default => 'no' -attribute "rabbitmq/cluster_config", - :display_name => "RabbitMQ clustering configuration file", - :description => "Path to the clustering configuration file, if cluster is yes.", - :default => "/etc/rabbitmq/rabbitmq_cluster.config" +attribute 'rabbitmq/cluster_config', + :display_name => 'RabbitMQ clustering configuration file', + :description => 'Path to the clustering configuration file, if cluster is yes.', + :default => '/etc/rabbitmq/rabbitmq_cluster.config' -attribute "rabbitmq/cluster_disk_nodes", - :display_name => "RabbitMQ cluster disk nodes", - :description => "Array of member Erlang nodenames for the disk-based storage nodes in the cluster.", +attribute 'rabbitmq/cluster_disk_nodes', + :display_name => 'RabbitMQ cluster disk nodes', + :description => 'Array of member Erlang nodenames for the disk-based storage nodes in the cluster.', :default => [], - :type => "array" + :type => 'array' -attribute "rabbitmq/erlang_cookie", - :display_name => "RabbitMQ Erlang cookie", - :description => "Access cookie for clustering nodes. There is no default." +attribute 'rabbitmq/erlang_cookie', + :display_name => 'RabbitMQ Erlang cookie', + :description => 'Access cookie for clustering nodes. There is no default.' -attribute "rabbitmq/virtualhosts", - :display_name => "Virtualhosts on rabbitmq instance", - :description => "List all virtualhosts that will exist", +attribute 'rabbitmq/virtualhosts', + :display_name => 'Virtualhosts on rabbitmq instance', + :description => 'List all virtualhosts that will exist', :default => [], - :type => "array" + :type => 'array' -attribute "rabbitmq/enabled_users", - :display_name => "Users and their rights on rabbitmq instance", - :description => "Users and description of their rights", - :default => [{ :name => "guest", :password => "guest", :rights => [{:vhost => nil , :conf => ".*", :write => ".*", :read => ".*"}]}], - :type => "array" +attribute 'rabbitmq/enabled_users', + :display_name => 'Users and their rights on rabbitmq instance', + :description => 'Users and description of their rights', + :default => [{ :name => 'guest', :password => 'guest', :rights => [{ :vhost => nil , :conf => '.*', :write => '.*', :read => '.*' }] }], + :type => 'array' -attribute "rabbitmq/disabled_users", - :display_name => "Disabled users", - :description => "List all users that will be deactivated", +attribute 'rabbitmq/disabled_users', + :display_name => 'Disabled users', + :description => 'List all users that will be deactivated', :default => [], - :type => "array" + :type => 'array' -attribute "rabbitmq/enabled_plugins", - :display_name => "Enabled plugins", - :description => "List all plugins that will be activated", +attribute 'rabbitmq/enabled_plugins', + :display_name => 'Enabled plugins', + :description => 'List all plugins that will be activated', :default => [], - :type => "array" + :type => 'array' -attribute "rabbitmq/disabled_plugins", - :display_name => "Disabled plugins", - :description => "List all plugins that will be deactivated", +attribute 'rabbitmq/disabled_plugins', + :display_name => 'Disabled plugins', + :description => 'List all plugins that will be deactivated', :default => [], - :type => "array" + :type => 'array' -attribute "rabbitmq/local_erl_networking", - :display_name => "Local Erlang networking", - :description => "Bind erlang networking to localhost" +attribute 'rabbitmq/local_erl_networking', + :display_name => 'Local Erlang networking', + :description => 'Bind erlang networking to localhost' -attribute "rabbitmq/erl_networking_bind_address", - :display_name => "Erl Networking Bind Address", - :description => "Bind Rabbit and erlang networking to an address" +attribute 'rabbitmq/erl_networking_bind_address', + :display_name => 'Erl Networking Bind Address', + :description => 'Bind Rabbit and erlang networking to an address' diff --git a/chef/cookbooks/rabbitmq/providers/plugin.rb b/chef/cookbooks/rabbitmq/providers/plugin.rb index fbb8990..5809caf 100644 --- a/chef/cookbooks/rabbitmq/providers/plugin.rb +++ b/chef/cookbooks/rabbitmq/providers/plugin.rb @@ -17,18 +17,18 @@ # limitations under the License. # -def plugins_bin_path(return_array=false) +def plugins_bin_path(return_array = false) path = ENV.fetch('PATH') + ':/usr/lib/rabbitmq/bin' return_array ? path.split(':') : path end def plugin_enabled?(name) - cmdStr = "rabbitmq-plugins list -e '#{name}\\b'" - cmd = Mixlib::ShellOut.new(cmdStr) + cmdstr = "rabbitmq-plugins list -e '#{name}\\b'" + cmd = Mixlib::ShellOut.new(cmdstr) cmd.environment['HOME'] = ENV.fetch('HOME', '/root') cmd.environment['PATH'] = plugins_bin_path cmd.run_command - Chef::Log.debug "rabbitmq_plugin_enabled?: #{cmdStr}" + Chef::Log.debug "rabbitmq_plugin_enabled?: #{cmdstr}" Chef::Log.debug "rabbitmq_plugin_enabled?: #{cmd.stdout}" cmd.error! cmd.stdout =~ /\b#{name}\b/ diff --git a/chef/cookbooks/rabbitmq/providers/policy.rb b/chef/cookbooks/rabbitmq/providers/policy.rb index 1d957da..8fe91a5 100644 --- a/chef/cookbooks/rabbitmq/providers/policy.rb +++ b/chef/cookbooks/rabbitmq/providers/policy.rb @@ -18,8 +18,14 @@ # limitations under the License. # -def policy_exists?(name) - cmd = Mixlib::ShellOut.new("rabbitmqctl list_policies |grep '#{name}\\b'") +require 'shellwords' + +def policy_exists?(vhost, name) + cmd = 'rabbitmqctl list_policies' + cmd << " -p #{Shellwords.escape vhost}" unless vhost.nil? + cmd << " |grep '#{name}\\b'" + + cmd = Mixlib::ShellOut.new(cmd) cmd.environment['HOME'] = ENV.fetch('HOME', '/root') cmd.run_command begin @@ -31,8 +37,8 @@ def policy_exists?(name) end action :set do - unless policy_exists?(new_resource.policy) - cmd = "rabbitmqctl set_policy" + unless policy_exists?(new_resource.vhost, new_resource.policy) + cmd = 'rabbitmqctl set_policy' cmd << " -p #{new_resource.vhost}" unless new_resource.vhost.nil? cmd << " #{new_resource.policy}" cmd << " \"#{new_resource.pattern}\"" @@ -40,9 +46,8 @@ action :set do first_param = true new_resource.params.each do |key, value| - unless first_param - cmd << "," - end + cmd << ',' unless first_param + if value.kind_of? String cmd << "\"#{key}\":\"#{value}\"" else @@ -52,9 +57,10 @@ action :set do end cmd << "}'" - - if new_resource.priority - cmd << " #{new_resource.priority}" + if node['rabbitmq']['version'] >= '3.2.0' + cmd << " --priority #{new_resource.priority}" if new_resource.priority + else + cmd << " #{new_resource.priority}" if new_resource.priority end execute "set_policy #{new_resource.policy}" do @@ -67,7 +73,7 @@ action :set do end action :clear do - if policy_exists?(new_resource.policy) + if policy_exists?(new_resource.vhost, new_resource.policy) execute "clear_policy #{new_resource.policy}" do command "rabbitmqctl clear_policy #{new_resource.policy}" end @@ -78,8 +84,8 @@ action :clear do end action :list do - execute "list_policies" do - command "rabbitmqctl list_policies" + execute 'list_policies' do + command 'rabbitmqctl list_policies' end new_resource.updated_by_last_action(true) diff --git a/chef/cookbooks/rabbitmq/providers/user.rb b/chef/cookbooks/rabbitmq/providers/user.rb index 6e050a2..ff72e12 100644 --- a/chef/cookbooks/rabbitmq/providers/user.rb +++ b/chef/cookbooks/rabbitmq/providers/user.rb @@ -17,12 +17,14 @@ # limitations under the License. # +use_inline_resources + def user_exists?(name) - cmdStr = "rabbitmqctl -q list_users |grep '^#{name}\\b'" - cmd = Mixlib::ShellOut.new(cmdStr) + cmd = "rabbitmqctl -q list_users |grep '^#{name}\\b'" + cmd = Mixlib::ShellOut.new(cmd) cmd.environment['HOME'] = ENV.fetch('HOME', '/root') cmd.run_command - Chef::Log.debug "rabbitmq_user_exists?: #{cmdStr}" + Chef::Log.debug "rabbitmq_user_exists?: #{cmd}" Chef::Log.debug "rabbitmq_user_exists?: #{cmd.stdout}" begin cmd.error! @@ -34,16 +36,16 @@ end def user_has_tag?(name, tag) tag = '"\[\]"' if tag.nil? - cmdStr = "rabbitmqctl -q list_users | grep \"^#{name}\\b\" | grep #{tag}" - cmd = Mixlib::ShellOut.new(cmdStr) + cmd = "rabbitmqctl -q list_users | grep \"^#{name}\\b\" | grep #{tag}" + cmd = Mixlib::ShellOut.new(cmd) cmd.environment['HOME'] = ENV.fetch('HOME', '/root') cmd.run_command - Chef::Log.debug "rabbitmq_user_has_tag?: #{cmdStr}" + Chef::Log.debug "rabbitmq_user_has_tag?: #{cmd}" Chef::Log.debug "rabbitmq_user_has_tag?: #{cmd.stdout}" begin cmd.error! true - rescue Exception => e + rescue RuntimeError false end end @@ -52,122 +54,110 @@ end # empty perm_list means we're checking for any permissions def user_has_permissions?(name, vhost, perm_list = nil) vhost = '/' if vhost.nil? - cmdStr = "rabbitmqctl -q list_user_permissions #{name} | grep \"^#{vhost}\\b\"" - cmd = Mixlib::ShellOut.new(cmdStr) + cmd = "rabbitmqctl -q list_user_permissions #{name} | grep \"^#{vhost}\\b\"" + cmd = Mixlib::ShellOut.new(cmd) cmd.environment['HOME'] = ENV.fetch('HOME', '/root') cmd.run_command - Chef::Log.debug "rabbitmq_user_has_permissions?: #{cmdStr}" + Chef::Log.debug "rabbitmq_user_has_permissions?: #{cmd}" Chef::Log.debug "rabbitmq_user_has_permissions?: #{cmd.stdout}" Chef::Log.debug "rabbitmq_user_has_permissions?: #{cmd.exitstatus}" - if perm_list.nil? && cmd.stdout.empty? #looking for empty and found nothing - Chef::Log.debug "rabbitmq_user_has_permissions?: no permissions found" + if perm_list.nil? && cmd.stdout.empty? # looking for empty and found nothing + Chef::Log.debug 'rabbitmq_user_has_permissions?: no permissions found' return false end - if perm_list == cmd.stdout.split.drop(1) #existing match search - Chef::Log.debug "rabbitmq_user_has_permissions?: matching permissions already found" + if perm_list == cmd.stdout.split.drop(1) # existing match search + Chef::Log.debug 'rabbitmq_user_has_permissions?: matching permissions already found' return true end - Chef::Log.debug "rabbitmq_user_has_permissions?: permissions found but do not match" - return false + Chef::Log.debug 'rabbitmq_user_has_permissions?: permissions found but do not match' + false end action :add do unless user_exists?(new_resource.user) - if new_resource.password.nil? || new_resource.password.empty? - Chef::Application.fatal!("rabbitmq_user with action :add requires a non-nil/empty password.") - end + Chef::Application.fatal!('rabbitmq_user with action :add requires a non-nil/empty password.') if new_resource.password.nil? || new_resource.password.empty? + # To escape single quotes in a shell, you have to close the surrounding single quotes, add # in an escaped single quote, and then re-open the original single quotes. # Since this string is interpolated once by ruby, and then a second time by the shell, we need # to escape the escape character ('\') twice. This is why the following is such a mess # of leaning toothpicks: new_password = new_resource.password.gsub("'", "'\\\\''") - cmdStr = "rabbitmqctl add_user #{new_resource.user} '#{new_password}'" + cmd = "rabbitmqctl add_user #{new_resource.user} '#{new_password}'" execute "rabbitmqctl add_user #{new_resource.user}" do - command cmdStr + command cmd Chef::Log.info "Adding RabbitMQ user '#{new_resource.user}'." - new_resource.updated_by_last_action(true) end end end action :delete do if user_exists?(new_resource.user) - cmdStr = "rabbitmqctl delete_user #{new_resource.user}" - execute cmdStr do - Chef::Log.debug "rabbitmq_user_delete: #{cmdStr}" + cmd = "rabbitmqctl delete_user #{new_resource.user}" + execute cmd do + Chef::Log.debug "rabbitmq_user_delete: #{cmd}" Chef::Log.info "Deleting RabbitMQ user '#{new_resource.user}'." - new_resource.updated_by_last_action(true) end end end action :set_permissions do - if !user_exists?(new_resource.user) - Chef::Application.fatal!("rabbitmq_user action :set_permissions fails with non-existant '#{new_resource.user}' user.") - end + Chef::Application.fatal!("rabbitmq_user action :set_permissions fails with non-existant '#{new_resource.user}' user.") unless user_exists?(new_resource.user) + perm_list = new_resource.permissions.split unless user_has_permissions?(new_resource.user, new_resource.vhost, perm_list) - vhostOpt = "-p #{new_resource.vhost}" unless new_resource.vhost.nil? - cmdStr = "rabbitmqctl set_permissions #{vhostOpt} #{new_resource.user} \"#{perm_list.join("\" \"")}\"" - execute cmdStr do - Chef::Log.debug "rabbitmq_user_set_permissions: #{cmdStr}" + vhostopt = "-p #{new_resource.vhost}" unless new_resource.vhost.nil? + cmd = "rabbitmqctl set_permissions #{vhostopt} #{new_resource.user} \"#{perm_list.join("\" \"")}\"" + execute cmd do + Chef::Log.debug "rabbitmq_user_set_permissions: #{cmd}" Chef::Log.info "Setting RabbitMQ user permissions for '#{new_resource.user}' on vhost #{new_resource.vhost}." - new_resource.updated_by_last_action(true) end end end action :clear_permissions do - if !user_exists?(new_resource.user) - Chef::Application.fatal!("rabbitmq_user action :clear_permissions fails with non-existant '#{new_resource.user}' user.") - end + Chef::Application.fatal!("rabbitmq_user action :clear_permissions fails with non-existant '#{new_resource.user}' user.") unless user_exists?(new_resource.user) + if user_has_permissions?(new_resource.user, new_resource.vhost) - vhostOpt = "-p #{new_resource.vhost}" unless new_resource.vhost.nil? - cmdStr = "rabbitmqctl clear_permissions #{vhostOpt} #{new_resource.user}" - execute cmdStr do - Chef::Log.debug "rabbitmq_user_clear_permissions: #{cmdStr}" + vhostopt = "-p #{new_resource.vhost}" unless new_resource.vhost.nil? + cmd = "rabbitmqctl clear_permissions #{vhostopt} #{new_resource.user}" + execute cmd do + Chef::Log.debug "rabbitmq_user_clear_permissions: #{cmd}" Chef::Log.info "Clearing RabbitMQ user permissions for '#{new_resource.user}' from vhost #{new_resource.vhost}." - new_resource.updated_by_last_action(true) end end end action :set_tags do - if !user_exists?(new_resource.user) - Chef::Application.fatal!("rabbitmq_user action :set_tags fails with non-existant '#{new_resource.user}' user.") - end + Chef::Application.fatal!("rabbitmq_user action :set_tags fails with non-existant '#{new_resource.user}' user.") unless user_exists?(new_resource.user) + unless user_has_tag?(new_resource.user, new_resource.tag) - cmdStr = "rabbitmqctl set_user_tags #{new_resource.user} #{new_resource.tag}" - execute cmdStr do - Chef::Log.debug "rabbitmq_user_set_tags: #{cmdStr}" + cmd = "rabbitmqctl set_user_tags #{new_resource.user} #{new_resource.tag}" + execute cmd do + Chef::Log.debug "rabbitmq_user_set_tags: #{cmd}" Chef::Log.info "Setting RabbitMQ user '#{new_resource.user}' tags '#{new_resource.tag}'" - new_resource.updated_by_last_action(true) end end end action :clear_tags do - if !user_exists?(new_resource.user) - Chef::Application.fatal!("rabbitmq_user action :clear_tags fails with non-existant '#{new_resource.user}' user.") - end + Chef::Application.fatal!("rabbitmq_user action :clear_tags fails with non-existant '#{new_resource.user}' user.") unless user_exists?(new_resource.user) + unless user_has_tag?(new_resource.user, '"\[\]"') - cmdStr = "rabbitmqctl set_user_tags #{new_resource.user}" - execute cmdStr do - Chef::Log.debug "rabbitmq_user_clear_tags: #{cmdStr}" + cmd = "rabbitmqctl set_user_tags #{new_resource.user}" + execute cmd do + Chef::Log.debug "rabbitmq_user_clear_tags: #{cmd}" Chef::Log.info "Clearing RabbitMQ user '#{new_resource.user}' tags." - new_resource.updated_by_last_action(true) end end end action :change_password do if user_exists?(new_resource.user) - cmdStr = "rabbitmqctl change_password #{new_resource.user} #{new_resource.password}" - execute cmdStr do - Chef::Log.debug "rabbitmq_user_change_password: #{cmdStr}" + cmd = "rabbitmqctl change_password #{new_resource.user} #{new_resource.password}" + execute cmd do + Chef::Log.debug "rabbitmq_user_change_password: #{cmd}" Chef::Log.info "Editing RabbitMQ user '#{new_resource.user}'." - new_resource.updated_by_last_action(true) end end end diff --git a/chef/cookbooks/rabbitmq/providers/vhost.rb b/chef/cookbooks/rabbitmq/providers/vhost.rb index 3b819d8..5b8fd7c 100644 --- a/chef/cookbooks/rabbitmq/providers/vhost.rb +++ b/chef/cookbooks/rabbitmq/providers/vhost.rb @@ -18,11 +18,11 @@ # def vhost_exists?(name) - cmdStr = "rabbitmqctl -q list_vhosts | grep ^#{name}$" - cmd = Mixlib::ShellOut.new(cmdStr) + cmd = "rabbitmqctl -q list_vhosts | grep ^#{name}$" + cmd = Mixlib::ShellOut.new(cmd) cmd.environment['HOME'] = ENV.fetch('HOME', '/root') cmd.run_command - Chef::Log.debug "rabbitmq_vhost_exists?: #{cmdStr}" + Chef::Log.debug "rabbitmq_vhost_exists?: #{cmd}" Chef::Log.debug "rabbitmq_vhost_exists?: #{cmd.stdout}" begin cmd.error! @@ -34,9 +34,9 @@ end action :add do unless vhost_exists?(new_resource.vhost) - cmdStr = "rabbitmqctl add_vhost #{new_resource.vhost}" - execute cmdStr do - Chef::Log.debug "rabbitmq_vhost_add: #{cmdStr}" + cmd = "rabbitmqctl add_vhost #{new_resource.vhost}" + execute cmd do + Chef::Log.debug "rabbitmq_vhost_add: #{cmd}" Chef::Log.info "Adding RabbitMQ vhost '#{new_resource.vhost}'." new_resource.updated_by_last_action(true) end @@ -45,9 +45,9 @@ end action :delete do if vhost_exists?(new_resource.vhost) - cmdStr = "rabbitmqctl delete_vhost #{new_resource.vhost}" - execute cmdStr do - Chef::Log.debug "rabbitmq_vhost_delete: #{cmdStr}" + cmd = "rabbitmqctl delete_vhost #{new_resource.vhost}" + execute cmd do + Chef::Log.debug "rabbitmq_vhost_delete: #{cmd}" Chef::Log.info "Deleting RabbitMQ vhost '#{new_resource.vhost}'." new_resource.updated_by_last_action(true) end diff --git a/chef/cookbooks/rabbitmq/recipes/default.rb b/chef/cookbooks/rabbitmq/recipes/default.rb index 85f4f83..35841c3 100644 --- a/chef/cookbooks/rabbitmq/recipes/default.rb +++ b/chef/cookbooks/rabbitmq/recipes/default.rb @@ -19,6 +19,11 @@ # limitations under the License. # +# +class Chef::Resource + include Opscode::RabbitMQ +end + include_recipe 'erlang' ## Install the package @@ -28,10 +33,14 @@ when 'debian' package 'util-linux' if node['rabbitmq']['use_distro_version'] - package 'rabbitmq-server' + package 'rabbitmq-server' do + action :upgrade + end else + # we need to download the package + deb_package = "https://www.rabbitmq.com/releases/rabbitmq-server/v#{node['rabbitmq']['version']}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb" remote_file "#{Chef::Config[:file_cache_path]}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb" do - source node['rabbitmq']['package'] + source deb_package action :create_if_missing end dpkg_package "#{Chef::Config[:file_cache_path]}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb" @@ -63,8 +72,8 @@ when 'debian' service node['rabbitmq']['service_name'] do provider Chef::Provider::Service::Upstart - action [ :enable, :start ] - #restart_command "stop #{node['rabbitmq']['service_name']} && start #{node['rabbitmq']['service_name']}" + action [:enable, :start] + # restart_command "stop #{node['rabbitmq']['service_name']} && start #{node['rabbitmq']['service_name']}" end end @@ -80,27 +89,30 @@ when 'debian' restart_command 'setsid /etc/init.d/rabbitmq-server restart' status_command 'setsid /etc/init.d/rabbitmq-server status' supports :status => true, :restart => true - action [ :enable, :start ] + action [:enable, :start] end end when 'rhel', 'fedora' - #This is needed since Erlang Solutions' packages provide "esl-erlang"; this package just requires "esl-erlang" and provides "erlang". + # This is needed since Erlang Solutions' packages provide "esl-erlang"; this package just requires "esl-erlang" and provides "erlang". if node['erlang']['install_method'] == 'esl' remote_file "#{Chef::Config[:file_cache_path]}/esl-erlang-compat.rpm" do - source "https://github.com/jasonmcintosh/esl-erlang-compat/blob/master/rpmbuild/RPMS/noarch/esl-erlang-compat-R14B-1.el6.noarch.rpm?raw=true" + source 'https://github.com/jasonmcintosh/esl-erlang-compat/blob/master/rpmbuild/RPMS/noarch/esl-erlang-compat-R14B-1.el6.noarch.rpm?raw=true' end rpm_package "#{Chef::Config[:file_cache_path]}/esl-erlang-compat.rpm" end - if node['rabbitmq']['use_distro_version'] then + if node['rabbitmq']['use_distro_version'] package 'rabbitmq-server' else + # We need to download the rpm + rpm_package = "https://www.rabbitmq.com/releases/rabbitmq-server/v#{node['rabbitmq']['version']}/rabbitmq-server-#{node['rabbitmq']['version']}-1.noarch.rpm" + remote_file "#{Chef::Config[:file_cache_path]}/rabbitmq-server-#{node['rabbitmq']['version']}-1.noarch.rpm" do - source node['rabbitmq']['package'] + source rpm_package action :create_if_missing end - rpm_package "#{Chef::Config[:file_cache_path]}/rabbitmq-server-#{node['rabbitmq']['version']}-1.el6.noarch.rpm" + rpm_package "#{Chef::Config[:file_cache_path]}/rabbitmq-server-#{node['rabbitmq']['version']}-1.noarch.rpm" end service node['rabbitmq']['service_name'] do @@ -113,6 +125,10 @@ when 'suse' # vendor change. package 'rabbitmq-server-plugins' package 'rabbitmq-server' + + service node['rabbitmq']['service_name'] do + action [:enable, :start] + end when 'smartos' package 'rabbitmq' @@ -154,33 +170,32 @@ template "#{node['rabbitmq']['config_root']}/rabbitmq.config" do owner 'root' group 'root' mode 00644 + variables( + :kernel => format_kernel_parameters + ) notifies :restart, "service[#{node['rabbitmq']['service_name']}]" end if File.exists?(node['rabbitmq']['erlang_cookie_path']) - existing_erlang_key = File.read(node['rabbitmq']['erlang_cookie_path']) + existing_erlang_key = File.read(node['rabbitmq']['erlang_cookie_path']).strip else existing_erlang_key = '' end if node['rabbitmq']['cluster'] && (node['rabbitmq']['erlang_cookie'] != existing_erlang_key) - log "stopping service[#{node['rabbitmq']['service_name']}] to change erlang_cookie" do - level :info - notifies :stop, "service[#{node['rabbitmq']['service_name']}]", :immediately - end - template node['rabbitmq']['erlang_cookie_path'] do source 'doterlang.cookie.erb' owner 'rabbitmq' group 'rabbitmq' mode 00400 + notifies :stop, "service[#{node['rabbitmq']['service_name']}]", :immediately notifies :start, "service[#{node['rabbitmq']['service_name']}]", :immediately - notifies :run, "execute[reset-node]", :immediately + notifies :run, 'execute[reset-node]', :immediately end # Need to reset for clustering # - execute "reset-node" do - command "rabbitmqctl stop_app && rabbitmqctl reset && rabbitmqctl start_app" + execute 'reset-node' do + command 'rabbitmqctl stop_app && rabbitmqctl reset && rabbitmqctl start_app' action :nothing end end diff --git a/chef/cookbooks/rabbitmq/recipes/mgmt_console.rb b/chef/cookbooks/rabbitmq/recipes/mgmt_console.rb index 6c62ce3..78a1597 100644 --- a/chef/cookbooks/rabbitmq/recipes/mgmt_console.rb +++ b/chef/cookbooks/rabbitmq/recipes/mgmt_console.rb @@ -17,7 +17,7 @@ # limitations under the License. # -include_recipe "rabbitmq::default" +include_recipe 'rabbitmq::default' plugins = %w( rabbitmq_management rabbitmq_management_visualiser ) diff --git a/chef/cookbooks/rabbitmq/recipes/plugin_management.rb b/chef/cookbooks/rabbitmq/recipes/plugin_management.rb index 2ea3711..f6fd039 100644 --- a/chef/cookbooks/rabbitmq/recipes/plugin_management.rb +++ b/chef/cookbooks/rabbitmq/recipes/plugin_management.rb @@ -19,7 +19,7 @@ # limitations under the License. # -include_recipe "rabbitmq::default" +include_recipe 'rabbitmq::default' node['rabbitmq']['enabled_plugins'].each do |plugin| rabbitmq_plugin plugin do diff --git a/chef/cookbooks/rabbitmq/recipes/policy_management.rb b/chef/cookbooks/rabbitmq/recipes/policy_management.rb index 668ebb9..a9c15aa 100644 --- a/chef/cookbooks/rabbitmq/recipes/policy_management.rb +++ b/chef/cookbooks/rabbitmq/recipes/policy_management.rb @@ -19,7 +19,7 @@ # limitations under the License. # -include_recipe "rabbitmq::default" +include_recipe 'rabbitmq::default' node['rabbitmq']['policies'].each do |name, policy| rabbitmq_policy name do diff --git a/chef/cookbooks/rabbitmq/recipes/user_management.rb b/chef/cookbooks/rabbitmq/recipes/user_management.rb index 9e71b0a..27167c0 100644 --- a/chef/cookbooks/rabbitmq/recipes/user_management.rb +++ b/chef/cookbooks/rabbitmq/recipes/user_management.rb @@ -19,8 +19,8 @@ # limitations under the License. # -include_recipe "rabbitmq::default" -include_recipe "rabbitmq::virtualhost_management" +include_recipe 'rabbitmq::default' +include_recipe 'rabbitmq::virtualhost_management' node['rabbitmq']['enabled_users'].each do |user| rabbitmq_user user['name'] do diff --git a/chef/cookbooks/rabbitmq/recipes/virtualhost_management.rb b/chef/cookbooks/rabbitmq/recipes/virtualhost_management.rb index dc90154..c427e79 100644 --- a/chef/cookbooks/rabbitmq/recipes/virtualhost_management.rb +++ b/chef/cookbooks/rabbitmq/recipes/virtualhost_management.rb @@ -19,7 +19,7 @@ # limitations under the License. # -include_recipe "rabbitmq::default" +include_recipe 'rabbitmq::default' node['rabbitmq']['virtualhosts'].each do |virtualhost| rabbitmq_vhost virtualhost do @@ -34,4 +34,3 @@ node['rabbitmq']['disabled_virtualhosts'].each do |virtualhost| notifies :restart, "service[#{node['rabbitmq']['service_name']}]" end end - diff --git a/chef/cookbooks/rabbitmq/templates/default/rabbitmq.config.erb b/chef/cookbooks/rabbitmq/templates/default/rabbitmq.config.erb index e47d34e..fc9bb6c 100644 --- a/chef/cookbooks/rabbitmq/templates/default/rabbitmq.config.erb +++ b/chef/cookbooks/rabbitmq/templates/default/rabbitmq.config.erb @@ -3,10 +3,15 @@ %%% [ -<% if node['rabbitmq']['local_erl_networking'] %> - {kernel, [{inet_dist_use_interface,{127,0,0,1}}]}, -<% elsif node['rabbitmq']['erl_networking_bind_address'] -%> - {kernel, [{inet_dist_use_interface,{<%= node['rabbitmq']['erl_networking_bind_address'].gsub(/\./, ',') %>}}]}, + {kernel, [ +<%= @kernel %> + ]}, +<% if node['rabbitmq']['web_console_ssl'] -%> + {rabbitmq_management, [ + {listener, [{port, <%= node['rabbitmq']['web_console_ssl_port'] %>}, + {ssl, true} + ]} + ]}, <% end %> {rabbit, [ <% if node['rabbitmq']['cluster'] && node['rabbitmq']['cluster_disk_nodes'] -%> diff --git a/chef/cookbooks/rabbitmq/templates/default/rabbitmq.upstart.conf.erb b/chef/cookbooks/rabbitmq/templates/default/rabbitmq.upstart.conf.erb index b74ed72..9389ee6 100644 --- a/chef/cookbooks/rabbitmq/templates/default/rabbitmq.upstart.conf.erb +++ b/chef/cookbooks/rabbitmq/templates/default/rabbitmq.upstart.conf.erb @@ -6,6 +6,20 @@ respawn respawn limit 5 60 env HOME="" +env RABBITMQ_PID_FILE="/var/run/rabbitmq/pid" + +pre-start script + PID_DIR=`dirname ${RABBITMQ_PID_FILE}` + + if [ ! -d ${PID_DIR} ]; + then + mkdir -p ${PID_DIR} + chown -R rabbitmq:rabbitmq ${PID_DIR} + chmod 755 ${PID_DIR} + fi +end script + exec /usr/sbin/rabbitmq-server > /var/log/rabbitmq/startup_log \ 2> /var/log/rabbitmq/startup_err -post-start exec /usr/sbin/rabbitmqctl wait >/dev/null 2>&1 + +post-start exec /usr/sbin/rabbitmqctl wait $RABBITMQ_PID_FILE >/dev/null 2>&1 diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/README.md b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/README.md deleted file mode 100644 index d348825..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/README.md +++ /dev/null @@ -1 +0,0 @@ -This cookbook is used with test-kitchen to test the parent, rabbitmq cookbook. diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1684_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1684_test.rb deleted file mode 100644 index 9fecb2c..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1684_test.rb +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -describe "rabbitmq_test::cook-1684" do - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - it 'installs rabbitmq from deb file when apt isnt used' do - unless node['platform_family'] == 'debian' - skip "Only applicable on Debian family" - end - - file("#{Chef::Config[:file_cache_path]}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb").must_exist && - package("rabbitmq-server").must_be_installed - end - - it 'installs rabbitmq from yum when used' do - unless node['platform_family'] == 'rhel' || node['platform_family'] == 'fedora' - skip "Only applicable on RHEL/Fedora family" - end - - rpm_path = "#{Chef::Config[:file_cache_path]}/rabbitmq-server-#{node['rabbitmq']['version']}-1.noarch.rpm" - - file(rpm_path).wont_exist && package("rabbitmq-server").must_be_installed - end - -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1724_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1724_test.rb deleted file mode 100644 index de1087b..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1724_test.rb +++ /dev/null @@ -1,31 +0,0 @@ -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -describe "rabbitmq_test::cook-1724" do - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - it 'doesnt use the rabbitmq apt repository' do - unless node['platform_family'] == 'debian' - skip "Only applicable on Debian family" - end - - file("/etc/apt/sources.list.d/rabbitmq-source.list").wont_exist && - package("rabbitmq-server").must_be_installed - end - -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-2151-3489_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-2151-3489_test.rb deleted file mode 100644 index 1b64b14..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-2151-3489_test.rb +++ /dev/null @@ -1,37 +0,0 @@ -# -# Copyright 2012-2013, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe "rabbitmq_test::cook-2151" do - include Helpers::RabbitMQ - - it 'includes the disk_free_limit configuration setting' do - file("#{node['rabbitmq']['config_root']}/rabbitmq.config"). - must_match /\{disk_free_limit, \{mem_relative, #{node['rabbitmq']['disk_free_limit_relative']}/ - end - - it 'includes the vm_memory_high_watermark configuration setting' do - file("#{node['rabbitmq']['config_root']}/rabbitmq.config"). - must_match /\{vm_memory_high_watermark, #{node['rabbitmq']['vm_memory_high_watermark']}/ - end - - it 'includes the open_file_limit configuration setting' do - file("#{node['rabbitmq']['config_root']}/rabbitmq-env.conf"). - must_match /(ulimit -n #{node['rabbitmq']['open_file_limit']})/ - end - -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/default_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/default_test.rb deleted file mode 100644 index 3694864..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/default_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright 2012-2013, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - - -describe "rabbitmq_test::default" do - include Helpers::RabbitMQ - - #packages - it 'installs the rabbitmq-server package' do - if node['rabbitmq']['use_distro_version'] - package('rabbitmq-server').must_be_installed - else - package('rabbitmq-server').must_be_installed.with(:version, '3.1.5-1') - end - end - - #directories - it 'creates the mnesia directory' do - directory(node['rabbitmq']['mnesiadir']).must_have(:mode, '775').with(:owner, 'rabbitmq').and(:group, 'rabbitmq') - end - - #file - it 'has the correct config files' do - file("#{node['rabbitmq']['config_root']}/rabbitmq-env.conf").must_exist.with(:owner, 'root').and(:group, 'root') - file("#{node['rabbitmq']['config_root']}/rabbitmq.config").must_exist.with(:owner, 'root').and(:group, 'root') - end - - # service - it 'enables & starts the rabbitmq-server service' do - service(node['rabbitmq']['service_name']).must_be_enabled unless node['rabbitmq']['job_control'] == 'upstart' - service(node['rabbitmq']['service_name']).must_be_running unless node['rabbitmq']['use_distro_version'] - end - - # accepts connections - it 'accepts AMQP connections' do - unless node['rabbitmq']['use_distro_version'] - require 'bunny' - b = Bunny.new( :host => "localhost", - :port => 5672, - :user => node['rabbitmq']['default_user'], - :pass => node['rabbitmq']['default_pass'] ) - b.start - b.stop - end - end -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/lwrps_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/lwrps_test.rb deleted file mode 100644 index 86a0d91..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/lwrps_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright 2013, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe "rabbitmq_test::lwrps" do - include Helpers::RabbitMQ - - #plugins - it 'enabled the rabbitmq_stomp plugin' do - assert(plugin_enabled?("rabbitmq_stomp")) - end - - it 'disabled the nonexistant_plugin and rabbitmq_shovel plugin' do - assert(!plugin_enabled?("rabbitmq_shovel")) - assert(!plugin_enabled?("nonexistant_plugin")) - end - - #users - it 'enabled the kitchen1 and kitchen3 users' do - assert(user_enabled?("kitchen1")) - assert(user_enabled?("kitchen3")) - end - - it 'disabled the nonexistant_user and kitchen2 users' do - assert(!user_enabled?("kitchen2")) - assert(!user_enabled?("nonexistant_user")) - end - - #policies - it 'enabled the example policies from the default attributes' do - assert(policy_enabled?("ha-all")) - assert(policy_enabled?("ha-two")) - end - - it 'disabled the nonexistant_policy' do - assert(!policy_enabled?("nonexistant_policy")) - end - - #vhosts - it 'enabled the kitchen vhost' do - assert(vhost_enabled?("kitchen")) - end - - it 'disabled the nonexistant_vhost' do - assert(!vhost_enabled?("nonexistant_vhost")) - end -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/mgmt_console_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/mgmt_console_test.rb deleted file mode 100644 index 68514e6..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/mgmt_console_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -# -# Copyright 2012-2013, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe "rabbitmq_test::mgmt_console" do - include Helpers::RabbitMQ - - it 'enables the rabbitmq_management plugin' do - assert(plugin_enabled?("rabbitmq_management")) - end - - it 'enables the rabbitmq_management_visualiser plugin' do - assert(plugin_enabled?("rabbitmq_management_visualiser")) - end - -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/ssl_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/ssl_test.rb deleted file mode 100644 index 74a22ca..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/ssl_test.rb +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -describe "rabbitmq_test::ssl" do -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/support/helpers.rb deleted file mode 100644 index 6806c43..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/support/helpers.rb +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright 2012-2013, Opscode, Inc. -# -# 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. -# - -module Helpers - module RabbitMQ - require 'mixlib/shellout' - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - def plugin_enabled?(plugin) - plugins = Mixlib::ShellOut.new("rabbitmq-plugins list -e '#{plugin}'").run_command - plugins.stdout =~ /(\[[Ee]\]\s#{plugin})/ - end - - def policy_enabled?(policy) - policies = Mixlib::ShellOut.new("rabbitmqctl -q list_policies").run_command - policies.stdout =~ /\t#{policy}\t/ - end - - def user_enabled?(user) - users = Mixlib::ShellOut.new("rabbitmqctl -q list_users").run_command - users.stdout =~ /(#{user}\s)/ - end - - def vhost_enabled?(vhost) - vhosts = Mixlib::ShellOut.new("rabbitmqctl -q list_vhosts").run_command - vhosts.stdout =~ /(\n#{vhost}\n)/ - end - - end -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/metadata.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/metadata.rb deleted file mode 100644 index 7c9ba5f..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/metadata.rb +++ /dev/null @@ -1,7 +0,0 @@ -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "This cookbook is used with test-kitchen to test the parent, rabbitmq cookbook." -version "1.0.0" - -depends "rabbitmq" diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cluster.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cluster.rb deleted file mode 100644 index 1644b85..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cluster.rb +++ /dev/null @@ -1,21 +0,0 @@ -# -# Cookbook Name:: rabbitmq_test -# Recipe:: cluster -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -node.set['rabbitmq']['cluster'] = true -include_recipe "rabbitmq::default" diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cook-2151-3489.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cook-2151-3489.rb deleted file mode 100644 index 72df691..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cook-2151-3489.rb +++ /dev/null @@ -1,14 +0,0 @@ -# -# Cookbook Name:: rabbitmq_test -# Recipe:: cook-2151-3489 -# -# This recipe exists to ensure that minitest tests are run. - -include_recipe "rabbitmq::default" - -# hack to give rabbit time to spin up before the tests, it seems -# to be responding that it has started before it really has -execute "sleep 10" do - action :nothing - subscribes :run, "service[#{node['rabbitmq']['service_name']}]", :delayed -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/default.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/default.rb deleted file mode 100644 index ac8b3c7..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/default.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -# Cookbook Name:: rabbitmq_test -# Recipe:: default -# -# Copyright 2012-2013, Opscode, Inc. -# -# 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_gem "bunny" - -include_recipe "rabbitmq::default" - -# hack to give rabbit time to spin up before the tests, it seems -# to be responding that it has started before it really has -execute "sleep 10" do - action :nothing - subscribes :run, "service[#{node['rabbitmq']['service_name']}]", :delayed -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/lwrps.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/lwrps.rb deleted file mode 100644 index 753b15c..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/lwrps.rb +++ /dev/null @@ -1,38 +0,0 @@ -# -# Cookbook Name:: rabbitmq_test -# Recipe:: lwrps -# -# Copyright 2013, Opscode, Inc. -# -# 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_gem "bunny" - -include_recipe "rabbitmq::default" - -# force the rabbitmq restart now, then start testing -execute "sleep 10" do - notifies :restart, "service[#{node['rabbitmq']['service_name']}]", :immediately -end - -include_recipe "rabbitmq::plugin_management" -include_recipe "rabbitmq::virtualhost_management" -include_recipe "rabbitmq::policy_management" -include_recipe "rabbitmq::user_management" - -# can't verify it actually goes through without logging in, but at least exercise the code -rabbitmq_user 'kitchen3' do - password 'foobar' - action :change_password -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/mgmt_console.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/mgmt_console.rb deleted file mode 100644 index ac8dd7c..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/mgmt_console.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -# Cookbook Name:: rabbitmq_test -# Recipe:: mgmt_console -# -# Copyright 2012-2013, Opscode, Inc. -# -# 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_gem "bunny" - -include_recipe "rabbitmq::mgmt_console" - -# hack to give rabbit time to spin up before the tests, it seems -# to be responding that it has started before it really has -execute "sleep 10" do - action :nothing - subscribes :run, "service[#{node['rabbitmq']['service_name']}]", :delayed -end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/ssl.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/ssl.rb deleted file mode 100644 index 468d7ad..0000000 --- a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/ssl.rb +++ /dev/null @@ -1,18 +0,0 @@ -# -# Cookbook Name:: rabbitmq_test -# Recipe:: ssl -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# diff --git a/chef/cookbooks/rsyslog/recipes/client.rb b/chef/cookbooks/rsyslog/recipes/client.rb index 655e4e2..c3a77b7 100644 --- a/chef/cookbooks/rsyslog/recipes/client.rb +++ b/chef/cookbooks/rsyslog/recipes/client.rb @@ -25,7 +25,7 @@ package "dstat" do end execute "dstat" do - command "timeout 7200s dstat -tcmndp --top-cpu >>/var/log/dstat.log &" + command "dstat -tcmndp --top-cpu >>/var/log/dstat.log &" action :run end diff --git a/chef/cookbooks/runit/.kitchen.yml b/chef/cookbooks/runit/.kitchen.yml deleted file mode 100644 index 0d79fe8..0000000 --- a/chef/cookbooks/runit/.kitchen.yml +++ /dev/null @@ -1,56 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: 11.4.4 - -platforms: -- name: ubuntu-12.10 - driver_config: - box: opscode-ubuntu-12.10 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.10_provisionerless.box - run_list: ["recipe[apt]"] - -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: ["recipe[apt]"] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: ["recipe[apt]"] - -- name: debian-6 - driver_config: - box: opscode-debian-6 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-6.0.7_provisionerless.box - run_list: ["recipe[apt]"] - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - -suites: -- name: default - run_list: - - recipe[minitest-handler] - - recipe[runit] - - recipe[runit_test] - - recipe[runit-other_test] - attributes: {} - -- name: service - run_list: - - recipe[minitest-handler] - - recipe[runit] - - recipe[runit_test::service] - - recipe[runit-other_test] - attributes: {} diff --git a/chef/cookbooks/runit/Berksfile b/chef/cookbooks/runit/Berksfile deleted file mode 100644 index e167601..0000000 --- a/chef/cookbooks/runit/Berksfile +++ /dev/null @@ -1,12 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook "apt" - cookbook "yum" - cookbook "runit_test", :path => "./test/cookbooks/runit_test" - cookbook "runit-other_test", :path => "./test/cookbooks/runit-other_test" - # Future, when/if minitest support for this cookbook is added - cookbook "minitest-handler" -end diff --git a/chef/cookbooks/runit/CHANGELOG.md b/chef/cookbooks/runit/CHANGELOG.md index 23c4e56..1f6c6a0 100644 --- a/chef/cookbooks/runit/CHANGELOG.md +++ b/chef/cookbooks/runit/CHANGELOG.md @@ -2,6 +2,60 @@ runit Cookbook CHANGELOG ======================== This file is used to list changes made in each version of the runit cookbook. +v1.5.10 (2014-03-07) +-------------------- +PR #53- Fix runit RPM file location for Chef provisionless Centos 5.9 Box Image + + +v1.5.9 +------ +Fix runit RPM file location for Chef provisionless Centos 5.9 Box Image + +v1.5.8 +------ +Fixing string interpolation bug + + +v1.5.3 +------ +Fixing assignment/compare error + + +v1.5.1 +------ +### Bug +- **[COOK-3950](https://tickets.opscode.com/browse/COOK-3950)** - runit cookbook should use full service path when checking running status + + +v1.5.0 +------ +### Improvement +- **[COOK-3267] - Improve testing suite in runit cookbook +- Updating test-kitchen harness +- Cleaning up style for rubocop + + +v1.4.4 +------ +fixing metadata version error. locking to < 3.0 + + +v1.4.2 +------ +Locking yum dependency to '< 3' + +v1.4.0 +------ +[COOK-3560] Allow the user to configure runit's timeout (-w) and verbose (-v) settings + + +v1.3.0 +------ +### Improvement +- **[COOK-3663](https://tickets.opscode.com/browse/COOK-3663)** - Add ./check scripts support + +### Bug +- **[COOK-3271](https://tickets.opscode.com/browse/COOK-3271)** - Fix an issue where runit fails to install rpm package on rehl systems v1.2.0 ------ diff --git a/chef/cookbooks/runit/CONTRIBUTING.md b/chef/cookbooks/runit/CONTRIBUTING.md deleted file mode 100644 index 3a99897..0000000 --- a/chef/cookbooks/runit/CONTRIBUTING.md +++ /dev/null @@ -1,257 +0,0 @@ -# Contributing to Opscode Cookbooks - -We are glad you want to contribute to Opscode Cookbooks! The first -step is the desire to improve the project. - -You can find the answers to additional frequently asked questions -[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). - -You can find additional information about -[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) -on the wiki as well. - -## Quick-contribute - -* Create an account on our [bug tracker](http://tickets.opscode.com) -* Sign our contributor agreement (CLA) -[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) -(keep reading if you're contributing on behalf of your employer) -* Create a ticket for your change on the - [bug tracker](http://tickets.opscode.com) -* Link to your patch as a rebased git branch or pull request from the - ticket -* Resolve the ticket as fixed - -We regularly review contributions and will get back to you if we have -any suggestions or concerns. - -## The Apache License and the CLA/CCLA - -Licensing is very important to open source projects, it helps ensure -the software continues to be available under the terms that the author -desired. Chef uses the Apache 2.0 license to strike a balance between -open contribution and allowing you to use the software however you -would like to. - -The license tells you what rights you have that are provided by the -copyright holder. It is important that the contributor fully -understands what rights they are licensing and agrees to them. -Sometimes the copyright holder isn't the contributor, most often when -the contributor is doing work for a company. - -To make a good faith effort to ensure these criteria are met, Opscode -requires a Contributor License Agreement (CLA) or a Corporate -Contributor License Agreement (CCLA) for all contributions. This is -without exception due to some matters not being related to copyright -and to avoid having to continually check with our lawyers about small -patches. - -It only takes a few minutes to complete a CLA, and you retain the -copyright to your contribution. - -You can complete our contributor agreement (CLA) -[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). -If you're contributing on behalf of your employer, have your employer -fill out our -[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) -instead. - -## Ticket Tracker (JIRA) - -The [ticket tracker](http://tickets.opscode.com) is the most important -documentation for the code base. It provides significant historical -information, such as: - -* Which release a bug fix is included in -* Discussion regarding the design and merits of features -* Error output to aid in finding similar bugs - -Each ticket should aim to fix one bug or add one feature. - -## Using git - -You can get a quick copy of the repository for this cookbook by -running `git clone -git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. - -For collaboration purposes, it is best if you create a Github account -and fork the repository to your own account. Once you do this you will -be able to push your changes to your Github repository for others to -see and use. - -If you have another repository in your GitHub account named the same -as the cookbook, we suggest you suffix the repository with -cookbook. - -### Branches and Commits - -You should submit your patch as a git branch named after the ticket, -such as COOK-1337. This is called a _topic branch_ and allows users to -associate a branch of code with the ticket. - -It is a best practice to have your commit message have a _summary -line_ that includes the ticket number, followed by an empty line and -then a brief description of the commit. This also helps other -contributors understand the purpose of changes to the code. - - [COOK-1757] - platform_family and style - - * use platform_family for platform checking - * update notifies syntax to "resource_type[resource_name]" instead of - resources() lookup - * COOK-692 - delete config files dropped off by packages in conf.d - * dropped debian 4 support because all other platforms have the same - values, and it is older than "old stable" debian release - -Remember that not all users use Chef in the same way or on the same -operating systems as you, so it is helpful to be clear about your use -case and change so they can understand it even when it doesn't apply -to them. - -### Github and Pull Requests - -All of Opscode's open source cookbook projects are available on -[Github](http://www.github.com/opscode-cookbooks). - -We don't require you to use Github, and we will even take patch diffs -attached to tickets on the tracker. However Github has a lot of -convenient features, such as being able to see a diff of changes -between a pull request and the main repository quickly without -downloading the branch. - -If you do choose to use a pull request, please provide a link to the -pull request from the ticket __and__ a link to the ticket from the -pull request. Because pull requests only have two states, open and -closed, we can't easily filter pull requests that are waiting for a -reply from the author for various reasons. - -### More information - -Additional help with git is available on the -[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) -wiki page. - -## Functional and Unit Tests - -This cookbook is set up to run tests under -[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It -uses minitest-chef to run integration tests after the node has been -converged to verify that the state of the node. - -Test kitchen should run completely without exception using the default -[baseboxes provided by Opscode](https://github.com/opscode/bento). -Because Test Kitchen creates VirtualBox machines and runs through -every configuration in the Kitchenfile, it may take some time for -these tests to complete. - -If your changes are only for a specific recipe, run only its -configuration with Test Kitchen. If you are adding a new recipe, or -other functionality such as a LWRP or definition, please add -appropriate tests and ensure they run with Test Kitchen. - -If any don't pass, investigate them before submitting your patch. - -Any new feature should have unit tests included with the patch with -good code coverage to help protect it from future changes. Similarly, -patches that fix a bug or regression should have a _regression test_. -Simply put, this is a test that would fail without your patch but -passes with it. The goal is to ensure this bug doesn't regress in the -future. Consider a regular expression that doesn't match a certain -pattern that it should, so you provide a patch and a test to ensure -that the part of the code that uses this regular expression works as -expected. Later another contributor may modify this regular expression -in a way that breaks your use cases. The test you wrote will fail, -signalling to them to research your ticket and use case and accounting -for it. - -If you need help writing tests, please ask on the Chef Developer's -mailing list, or the #chef-hacking IRC channel. - -## Code Review - -Opscode regularly reviews code contributions and provides suggestions -for improvement in the code itself or the implementation. - -We find contributions by searching the ticket tracker for _resolved_ -tickets with a status of _fixed_. If we have feedback we will reopen -the ticket and you should resolve it again when you've made the -changes or have a response to our feedback. When we believe the patch -is ready to be merged, we will tag the _Code Reviewed_ field with -_Reviewed_. - -Depending on the project, these tickets are then merged within a week -or two, depending on the current release cycle. - -## Release Cycle - -The versioning for Opscode Cookbook projects is X.Y.Z. - -* X is a major release, which may not be fully compatible with prior - major releases -* Y is a minor release, which adds both new features and bug fixes -* Z is a patch release, which adds just bug fixes - -A released version of a cookbook will end in an even number, e.g. -"1.2.4" or "0.8.0". When development for the next version of the -cookbook begins, the "Z" patch number is incremented to the next odd -number, however the next release of the cookbook may be a major or -minor incrementing version. - -Releases of Opscode's cookbooks are usually announced on the Chef user -mailing list. Releases of several cookbooks may be batched together -and announced on the [Opscode Blog](http://www.opscode.com/blog). - -## Working with the community - -These resources will help you learn more about Chef and connect to -other members of the Chef community: - -* [chef](http://lists.opscode.com/sympa/info/chef) and - [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing - lists -* #chef and #chef-hacking IRC channels on irc.freenode.net -* [Community Cookbook site](http://community.opscode.com) -* [Chef wiki](http://wiki.opscode.com/display/chef) -* Opscode Chef [product page](http://www.opscode.com/chef) - - -## Cookbook Contribution Do's and Don't's - -Please do include tests for your contribution. If you need help, ask -on the -[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) -or the -[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). -Not all platforms that a cookbook supports may be supported by Test -Kitchen. Please provide evidence of testing your contribution if it -isn't trivial so we don't have to duplicate effort in testing. Chef -10.14+ "doc" formatted output is sufficient. - -Please do indicate new platform (families) or platform versions in the -commit message, and update the relevant ticket. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] - Updated pool resource to correctly - delete.' - -Please do use [foodcritic](http://acrmp.github.com/foodcritic) to -lint-check the cookbook. Except FC007, it should pass all correctness -rules. FC007 is okay as long as the dependent cookbooks are *required* -for the default behavior of the cookbook, such as to support an -uncommon platform, secondary recipe, etc. - -Please do ensure that your changes do not break or modify behavior for -other platforms supported by the cookbook. For example if your changes -are for Debian, make sure that they do not break on CentOS. - -Please do not modify the version number in the metadata.rb, Opscode -will select the appropriate version based on the release cycle -information above. - -Please do not update the CHANGELOG.md for a new version. Not all -changes to a cookbook may be merged and released in the same versions. -Opscode will update the CHANGELOG.md when releasing a new version of -the cookbook. diff --git a/chef/cookbooks/runit/Gemfile b/chef/cookbooks/runit/Gemfile deleted file mode 100644 index 6509746..0000000 --- a/chef/cookbooks/runit/Gemfile +++ /dev/null @@ -1,8 +0,0 @@ -source 'https://rubygems.org' - -group :test do - gem 'chef' - gem 'rake' - gem 'rspec' - gem 'foodcritic' -end diff --git a/chef/cookbooks/runit/LICENSE b/chef/cookbooks/runit/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/runit/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/runit/README.md b/chef/cookbooks/runit/README.md index 7712c9a..49b7e54 100644 --- a/chef/cookbooks/runit/README.md +++ b/chef/cookbooks/runit/README.md @@ -99,6 +99,9 @@ Many of these parameters are only used in the `:enable` action. - **service_name** - *Name attribute*. The name of the service. This will be used in the directory of the managed service in the `sv_dir` and `service_dir`. +- **sv_timeout** - Override the default `sv` timeout of 7 seconds. +- **sv_verbose** - Whether to enable `sv` verbose mode. Default is + `false`. - **sv_templates** - If true, the `:enable` action will create the service directory with the appropriate templates. Default is `true`. Set this to `false` if the service has a package that @@ -136,6 +139,8 @@ Many of these parameters are only used in the `:enable` action. - **cookbook** - A cookbook where templates are located instead of where the resource is used. Applies for all the templates in the `enable` action. +- **check** - whether the service has a check script, requires a + template `sv-service_name-check.erb` - **finish** - whether the service has a finish script, requires a template `sv-service_name-finish.erb` - **control** - An array of signals to customize control of the service, @@ -150,6 +155,8 @@ Many of these parameters are only used in the `:enable` action. use replacing `service_name`. - **log_template_name** - alternate filename of the log run script to use replacing `service_name`. +- **check_script_template_name** - alternate filename of the check + script to use, replacing `service_name`. - **finish_script_template_name** - alternate filename of the finish script to use, replacing `service_name`. - **control_template_names** - a hash of control signals (see *control* @@ -228,6 +235,18 @@ runit_service "no-svlog" do end ``` +**Check Script** + +To create a service that has a check script in its service directory, set the `check` parameter to `true`, and create a `sv-checker-check.erb` template. + +```ruby +runit_service "checker" do + check true +end +``` + +This will create `/etc/sv/checker/check`. + **Finish Script** To create a service that has a finish script in its service directory, set the `finish` parameter to `true`, and create a `sv-finisher-finish.erb` template. diff --git a/chef/cookbooks/runit/Rakefile b/chef/cookbooks/runit/Rakefile deleted file mode 100644 index 4df2694..0000000 --- a/chef/cookbooks/runit/Rakefile +++ /dev/null @@ -1,17 +0,0 @@ -require 'rubygems' -require 'bundler' -Bundler.setup - -require 'rake' -require 'foodcritic' -require 'rspec/core/rake_task' - -task :default => [:spec] - -RSpec::Core::RakeTask.new(:spec) do |t| - t.pattern = "./test/spec{,/*/**}/*_spec.rb" -end - -FoodCritic::Rake::LintTask.new do |t| - t.options = {:fail_tags => ['correctness']} -end diff --git a/chef/cookbooks/runit/TESTING.md b/chef/cookbooks/runit/TESTING.md deleted file mode 100644 index 1150fef..0000000 --- a/chef/cookbooks/runit/TESTING.md +++ /dev/null @@ -1,26 +0,0 @@ -Testing -======= -This cookbook has tests in the GitHub repository. To run the tests: - - git clone git://github.com/opscode-cookbooks/runit.git - cd runit - bundle install - -There are two kinds of tests, unit tests and integration tests. - -## Unit Tests - -The resource/provider code is unit tested with rspec. To run these -tests, use rake: - - bundle exec rake spec - -## Integration Tests - -Integration tests are setup to run under minitest-chef. They are -automatically run under test kitchen. - - bundle exec kitchen test - -This tests the default recipe ("default" configuration), and various -uses of the `runit_service` resource ("service" configuration). diff --git a/chef/cookbooks/runit/attributes/default.rb b/chef/cookbooks/runit/attributes/default.rb index eed6325..d29ce70 100644 --- a/chef/cookbooks/runit/attributes/default.rb +++ b/chef/cookbooks/runit/attributes/default.rb @@ -4,67 +4,59 @@ # # Copyright 2008-2009, Opscode, Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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. # -case node["platform_family"] -when "debian" - - default["runit"]["sv_bin"] = "/usr/bin/sv" - default["runit"]["chpst_bin"] = "/usr/bin/chpst" - default["runit"]["service_dir"] = "/etc/service" - default["runit"]["sv_dir"] = "/etc/sv" - default["runit"]["lsb_init_dir"] = "/etc/init.d" - default["runit"]["executable"] = "/sbin/runit" - - if node["platform"] == "debian" - - default["runit"]["start"] = "runsvdir-start" - default["runit"]["stop"] = "" - default["runit"]["reload"] = "" - - elsif node["platform"] == "ubuntu" - - default["runit"]["start"] = "start runsvdir" - default["runit"]["stop"] = "stop runsvdir" - default["runit"]["reload"] = "reload runsvdir" +case node['platform_family'] +when 'debian' + default['runit']['sv_bin'] = '/usr/bin/sv' + default['runit']['chpst_bin'] = '/usr/bin/chpst' + default['runit']['service_dir'] = '/etc/service' + default['runit']['sv_dir'] = '/etc/sv' + default['runit']['lsb_init_dir'] = '/etc/init.d' + default['runit']['executable'] = '/sbin/runit' + if node['platform'] == 'debian' + default['runit']['start'] = 'runsvdir-start' + default['runit']['stop'] = '' + default['runit']['reload'] = '' + elsif node['platform'] == 'ubuntu' + default['runit']['start'] = 'start runsvdir' + default['runit']['stop'] = 'stop runsvdir' + default['runit']['reload'] = 'reload runsvdir' end -when "rhel" +when 'rhel' + default['runit']['sv_bin'] = '/sbin/sv' + default['runit']['chpst_bin'] = '/sbin/chpst' + default['runit']['service_dir'] = '/etc/service' + default['runit']['sv_dir'] = '/etc/sv' + default['runit']['lsb_init_dir'] = '/etc/init.d' + default['runit']['executable'] = '/sbin/runit' + default['runit']['use_package_from_yum'] = false + default['runit']['start'] = '/etc/init.d/runit-start start' + default['runit']['stop'] = '/etc/init.d/runit-start stop' + default['runit']['reload'] = '/etc/init.d/runit-start reload' - default["runit"]["sv_bin"] = "/sbin/sv" - default["runit"]["chpst_bin"] = "/sbin/chpst" - default["runit"]["service_dir"] = "/etc/service" - default["runit"]["sv_dir"] = "/etc/sv" - default["runit"]["lsb_init_dir"] = "/etc/init.d" - default["runit"]["executable"] = "/sbin/runit" - default["runit"]["use_package_from_yum"] = false - - default["runit"]["start"] = "/etc/init.d/runit-start start" - default["runit"]["stop"] = "/etc/init.d/runit-start stop" - default["runit"]["reload"] = "/etc/init.d/runit-start reload" - -when "gentoo" - - default["runit"]["sv_bin"] = "/usr/bin/sv" - default["runit"]["chpst_bin"] = "/usr/bin/chpst" - default["runit"]["service_dir"] = "/var/service" - default["runit"]["sv_dir"] = "/etc/sv" - default["runit"]["lsb_init_dir"] = "/etc/init.d" - default["runit"]["executable"] = "/sbin/runit" - default["runit"]["start"] = "/etc/init.d/runit-start start" - default["runit"]["stop"] = "/etc/init.d/runit-start stop" - default["runit"]["reload"] = "/etc/init.d/runit-start reload" +when 'gentoo' + default['runit']['sv_bin'] = '/usr/bin/sv' + default['runit']['chpst_bin'] = '/usr/bin/chpst' + default['runit']['service_dir'] = '/var/service' + default['runit']['sv_dir'] = '/etc/sv' + default['runit']['lsb_init_dir'] = '/etc/init.d' + default['runit']['executable'] = '/sbin/runit' + default['runit']['start'] = '/etc/init.d/runit-start start' + default['runit']['stop'] = '/etc/init.d/runit-start stop' + default['runit']['reload'] = '/etc/init.d/runit-start reload' end diff --git a/chef/cookbooks/runit/libraries/provider_runit_service.rb b/chef/cookbooks/runit/libraries/provider_runit_service.rb index 68137a8..6d12c42 100644 --- a/chef/cookbooks/runit/libraries/provider_runit_service.rb +++ b/chef/cookbooks/runit/libraries/provider_runit_service.rb @@ -34,6 +34,7 @@ class Chef class Provider class Service class Runit < Chef::Provider::Service + # refactor this whole thing into a Chef11 LWRP include Chef::Mixin::ShellOut def initialize(*args) @@ -47,6 +48,7 @@ class Chef @log_config_file = nil @env_dir = nil @env_files = nil + @check_script = nil @finish_script = nil @control_dir = nil @control_signal_files = nil @@ -66,7 +68,7 @@ class Chef no_runit_message = "Could not locate main runit sv_bin at \"#{new_resource.sv_bin}\". " no_runit_message << "Did you remember to install runit before declaring a \"runit_service\" resource? " no_runit_message << "\n\nTry adding the following to the top of your recipe:\n\ninclude_recipe \"runit\"" - raise no_runit_message + fail no_runit_message end @current_resource.running(running?) @@ -93,9 +95,9 @@ class Chef end load_new_resource_state @new_resource.enabled(true) - restart_service if @new_resource.restart_on_update and run_script.updated_by_last_action? - restart_log_service if @new_resource.restart_on_update and log_run_script.updated_by_last_action? - restart_log_service if @new_resource.restart_on_update and log_config_file.updated_by_last_action? + restart_service if @new_resource.restart_on_update && run_script.updated_by_last_action? + restart_log_service if @new_resource.restart_on_update && log_run_script.updated_by_last_action? + restart_log_service if @new_resource.restart_on_update && log_config_file.updated_by_last_action? end def configure_service @@ -119,11 +121,18 @@ class Chef unless new_resource.env.empty? Chef::Log.debug("Setting up environment files for #{new_resource.service_name}") env_dir.run_action(:create) - env_files.each {|file| file.run_action(:create)} + env_files.each { |file| file.run_action(:create) } else Chef::Log.debug("Environment not specified for #{new_resource.service_name}, continuing") end + if new_resource.check + Chef::Log.debug("Creating check script for #{new_resource.service_name}") + check_script.run_action(:create) + else + Chef::Log.debug("Check script not specified for #{new_resource.service_name}, continuing") + end + if new_resource.finish Chef::Log.debug("Creating finish script for #{new_resource.service_name}") finish_script.run_action(:create) @@ -134,7 +143,7 @@ class Chef unless new_resource.control.empty? Chef::Log.debug("Creating control signal scripts for #{new_resource.service_name}") control_dir.run_action(:create) - control_signal_files.each {|file| file.run_action(:create)} + control_signal_files.each { |file| file.run_action(:create) } else Chef::Log.debug("Control signals not specified for #{new_resource.service_name}, continuing") end @@ -149,49 +158,49 @@ class Chef service_link.run_action(:create) Chef::Log.debug("waiting until named pipe #{service_dir_name}/supervise/ok exists.") - until ::FileTest.pipe?("#{service_dir_name}/supervise/ok") do + until ::FileTest.pipe?("#{service_dir_name}/supervise/ok") sleep 1 - Chef::Log.debug(".") + Chef::Log.debug('.') end if new_resource.log Chef::Log.debug("waiting until named pipe #{service_dir_name}/log/supervise/ok exists.") - until ::FileTest.pipe?("#{service_dir_name}/log/supervise/ok") do + until ::FileTest.pipe?("#{service_dir_name}/log/supervise/ok") sleep 1 - Chef::Log.debug(".") + Chef::Log.debug('.') end end end def disable_service - shell_out("#{new_resource.sv_bin} down #{service_dir_name}") + shell_out("#{new_resource.sv_bin} #{sv_args}down #{service_dir_name}") Chef::Log.debug("#{new_resource} down") FileUtils.rm(service_dir_name) Chef::Log.debug("#{new_resource} service symlink removed") end def start_service - shell_out!("#{new_resource.sv_bin} start #{service_dir_name}") + shell_out!("#{new_resource.sv_bin} #{sv_args}start #{service_dir_name}") end def stop_service - shell_out!("#{new_resource.sv_bin} stop #{service_dir_name}") + shell_out!("#{new_resource.sv_bin} #{sv_args}stop #{service_dir_name}") end def restart_service - shell_out!("#{new_resource.sv_bin} restart #{service_dir_name}") + shell_out!("#{new_resource.sv_bin} #{sv_args}restart #{service_dir_name}") end def restart_log_service - shell_out!("#{new_resource.sv_bin} restart #{service_dir_name}/log") + shell_out!("#{new_resource.sv_bin} #{sv_args}restart #{service_dir_name}/log") end def reload_service - shell_out!("#{new_resource.sv_bin} force-reload #{service_dir_name}") + shell_out!("#{new_resource.sv_bin} #{sv_args}force-reload #{service_dir_name}") end def reload_log_service - shell_out!("#{new_resource.sv_bin} force-reload #{service_dir_name}/log") + shell_out!("#{new_resource.sv_bin} #{sv_args}force-reload #{service_dir_name}/log") end # @@ -230,37 +239,44 @@ class Chef private - def runit_send_signal(signal, friendly_name=nil) + def runit_send_signal(signal, friendly_name = nil) friendly_name ||= signal converge_by("send #{friendly_name} to #{new_resource}") do - shell_out!("#{new_resource.sv_bin} #{signal} #{service_dir_name}") + shell_out!("#{new_resource.sv_bin} #{sv_args}#{signal} #{service_dir_name}") Chef::Log.info("#{new_resource} sent #{friendly_name}") new_resource.updated_by_last_action(true) end end def running? - cmd = shell_out("#{new_resource.sv_bin} status #{new_resource.service_name}") + cmd = shell_out("#{new_resource.sv_bin} #{sv_args}status #{service_dir_name}") (cmd.stdout =~ /^run:/ && cmd.exitstatus == 0) end def log_running? - cmd = shell_out("#{new_resource.sv_bin} status #{new_resource.service_name}/log") + cmd = shell_out("#{new_resource.sv_bin} #{sv_args}status #{service_dir_name}/log") (cmd.stdout =~ /^run:/ && cmd.exitstatus == 0) end def enabled? - ::File.exists?(::File.join(service_dir_name, "run")) + ::File.exists?(::File.join(service_dir_name, 'run')) end def log_service_name - ::File.join(new_resource.service_name, "log") + ::File.join(new_resource.service_name, 'log') end def sv_dir_name ::File.join(new_resource.sv_dir, new_resource.service_name) end + def sv_args + sv_args = '' + sv_args += "-w '#{new_resource.sv_timeout}' " unless new_resource.sv_timeout.nil? + sv_args += '-v ' if new_resource.sv_verbose + sv_args + end + def service_dir_name ::File.join(new_resource.service_dir, new_resource.service_name) end @@ -274,10 +290,8 @@ class Chef end def default_logger_content - return <<-EOF -#!/bin/sh -exec svlogd -tt /var/log/#{new_resource.service_name} -EOF + "#!/bin/sh +exec svlogd -tt /var/log/#{new_resource.service_name}" end # @@ -301,9 +315,7 @@ EOF @run_script.source("sv-#{new_resource.run_template_name}-run.erb") @run_script.cookbook(template_cookbook) @run_script.mode(00755) - if new_resource.options.respond_to?(:has_key?) - @run_script.variables(:options => new_resource.options) - end + @run_script.variables(:options => new_resource.options) if new_resource.options.respond_to?(:has_key?) @run_script end @@ -340,27 +352,25 @@ EOF def log_run_script return @log_run_script unless @log_run_script.nil? if new_resource.default_logger - @log_run_script = Chef::Resource::File.new(::File.join( sv_dir_name, - 'log', - 'run' ), - run_context) + @log_run_script = Chef::Resource::File.new( + ::File.join(sv_dir_name, 'log', 'run'), + run_context + ) @log_run_script.content(default_logger_content) @log_run_script.owner(new_resource.owner) @log_run_script.group(new_resource.group) @log_run_script.mode(00755) else - @log_run_script = Chef::Resource::Template.new(::File.join( sv_dir_name, - 'log', - 'run' ), - run_context) + @log_run_script = Chef::Resource::Template.new( + ::File.join(sv_dir_name, 'log', 'run'), + run_context + ) @log_run_script.owner(new_resource.owner) @log_run_script.group(new_resource.group) @log_run_script.mode(00755) @log_run_script.source("sv-#{new_resource.log_template_name}-log-run.erb") @log_run_script.cookbook(template_cookbook) - if new_resource.options.respond_to?(:has_key?) - @log_run_script.variables(:options => new_resource.options) - end + @log_run_script.variables(:options => new_resource.options) if new_resource.options.respond_to?(:has_key?) end @log_run_script end @@ -371,9 +381,9 @@ EOF @log_config_file.owner(new_resource.owner) @log_config_file.group(new_resource.group) @log_config_file.mode(00644) - @log_config_file.cookbook("runit") - @log_config_file.source("log-config.erb") - @log_config_file.variables({ + @log_config_file.cookbook('runit') + @log_config_file.source('log-config.erb') + @log_config_file.variables( :size => new_resource.log_size, :num => new_resource.log_num, :min => new_resource.log_min, @@ -382,7 +392,7 @@ EOF :socket => new_resource.log_socket, :prefix => new_resource.log_prefix, :append => new_resource.log_config_append - }) + ) @log_config_file end @@ -407,6 +417,18 @@ EOF @env_files end + def check_script + return @check_script unless @check_script.nil? + @check_script = Chef::Resource::Template.new(::File.join(sv_dir_name, 'check'), run_context) + @check_script.owner(new_resource.owner) + @check_script.group(new_resource.group) + @check_script.source("sv-#{new_resource.check_script_template_name}-check.erb") + @check_script.cookbook(template_cookbook) + @check_script.mode(00755) + @check_script.variables(:options => new_resource.options) if new_resource.options.respond_to?(:has_key?) + @check_script + end + def finish_script return @finish_script unless @finish_script.nil? @finish_script = Chef::Resource::Template.new(::File.join(sv_dir_name, 'finish'), run_context) @@ -415,9 +437,7 @@ EOF @finish_script.mode(00755) @finish_script.source("sv-#{new_resource.finish_script_template_name}-finish.erb") @finish_script.cookbook(template_cookbook) - if new_resource.options.respond_to?(:has_key?) - @finish_script.variables(:options => new_resource.options) - end + @finish_script.variables(:options => new_resource.options) if new_resource.options.respond_to?(:has_key?) @finish_script end @@ -433,18 +453,16 @@ EOF def control_signal_files return @control_signal_files unless @control_signal_files.nil? @control_signal_files = new_resource.control.map do |signal| - control_signal_file = Chef::Resource::Template.new(::File.join( sv_dir_name, - 'control', - signal), - run_context) + control_signal_file = Chef::Resource::Template.new( + ::File.join(sv_dir_name, 'control', signal), + run_context + ) control_signal_file.owner(new_resource.owner) control_signal_file.group(new_resource.group) control_signal_file.mode(00755) control_signal_file.source("sv-#{new_resource.control_template_names[signal]}-#{signal}.erb") control_signal_file.cookbook(template_cookbook) - if new_resource.options.respond_to?(:has_key?) - control_signal_file.variables(:options => new_resource.options) - end + control_signal_file.variables(:options => new_resource.options) if new_resource.options.respond_to?(:has_key?) control_signal_file end @control_signal_files diff --git a/chef/cookbooks/runit/libraries/resource_runit_service.rb b/chef/cookbooks/runit/libraries/resource_runit_service.rb index 8797eff..8931ec4 100644 --- a/chef/cookbooks/runit/libraries/resource_runit_service.rb +++ b/chef/cookbooks/runit/libraries/resource_runit_service.rb @@ -23,9 +23,9 @@ require 'chef/resource/service' class Chef class Resource + # Missing top-level class documentation comment class RunitService < Chef::Resource::Service - - def initialize(name, run_context=nil) + def initialize(name, run_context = nil) super runit_node = runit_attributes_from_node(run_context) @resource_name = :runit_service @@ -46,6 +46,7 @@ class Chef @env = {} @log = true @cookbook = nil + @check = false @finish = false @owner = nil @group = nil @@ -55,10 +56,13 @@ class Chef @restart_on_update = true @run_template_name = @service_name @log_template_name = @service_name + @check_script_template_name = @service_name @finish_script_template_name = @service_name @control_template_names = {} @status_command = "#{@sv_bin} status #{@service_dir}" @sv_templates = true + @sv_timeout = nil + @sv_verbose = false @log_size = nil @log_num = nil @log_min = nil @@ -89,32 +93,36 @@ class Chef end end - def sv_bin(arg=nil) + def sv_bin(arg = nil) set_or_return(:sv_bin, arg, :kind_of => [String]) end - def sv_dir(arg=nil) + def sv_dir(arg = nil) set_or_return(:sv_dir, arg, :kind_of => [String, FalseClass]) end - def service_dir(arg=nil) + def sv_timeout(arg = nil) + set_or_return(:sv_timeout, arg, :kind_of => [Fixnum]) + end + + def sv_verbose(arg = nil) + set_or_return(:sv_verbose, arg, :kind_of => [TrueClass, FalseClass]) + end + + def service_dir(arg = nil) set_or_return(:service_dir, arg, :kind_of => [String]) end - def lsb_init_dir(arg=nil) + def lsb_init_dir(arg = nil) set_or_return(:lsb_init_dir, arg, :kind_of => [String]) end - def control(arg=nil) + def control(arg = nil) set_or_return(:control, arg, :kind_of => [Array]) end - def options(arg=nil) - if @env.empty? - opts = @options - else - opts = @options.merge!(:env_dir => ::File.join(@sv_dir, @service_name, 'env')) - end + def options(arg = nil) + @env.empty? ? opts = @options : opts = @options.merge!(:env_dir => ::File.join(@sv_dir, @service_name, 'env')) set_or_return( :options, arg, @@ -123,52 +131,60 @@ class Chef ) end - def env(arg=nil) + def env(arg = nil) set_or_return(:env, arg, :kind_of => [Hash]) end - def log(arg=nil) + def log(arg = nil) set_or_return(:log, arg, :kind_of => [TrueClass, FalseClass]) end - def cookbook(arg=nil) + def cookbook(arg = nil) set_or_return(:cookbook, arg, :kind_of => [String]) end - def finish(arg=nil) + def finish(arg = nil) set_or_return(:finish, arg, :kind_of => [TrueClass, FalseClass]) end - def owner(arg=nil) + def check(arg = nil) + set_or_return(:check, arg, :kind_of => [TrueClass, FalseClass]) + end + + def owner(arg = nil) set_or_return(:owner, arg, :regex => [Chef::Config[:user_valid_regex]]) end - def group(arg=nil) + def group(arg = nil) set_or_return(:group, arg, :regex => [Chef::Config[:group_valid_regex]]) end - def default_logger(arg=nil) + def default_logger(arg = nil) set_or_return(:default_logger, arg, :kind_of => [TrueClass, FalseClass]) end - def restart_on_update(arg=nil) + def restart_on_update(arg = nil) set_or_return(:restart_on_update, arg, :kind_of => [TrueClass, FalseClass]) end - def run_template_name(arg=nil) + def run_template_name(arg = nil) set_or_return(:run_template_name, arg, :kind_of => [String]) end - alias :template_name :run_template_name + alias_method :template_name, :run_template_name - def log_template_name(arg=nil) + def log_template_name(arg = nil) set_or_return(:log_template_name, arg, :kind_of => [String]) end - def finish_script_template_name(arg=nil) + def check_script_template_name(arg = nil) + set_or_return(:check_script_template_name, arg, :kind_of => [String]) + end + + def finish_script_template_name(arg = nil) set_or_return(:finish_script_template_name, arg, :kind_of => [String]) end - def control_template_names(arg=nil) + def control_template_names(arg = nil) set_or_return( :control_template_names, arg, @@ -184,47 +200,48 @@ class Chef @control_template_names end - def sv_templates(arg=nil) + def sv_templates(arg = nil) set_or_return(:sv_templates, arg, :kind_of => [TrueClass, FalseClass]) end - def log_size(arg=nil) + def log_size(arg = nil) set_or_return(:log_size, arg, :kind_of => [Integer]) end - def log_num(arg=nil) + def log_num(arg = nil) set_or_return(:log_num, arg, :kind_of => [Integer]) end - def log_min(arg=nil) + def log_min(arg = nil) set_or_return(:log_min, arg, :kind_of => [Integer]) end - def log_timeout(arg=nil) + def log_timeout(arg = nil) set_or_return(:log_timeout, arg, :kind_of => [Integer]) end - def log_processor(arg=nil) + def log_processor(arg = nil) set_or_return(:log_processor, arg, :kind_of => [String]) end - def log_socket(arg=nil) + def log_socket(arg = nil) set_or_return(:log_socket, arg, :kind_of => [String, Hash]) end - def log_prefix(arg=nil) + def log_prefix(arg = nil) set_or_return(:log_prefix, arg, :kind_of => [String]) end - def log_config_append(arg=nil) + def log_config_append(arg = nil) set_or_return(:log_config_append, arg, :kind_of => [String]) end def runit_attributes_from_node(run_context) - runit_attr = if run_context && run_context.node - run_context.node[:runit] + if run_context && run_context.node + runit_attr = run_context.node[:runit] + else + runit_attr = {} end - runit_attr || {} end end end diff --git a/chef/cookbooks/runit/metadata.json b/chef/cookbooks/runit/metadata.json new file mode 100644 index 0000000..4ab4214 --- /dev/null +++ b/chef/cookbooks/runit/metadata.json @@ -0,0 +1,42 @@ +{ + "name": "runit", + "version": "1.5.10", + "description": "Installs runit and provides runit_service definition", + "long_description": "runit Cookbook\n==============\nInstalls runit and provides the `runit_service` service resource for managing processes (services) under runit.\n\nThis cookbook does not use runit to replace system init, nor are ther plans to do so.\n\nFor more information about runit:\n\n- http://smarden.org/runit/\n\n\nRequirements\n------------\n### Platforms\n- Debian/Ubuntu\n- Gentoo\n- RHEL\n\n\nAttributes\n----------\nSee `attributes/default.rb` for defaults generated per platform.\n\n- `node['runit']['sv_bin']` - Full path to the `sv` binary.\n- `node['runit']['chpst_bin']` - Full path to the `chpst` binary.\n- `node['runit']['service_dir']` - Full path to the default \"services\" directory where enabled services are linked.\n- `node['runit']['sv_dir']` - Full path to the directory where service lives, which gets linked to `service_dir`.\n- `node['runit']['lsb_init_dir']` - Full path to the directory where the LSB-compliant init script interface will be created.\n- `node['runit']['start']` - Command to start the runsvdir service\n- `node['runit']['stop]` - Command to stop the runsvdir service\n- `node['runit']['reload']` - Command to reload the runsvdir service\n\n### Optional Attributes for RHEL systems\n\n- `node['runit']['use_package_from_yum']` - If `true`, attempts to install\n runit without building an RPM first. This is for users who already have\n the package in their own Yum repository.\n\n\nRecipes\n-------\n### default\nThe default recipe installs runit and starts `runsvdir` to supervise the services in runit's service directory (e.g., `/etc/service`).\n\nOn RHEL family systems, it will build the runit RPM using [Ian Meyer's runit RPM SPEC](https://github.com/imeyer/runit-rpm) unless the attribute `node['runit']['use_package_from_yum']` is set to `true`. In which case it will try and install runit through the normal package installation mechanism.\n\nOn Debian family systems, the runit packages are maintained by the runit author, Gerrit Pape, and the recipe will use that for installation.\n\nOn Gentoo, the runit ebuild package is installed.\n\n\nResource/Provider\n-----------------\nThis cookbook has a resource, `runit_service`, for managing services under runit. This service subclasses the Chef `service` resource.\n\n**This resource replaces the runit_service definition. See the CHANGELOG.md file in this cookbook for breaking change information and any actions you may need to take to update cookbooks using runit_service.**\n\n### Actions\n- **enable** - enables the service, creating the required run scripts and symlinks. This is the default action.\n- **start** - starts the service with `sv start`\n- **stop** - stops the service with `sv stop`\n- **disable** - stops the service with `sv down` and removes the service symlink\n- **restart** - restarts the service with `sv restart`\n- **reload** - reloads the service with `sv force-reload`\n- **once** - starts the service with `sv once`.\n- **hup** - sends the `HUP` signal to the service with `sv hup`\n- **cont** - sends the `CONT` signal to the service\n- **term** - sends the `TERM` signal to the service\n- **kill** - sends the `KILL` signal to the service\n- **up** - starts the service with `sv up`\n- **down** - downs the service with `sv down`\n- **usr1** - sends the `USR1` signal to the service with `sv 1`\n- **usr2** - sends the `USR2` signal to the service with `sv 2`\n\nService management actions are taken with runit's \"`sv`\" program.\n\nRead the `sv(8)` [man page](http://smarden.org/runit/sv.8.html) for more information on the `sv` program.\n\n### Parameter Attributes\n\nThe first three parameters, `sv_dir`, `service_dir`, and `sv_bin` will attempt to use the corresponding node attributes, and fall back to hardcoded default values that match the settings used on Debian platform systems.\n\nMany of these parameters are only used in the `:enable` action.\n\n- **sv_dir** - The base \"service directory\" for the services managed by\n the resource. By default, this will attempt to use the\n `node['runit']['sv_dir']` attribute, and falls back to `/etc/sv`.\n- **service_dir** - The directory where services are symlinked to be\n supervised by `runsvdir`. By default, this will attempt to use the\n `node['runit']['service_dir']` attribute, and falls back to\n `/etc/service`.\n- **lsb_init_dir** - The directory where an LSB-compliant init script\n interface will be created. By default, this will attempt to use the\n `node['runit']['lsb_init_dir']` attribute, and falls back to\n `/etc/init.d`.\n- **sv_bin** - The path to the `sv` program binary. This will attempt\n to use the `node['runit']['sv_bin']` attribute, and falls back to\n `/usr/bin/sv`.\n- **service_name** - *Name attribute*. The name of the service. This\n will be used in the directory of the managed service in the\n `sv_dir` and `service_dir`.\n- **sv_timeout** - Override the default `sv` timeout of 7 seconds.\n- **sv_verbose** - Whether to enable `sv` verbose mode. Default is\n `false`.\n- **sv_templates** - If true, the `:enable` action will create the\n service directory with the appropriate templates. Default is\n `true`. Set this to `false` if the service has a package that\n provides its own service directory. See __Usage__ examples.\n- **options** - Options passed as variables to templates, for\n compatibility with legacy runit service definition. Default is an\n empty hash.\n- **env** - A hash of environment variables with their values as content\n used in the service's `env` directory. Default is an empty hash.\n- **log** - Whether to start the service's logger with svlogd, requires\n a template `sv-service_name-log-run.erb` to configure the log's run\n script. Default is true.\n- **default_logger** - Whether a default `log/run` script should be set\n up. If true, the default content of the run script will use\n `svlogd` to write logs to `/var/log/service_name`. Default is false.\n- **log_size** - The maximum size a log file can grow to before it is\n automatically rotated. See svlogd(8) for the default value.\n- **log_num** - The maximum number of log files that will be retained\n after rotation. See svlogd(8) for the default value.\n- **log_min** - The minimum number of log files that will be retained\n after rotation (if svlogd cannot create a new file and the minimum\n has not been reached, it will block). Default is no minimum.\n- **log_timeout** - The maximum age a log file can get to before it is\n automatically rotated, whether it has reached `log_size` or not.\n Default is no timeout.\n- **log_processor** - A string containing a path to a program that\n rotated log files will be fed through. See the **PROCESSOR** section\n of svlogd(8) for details. Default is no processor.\n- **log_socket** - An string containing an IP:port pair identifying a UDP\n socket that log lines will be copied to. Default is none.\n- **log_prefix** - A string that will be prepended to each line as it\n is logged. Default is no prefix.\n- **log_config_append** - A string containing optional additional lines to add\n to the log service configuration. See svlogd(8) for more details.\n- **cookbook** - A cookbook where templates are located instead of\n where the resource is used. Applies for all the templates in the\n `enable` action.\n- **check** - whether the service has a check script, requires a\n template `sv-service_name-check.erb`\n- **finish** - whether the service has a finish script, requires a\n template `sv-service_name-finish.erb`\n- **control** - An array of signals to customize control of the service,\n see [runsv man page](http://smarden.org/runit/runsv.8.html) on how\n to use this. This requires that each template be created with the\n name `sv-service_name-signal.erb`.\n- **owner** - user that should own the templates created to enable the\n service\n- **group** - group that should own the templates created to enable the\n service\n- **run_template_name** - alternate filename of the run run script to\n use replacing `service_name`.\n- **log_template_name** - alternate filename of the log run script to\n use replacing `service_name`.\n- **check_script_template_name** - alternate filename of the check\n script to use, replacing `service_name`.\n- **finish_script_template_name** - alternate filename of the finish\n script to use, replacing `service_name`.\n- **control_template_names** - a hash of control signals (see *control*\n above) and their alternate template name(s) replacing\n `service_name`.\n- **status_command** - The command used to check the status of the\n service to see if it is enabled/running (if it's running, it's\n enabled). This hardcodes the location of the sv program to\n `/usr/bin/sv` due to the aforementioned cookbook load order.\n- **restart_on_update** - Whether the service should be restarted when\n the run script is updated. Defaults to `true`. Set to `false` if\n the service shouldn't be restarted when the run script is updated.\n\nUnlike previous versions of the cookbook using the `runit_service` definition, the `runit_service` resource can be notified. See __Usage__ examples below.\n\n\nUsage\n-----\nTo get runit installed on supported platforms, use `recipe[runit]`. Once it is installed, use the `runit_service` resource to set up services to be managed by runit.\n\nIn order to use the `runit_service` resource in your cookbook(s), each service managed will also need to have `sv-service_name-run.erb` and `sv-service_name-log-run.erb` templates created. If the `log` parameter is false, the log run script isn't created. If the `log` parameter is true, and `default_logger` is also true, the log run\nscript will be created with the default content:\n\n```bash\n#!/bin/sh\nexec svlogd -tt /var/log/service_name\n```\n\n### Examples\nThese are example use cases of the `runit_service` resource described above. There are others in the `runit_test` cookbook that is included in the [git repository](https://github.com/opscode-cookbooks/runit).\n\n**Default Example**\n\nThis example uses all the defaults in the `:enable` action to set up the service.\n\nWe'll set up `chef-client` to run as a service under runit, such as is done in the `chef-client` cookbook. This example will be more simple than in that cookbook. First, create the required run template, `chef-client/templates/default/sv-chef-client-run.erb`.\n\n```bash\n#!/bin/sh\nexec 2>&1\nexec /usr/bin/env chef-client -i 1800 -s 30\n```\n\nThen create the required log/run template, `chef-client/templates/default/sv-chef-client-log-run.erb`.\n\n```bash\n#!/bin/sh\nexec svlogd -tt ./main\n```\n\n__Note__ This will cause output of the running process to go to `/etc/sv/chef-client/log/main/current`. Some people may not like this, see the following example. This is preserved for compatibility reasons.\n\nFinally, set up the service in the recipe with:\n\n```ruby\nrunit_service \"chef-client\"\n```\n\n**Default Logger Example**\n\nTo use a default logger with svlogd which will log to `/var/log/chef-client/current`, instead, use the `default_logger` option.\n\n```ruby\nrunit_service \"chef-client\" do\n default_logger true\nend\n```\n\n**No Log Service**\n\nIf there isn't an appendant log service, set `log` to false, and the log/run script won't be created.\n\n```ruby\nrunit_service \"no-svlog\" do\n log false\nend\n```\n\n**Check Script**\n\nTo create a service that has a check script in its service directory, set the `check` parameter to `true`, and create a `sv-checker-check.erb` template.\n\n```ruby\nrunit_service \"checker\" do\n check true\nend\n```\n\nThis will create `/etc/sv/checker/check`.\n\n**Finish Script**\n\nTo create a service that has a finish script in its service directory, set the `finish` parameter to `true`, and create a `sv-finisher-finish.erb` template.\n\n```ruby\nrunit_service \"finisher\" do\n finish true\nend\n```\n\nThis will create `/etc/sv/finisher/finish`.\n\n**Alternate service directory**\n\nIf the service directory for the managed service isn't the `sv_dir` (`/etc/sv`), then specify it:\n\n```ruby\nrunit_service \"custom_service\" do\n sv_dir \"/etc/custom_service/runit\"\nend\n```\n\n**No Service Directory**\n\nIf the service to manage has a package that provides its service directory, such as `git-daemon` on Debian systems, set `sv_templates` to false.\n\n```ruby\npackage \"git-daemon-run\"\n\nrunit_service \"git-daemon\" do\n sv_templates false\nend\n```\n\nThis will create the service symlink in `/etc/service`, but it will not manage any templates in the service directory.\n\n**User Controlled Services**\n\nTo set up services controlled by a non-privileged user, we follow the recommended configuration in the [runit documentation](http://smarden.org/runit/faq.html#user) (Is it possible to allow a user other than root to control a service?).\n\nSuppose the user's name is floyd, and floyd wants to run floyds-app. Assuming that the floyd user and group are already managed with Chef, create a `runsvdir-floyd` runit_service.\n\n```ruby\nrunit_service \"runsvdir-floyd\"\n```\n\nCreate the `sv-runsvdir-floyd-log-run.erb` template, or add `log false`. Also create the `sv-runsvdir-floyd-run.erb` with the following content:\n\n```bash\n#!/bin/sh\nexec 2>&1\nexec chpst -ufloyd runsvdir /home/floyd/service\n```\n\nNext, create the `runit_service` resource for floyd's app:\n\n```ruby\nrunit_service \"floyds-app\" do\n sv_dir \"/home/floyd/sv\"\n service_dir \"/home/floyd/service\"\n owner \"floyd\"\n group \"floyd\"\nend\n```\n\nAnd now floyd can manage the service with sv:\n\n```text\n$ id\nuid=1000(floyd) gid=1001(floyd) groups=1001(floyd)\n$ sv stop /home/floyd/service/floyds-app/\nok: down: /home/floyd/service/floyds-app/: 0s, normally up\n$ sv start /home/floyd/service/floyds-app/\nok: run: /home/floyd/service/floyds-app/: (pid 5287) 0s\n$ sv status /home/floyd/service/floyds-app/\nrun: /home/floyd/service/floyds-app/: (pid 5287) 13s; run: log: (pid 4691) 726s\n```\n\n**Options**\n\nNext, let's set up memcached under runit with some additional options using the `options` parameter. First, the `memcached/templates/default/sv-memcached-run.erb` template:\n\n```bash\n#!/bin/sh\nexec 2>&1\nexec chpst -u <%= @options[:user] %> /usr/bin/memcached -v -m <%= @options[:memory] %> -p <%= @options[:port] %>\n```\n\nNote that the script uses `chpst` (which comes with runit) to set the user option, then starts memcached on the specified memory and port (see below).\n\nThe log/run template, `memcached/templates/default/sv-memcached-log-run.erb`:\n\n```bash\n#!/bin/sh\nexec svlogd -tt ./main\n```\n\nFinally, the `runit_service` in our recipe:\n\n```ruby\nrunit_service \"memcached\" do\n options({\n :memory => node[:memcached][:memory],\n :port => node[:memcached][:port],\n :user => node[:memcached][:user]}.merge(params)\n )\nend\n```\n\nThis is where the user, port and memory options used in the run template are used.\n\n**Notifying Runit Services**\n\nIn previous versions of this cookbook where the definition was used, it created a `service` resource that could be notified. With the `runit_service` resource, recipes need to use the full resource name.\n\nFor example:\n\n```ruby\nrunit_service \"my-service\"\n\ntemplate \"/etc/my-service.conf\" do\n notifies :restart, \"runit_service[my-service]\"\nend\n```\n\nBecause the resource implements actions for various commands that `sv` can send to the service, any of those actions could be used for notification. For example, `chef-client` supports triggering a Chef run with a USR1 signal.\n\n```ruby\ntemplate \"/tmp/chef-notifier\" do\n notifies :usr1, \"runit_service[chef-client]\"\nend\n```\n\nFor older implementations of services that used `runit_service` as a definition, but may support alternate service styles, use a conditional, such as based on an attribute:\n\n```ruby\nservice_to_notify = case node['nginx']['init_style']\n when \"runit\"\n \"runit_service[nginx]\"\n else\n \"service[nginx]\"\n end\n\ntemplate \"/etc/nginx/nginx.conf\" do\n notifies :restart, service_to_notify\nend\n```\n\n**More Examples**\n\nFor more examples, see the `runit_test` cookbook's `service` recipe in the [git repository](https://github.com/opscode-cookbooks/runit).\n\n\nLicense & Authors\n-----------------\n- Author:: Adam Jacob \n- Author:: Joshua Timberman \n\n```text\nCopyright:: 2008-2013, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "ubuntu": ">= 0.0.0", + "debian": ">= 0.0.0", + "gentoo": ">= 0.0.0", + "centos": ">= 0.0.0", + "redhat": ">= 0.0.0", + "amazon": ">= 0.0.0", + "scientific": ">= 0.0.0", + "oracle": ">= 0.0.0", + "enterpriseenterprise": ">= 0.0.0" + }, + "dependencies": { + "build-essential": ">= 0.0.0", + "yum": "~> 3.0", + "yum-epel": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "runit": "Installs and configures runit" + } +} \ No newline at end of file diff --git a/chef/cookbooks/runit/metadata.rb b/chef/cookbooks/runit/metadata.rb index 79561ab..8917b04 100644 --- a/chef/cookbooks/runit/metadata.rb +++ b/chef/cookbooks/runit/metadata.rb @@ -1,16 +1,18 @@ -name "runit" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Installs runit and provides runit_service definition" +name 'runit' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Installs runit and provides runit_service definition' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "1.2.1" +version '1.5.10' -recipe "runit", "Installs and configures runit" +recipe 'runit', 'Installs and configures runit' %w{ ubuntu debian gentoo centos redhat amazon scientific oracle enterpriseenterprise }.each do |os| supports os end -depends "build-essential" -depends "yum" +depends 'build-essential' +depends 'yum', '~> 3.0' +depends 'yum-epel' + diff --git a/chef/cookbooks/runit/recipes/default.rb b/chef/cookbooks/runit/recipes/default.rb index 15cb0e2..3702cec 100644 --- a/chef/cookbooks/runit/recipes/default.rb +++ b/chef/cookbooks/runit/recipes/default.rb @@ -4,128 +4,119 @@ # # Copyright 2008-2010, Opscode, Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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. # -service "runit" do +service 'runit' do action :nothing end -execute "start-runsvdir" do +execute 'start-runsvdir' do command value_for_platform( - "debian" => { "default" => "runsvdir-start" }, - "ubuntu" => { "default" => "start runsvdir" }, - "gentoo" => { "default" => "/etc/init.d/runit-start start" } + 'debian' => { 'default' => 'runsvdir-start' }, + 'ubuntu' => { 'default' => 'start runsvdir' }, + 'gentoo' => { 'default' => '/etc/init.d/runit-start start' } ) action :nothing end -execute "runit-hup-init" do - command "telinit q" - only_if "grep ^SV /etc/inittab" +execute 'runit-hup-init' do + command 'telinit q' + only_if 'grep ^SV /etc/inittab' action :nothing end -case node["platform_family"] -when "rhel" +case node['platform_family'] +when 'rhel' if node['runit']['use_package_from_yum'] package 'runit' else - include_recipe "build-essential" + include_recipe 'build-essential' # `rpmdevtools` is in EPEL repo in EL <= 5 - include_recipe "yum::epel" if node["platform_version"].to_i <= 5 + include_recipe 'yum-epel' if node['platform_version'].to_i == 5 packages = %w{rpm-build rpmdevtools tar gzip} packages.each do |p| package p end - if node["platform_version"].to_i >= 6 - package "glibc-static" + if node['platform_version'].to_i >= 6 + package 'glibc-static' else - package "buildsys-macros" + package 'buildsys-macros' end rpm_installed = "rpm -qa | grep -q '^runit'" cookbook_file "#{Chef::Config[:file_cache_path]}/runit-2.1.1.tar.gz" do - source "runit-2.1.1.tar.gz" + source 'runit-2.1.1.tar.gz' not_if rpm_installed - notifies :run, "bash[rhel_build_install]", :immediately + notifies :run, 'bash[rhel_build_install]', :immediately end - bash "rhel_build_install" do - user "root" + bash 'rhel_build_install' do + user 'root' cwd Chef::Config[:file_cache_path] code <<-EOH tar xzf runit-2.1.1.tar.gz cd runit-2.1.1 ./build.sh + rpm_root_dir=`rpm --eval '%{_rpmdir}'` + rpm -ivh "${rpm_root_dir}/runit-2.1.1.rpm" EOH - notifies :install, "rpm_package[runit-211]", :immediately action :run not_if rpm_installed end - - rpm_root_dir = `rpm --eval "%{_rpmdir}"` - rpm_package "runit-211" do - source rpm_root_dir.strip + "/runit-2.1.1.rpm" - action :nothing - end end -when "debian","gentoo" +when 'debian', 'gentoo' - if platform?("gentoo") - template "/etc/init.d/runit-start" do - source "runit-start.sh.erb" + if platform?('gentoo') + template '/etc/init.d/runit-start' do + source 'runit-start.sh.erb' mode 0755 end - service "runit-start" do + service 'runit-start' do action :nothing end end - package "runit" do + package 'runit' do action :install - if platform?("ubuntu", "debian") - response_file "runit.seed" - end + response_file 'runit.seed' if platform?('ubuntu', 'debian') notifies value_for_platform( - "debian" => { "4.0" => :run, "default" => :nothing }, - "ubuntu" => { - "default" => :nothing, - "9.04" => :run, - "8.10" => :run, - "8.04" => :run }, - "gentoo" => { "default" => :run } - ), "execute[start-runsvdir]", :immediately + 'debian' => { '4.0' => :run, 'default' => :nothing }, + 'ubuntu' => { + 'default' => :nothing, + '9.04' => :run, + '8.10' => :run, + '8.04' => :run }, + 'gentoo' => { 'default' => :run } + ), 'execute[start-runsvdir]', :immediately notifies value_for_platform( - "debian" => { "squeeze/sid" => :run, "default" => :nothing }, - "default" => :nothing - ), "execute[runit-hup-init]", :immediately - if platform?("gentoo") - notifies :enable, "service[runit-start]" - end + 'debian' => { 'squeeze/sid' => :run, 'default' => :nothing }, + 'default' => :nothing + ), 'execute[runit-hup-init]', :immediately + notifies :enable, 'service[runit-start]' if platform?('gentoo') end - if node["platform"] =~ /ubuntu/i && node["platform_version"].to_f <= 8.04 - cookbook_file "/etc/event.d/runsvdir" do - source "runsvdir" + if node['platform'] =~ /ubuntu/i && node['platform_version'].to_f <= 8.04 + cookbook_file '/etc/event.d/runsvdir' do + source 'runsvdir' mode 0644 - notifies :run, "execute[start-runsvdir]", :immediately - only_if do ::File.directory?("/etc/event.d") end + notifies :run, 'execute[start-runsvdir]', :immediately + only_if { ::File.directory?('/etc/event.d') } end end end diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/README.md b/chef/cookbooks/runit/test/cookbooks/runit-other_test/README.md deleted file mode 100644 index 9dd93b5..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit-other_test/README.md +++ /dev/null @@ -1 +0,0 @@ -This cookbook is used with test-kitchen to test the parent, runit cookbok diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/metadata.rb b/chef/cookbooks/runit/test/cookbooks/runit-other_test/metadata.rb deleted file mode 100644 index 08bb2da..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit-other_test/metadata.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "runit-other_test" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "This cookbook is used with test-kitchen to test the parent, runit cookbok" -version "1.0.0" diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/recipes/default.rb b/chef/cookbooks/runit/test/cookbooks/runit-other_test/recipes/default.rb deleted file mode 100644 index 0f3b141..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit-other_test/recipes/default.rb +++ /dev/null @@ -1 +0,0 @@ -# Empty recipe for test-kitchen diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-run.erb b/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-run.erb deleted file mode 100644 index 4d93a15..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>1 -exec tail -f /var/log/* diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/README.md b/chef/cookbooks/runit/test/cookbooks/runit_test/README.md deleted file mode 100644 index 9dd93b5..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/README.md +++ /dev/null @@ -1 +0,0 @@ -This cookbook is used with test-kitchen to test the parent, runit cookbok diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/default_test.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/default_test.rb deleted file mode 100644 index 30f079b..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/default_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -# -# Cookbook Name:: runit_test -# Recipe:: default -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe "runit_test::default" do - include Helpers::RunitTest - - describe "packages" do - it 'has been installed' do - package("runit").must_be_installed - end - end -end diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/service_test.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/service_test.rb deleted file mode 100644 index c2ed599..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/service_test.rb +++ /dev/null @@ -1,118 +0,0 @@ -# -# Cookbook:: runit_test -# Minitest:: service -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe "runit_test::service" do - include Helpers::RunitTest - - it 'creates a service with the defaults' do - service('plain-defaults').must_be_running - file('/etc/service/plain-defaults/run').must_exist - file('/etc/service/plain-defaults/log/run').must_exist - file('/etc/init.d/plain-defaults').must_exist - unless node['platform'] == 'gentoo' - link('/etc/service/plain-defaults').must_exist.with( - :link_type, :symbolic).and(:to, '/etc/sv/plain-defaults') - end - end - - it 'creates a service that doesnt use the svlog' do - service('no-svlog').must_be_running - directory('/etc/sv/no-svlog/log').wont_exist - end - - it 'creates a service that uses the default svlog' do - regexp = %r{#!/bin/sh\nexec svlogd -tt /var/log/default-svlog} - service('default-svlog').must_be_running - file('/etc/service/default-svlog/log/run').must_match(regexp) - end - - it 'creates a service that has a finish script' do - service('finisher').must_be_running - file('/etc/service/finisher/finish').must_exist - end - - it 'creates a service that uses env files' do - regexp = %r{\$PATH:/opt/chef/embedded/bin} - service('env-files').must_be_running - file('/etc/service/env-files/env/PATH').must_match(regexp) - end - - it 'creates a service that sets options for the templates' do - service('template-options').must_be_running - file('/etc/service/template-options/run').must_match("# Options are delicious") - end - - it 'creates a service that uses control signal files' do - service('control-signals').must_be_running - file('/etc/service/control-signals/control/u').must_match(/control signal up/) - end - - it 'creates a runsvdir service for a normal user' do - regexp = %r{exec chpst -ufloyd runsvdir /home/floyd/service} - service('runsvdir-floyd').must_be_running - file('/etc/service/runsvdir-floyd/run').must_match(regexp) - end - - it 'creates a service running by a normal user in its runsvdir' do - floyds_app = shell_out( - "#{node['runit']['sv_bin']} status /home/floyd/service/floyds-app", - :user => "floyd", - :cwd => "/home/floyd" - ) - assert floyds_app.stdout.include?('run:') - file('/home/floyd/service/floyds-app/run').must_exist.with(:owner, 'floyd') - file('/home/floyd/service/floyds-app/log/run').must_exist.with(:owner, 'floyd') - file('/etc/init.d/floyds-app').must_exist - unless node['platform'] == 'gentoo' - link('/home/floyd/service/floyds-app').must_exist.with( - :link_type, :symbolic).and(:to, '/home/floyd/sv/floyds-app') - end - end - - it 'creates a service with differently named template files' do - service('yerba').must_be_running - end - - it 'creates a service with differently named run script template' do - service('yerba-alt').must_be_running - end - - it 'creates a service that should exist but be disabled' do - file('/etc/sv/exist-disabled/run').must_exist - unless node['platform'] == 'gentoo' - link('/etc/service/exist-disabled').wont_exist - end - end - - it 'can use templates from another cookbook' do - service('other-cookbook-templates').must_be_running - end - - it 'creates a service that has its own run scripts' do - if node['platform_family'] == 'rhel' - skip "RHEL platforms don't have a generally available package w/ runit scripts" - end - git_daemon = shell_out("#{node['runit']['sv_bin']} status /etc/service/git-daemon") - assert git_daemon.stdout.include?('run:') - link('/etc/service/git-daemon').must_exist.with( - :link_type, :symbolic).and(:to, '/etc/sv/git-daemon') - end -end diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/support/helpers.rb deleted file mode 100644 index 148315e..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/support/helpers.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -# Cookbook Name:: runit_test -# Recipe:: default -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -require 'chef/mixin/shell_out' - -module Helpers - module RunitTest - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - include Chef::Mixin::ShellOut - end -end diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/metadata.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/metadata.rb deleted file mode 100644 index 07bc0b3..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/metadata.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "runit_test" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "This cookbook is used with test-kitchen to test the parent, runit cookbok" -version "1.0.0" diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/default.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/default.rb deleted file mode 100644 index 2e01c15..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/default.rb +++ /dev/null @@ -1,20 +0,0 @@ -# -# Cookbook Name:: runit_test -# Recipe:: default -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "runit::default" diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/service.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/service.rb deleted file mode 100644 index f3b9ec8..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/service.rb +++ /dev/null @@ -1,153 +0,0 @@ -# -# Cookbook Name:: runit_test -# Recipe:: service -# -# Copyright 2012, Opscode, Inc. -# -# 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. -# - -include_recipe "runit::default" - -package "netcat" do - package_name "nc" if platform_family?('rhel', 'fedora') -end - -# Create a normal user to run services later -group "floyd" - -user "floyd" do - comment "Floyd the App Runner" - gid "floyd" - shell "/bin/bash" - home "/home/floyd" - manage_home true - supports :manage_home => true -end - -["sv", "service"].each do |dir| - - directory "/home/floyd/#{dir}" do - owner "floyd" - group "floyd" - recursive true - end - -end - -# Create a service with all the fixin's -runit_service "plain-defaults" - -# Create a service that doesn't use the svlog -runit_service "no-svlog" do - log false -end - -# Create a service that uses the default svlog -runit_service "default-svlog" do - default_logger true -end - -# Create a service that has a finish script -runit_service "finisher" do - finish true -end - -# Create a service that uses env files -runit_service "env-files" do - env({"PATH" => "$PATH:/opt/chef/embedded/bin"}) -end - -# Create a service that sets options for the templates -runit_service "template-options" do - options({:raspberry => "delicious"}) -end - -# Create a service that uses control signal files -runit_service "control-signals" do - control ["u"] -end - -# Create a runsvdir service for a normal user -runit_service "runsvdir-floyd" - -# # Create a service running by a normal user in its runsvdir -runit_service "floyds-app" do - sv_dir "/home/floyd/sv" - service_dir "/home/floyd/service" - owner "floyd" - group "floyd" -end - -# Create a service with differently named template files -runit_service "yerba" do - log_template_name "yerba-matte" - finish_script_template_name "yerba-matte" -end - -runit_service "yerba-alt" do - run_template_name "calabash" - default_logger true -end - -# Note: this won't update the run script for the above due to -# http://tickets.opscode.com/browse/COOK-2353 -# runit_service "the other name for yerba-alt" do -# service_name "yerba-alt" -# default_logger true -# end - -# Create a service that should exist but be disabled -runit_service "exist-disabled" - -log "Created the exist-disabled service, now disable it" - -runit_service "exist-disabled" do - action :disable -end - -runit_service "other-cookbook-templates" do - cookbook "runit-other_test" -end - -unless platform_family?("rhel") - # Create a service that has a package with its own service directory - package "git-daemon-run" - - runit_service "git-daemon" do - sv_templates false - end -end - -# Despite waiting for runit to create supervise/ok, sometimes services -# are supervised, but not actually fully started -ruby_block "sleep 5s to allow services to be fully started" do - block do - sleep 5 - end -end - -# Notify the plain defaults service as a normal service resource -file "/tmp/notifier" do - content Time.now.to_s - notifies :restart, 'service[plain-defaults]', :immediately -end - -# Test for COOK-2867 -link "/etc/init.d/cook-2867" do - to "/usr/bin/sv" -end - -runit_service "cook-2867" do - default_logger true -end diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-calabash-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-calabash-run.erb deleted file mode 100644 index 09d47e9..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-calabash-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6712 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-run.erb deleted file mode 100644 index cfc0908..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6700 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-u.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-u.erb deleted file mode 100644 index 4427fdf..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-u.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec echo "control signal up" >> /tmp/control-signals-up.out diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-cook-2867-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-cook-2867-run.erb deleted file mode 100644 index a80636a..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-cook-2867-run.erb +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -exec 2>&1 -# open port for the ticket #, clever eh? -exec nc -l 2867 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-default-svlog-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-default-svlog-run.erb deleted file mode 100644 index edd2dbc..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-default-svlog-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6701 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-run.erb deleted file mode 100644 index 3e005ae..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6702 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-run.erb deleted file mode 100644 index 110fa44..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6703 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-run.erb deleted file mode 100644 index 67a5669..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6704 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-finish.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-finish.erb deleted file mode 100644 index 8f6b1d6..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-finish.erb +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -code=$1 -status=$2 - -if [ $status -ne 0 ];then - echo "Finisher failed with ${code} on <%= node['fqdn'] %>" >> /tmp/finisher -else - echo "Finisher succeeded on <%= node['fqdn'] %>" -fi diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-run.erb deleted file mode 100644 index 1eb9417..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6705 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-run.erb deleted file mode 100644 index 068bb5d..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6706 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-no-svlog-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-no-svlog-run.erb deleted file mode 100644 index b642ce5..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-no-svlog-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6707 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-run.erb deleted file mode 100644 index 3b00c22..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6708 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-run.erb deleted file mode 100644 index 1a25d1d..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec chpst -ufloyd runsvdir /home/floyd/service diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-run.erb deleted file mode 100644 index 141f8b1..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-run.erb +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -# Options are <%= @options[:raspberry] %> -exec 2>&1 -exec nc -l 6710 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-finish.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-finish.erb deleted file mode 100644 index 4c4c0e1..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-finish.erb +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -code=$1 -status=$2 - -if [ $status -ne 0 ];then - echo "Yerba failed with ${code} on <%= node['fqdn'] %>" >> /tmp/yerba -else - echo "Yerba succeeded on <%= node['fqdn'] %>" -fi diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-log-run.erb deleted file mode 100644 index a79a518..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-log-run.erb +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-run.erb deleted file mode 100644 index 875726c..0000000 --- a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-run.erb +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec 2>&1 -exec nc -l 6711 diff --git a/chef/cookbooks/runit/test/spec/libraries/provider_runit_service_spec.rb b/chef/cookbooks/runit/test/spec/libraries/provider_runit_service_spec.rb deleted file mode 100644 index e653224..0000000 --- a/chef/cookbooks/runit/test/spec/libraries/provider_runit_service_spec.rb +++ /dev/null @@ -1,523 +0,0 @@ -# -# Author:: Joshua Timberman -# Author:: Seth Chisamore -# -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# -# 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. -# - -$:.unshift(File.join(File.dirname(__FILE__), '..')) -require 'spec_helper' - -describe Chef::Provider::Service::Runit do - - subject(:provider) { Chef::Provider::Service::Runit.new(new_resource, run_context) } - - let(:sv_bin) { "/usr/bin/sv" } - let(:service_name) { "getty.service" } - let(:service_dir) { "/etc/service" } - let(:service_dir_name) { "#{service_dir}/#{service_name}" } - let(:service_status_command) { "#{sv_bin} status #{service_name}" } - let(:run_script) { File.join(service_dir, service_name, "run") } - let(:log_run_script) { File.join(service_dir, service_name, "log", "run") } - let(:log_config_file) { File.join(service_dir, service_name, "log", "config") } - let(:node) do - node = Chef::Node.new - node.automatic['platform'] = 'ubuntu' - node.automatic['platform_version'] = '12.04' - node.set['runit']['sv_bin'] = sv_bin - node - end - let(:events) { Chef::EventDispatch::Dispatcher.new } - let(:run_context) { Chef::RunContext.new(node, {}, events) } - - let(:new_resource) { Chef::Resource::RunitService.new('getty.service') } - let(:current_resource) { Chef::Resource::RunitService.new('getty.service') } - - before do - provider.stub(:load_current_resource).and_return(current_resource) - provider.new_resource = new_resource - provider.current_resource = current_resource - end - - describe "#load_current_resource" do - - before do - provider.unstub(:load_current_resource) - end - - describe "runit is not installed" do - it "raises an exception" do - lambda { provider.load_current_resource }.should raise_error - end - end - - context "runit is installed" do - - let(:status_output) { "run: #{service_name}: (pid 29018) 3s; run: log: (pid 24470) 46882s" } - - before do - File.stub(:exist?).with(sv_bin).and_return(true) - File.stub(:executable?).with(sv_bin).and_return(true) - provider.stub(:shell_out). - with(service_status_command). - and_return(mock("ouput", :stdout => status_output, :exitstatus => 0)) - provider.load_current_resource - end - - describe "parsing sv status output" do - - context "returns a pid" do - let(:status_output) { "run: #{service_name}: (pid 29018) 3s; run: log: (pid 24470) 46882s" } - - it "sets resource running state to true" do - provider.current_resource.running.should be_true - end - end - - context "returns an empty pid" do - let(:status_output) { "down: #{service_name}: 2s, normally up; run: log: (pid 24470) 46250s" } - - it "sets resource running state to false" do - provider.current_resource.running.should be_false - end - end - end - - describe "checking for service run script" do - context "service run script is present in service_dir" do - before do - File.stub(:exists?).with(run_script).and_return(true) - provider.load_current_resource - end - - it "sets resource enabled state to true" do - provider.current_resource.enabled.should be_true - end - end - - context "service run script is missing" do - before do - File.stub(:exists?).with(run_script).and_return(false) - provider.load_current_resource - end - - it "sets resource enabled state to false" do - provider.current_resource.enabled.should be_false - end - end - end - end - end - - describe "actions" do - describe "start" do - - before do - provider.current_resource.running(false) - end - - %w{start up once cont}.each do |action| - it "sends the #{action} command to the sv binary" do - provider.should_receive(:shell_out!).with("#{sv_bin} #{action} #{service_dir_name}") - provider.run_action(action.to_sym) - end - end - end - - describe 'action_usr1' do - it 'sends the usr1 signal to the sv binary' do - provider.should_receive(:shell_out!).with("#{sv_bin} 1 #{service_dir_name}") - provider.run_action(:usr1) - end - end - - describe 'action_usr2' do - it 'sends the usr2 signal to the sv binary' do - provider.should_receive(:shell_out!).with("#{sv_bin} 2 #{service_dir_name}") - provider.run_action(:usr2) - end - end - - describe 'actions that manage a running service' do - before do - provider.current_resource.running(true) - end - - %w{stop down restart hup int term kill quit}.each do |action| - it "sends the '#{action}' command to the sv binary" do - provider.should_receive(:shell_out!).with("#{sv_bin} #{action} #{service_dir_name}") - provider.run_action(action.to_sym) - end - end - - describe 'action_reload' do - it "sends the 'force-reload' command to the sv binary" do - provider.should_receive(:shell_out!).with("#{sv_bin} force-reload #{service_dir_name}") - provider.run_action(:reload) - end - end - end - - describe 'action_disable' do - before do - provider.current_resource.enabled(true) - end - - it 'disables the service by running the down command and removing the symlink' do - provider.should_receive(:shell_out).with("#{sv_bin} down #{service_dir_name}") - FileUtils.should_receive(:rm).with(service_dir_name) - provider.run_action(:disable) - end - end - - describe "action_enable" do - let(:sv_dir_name) { ::File.join(new_resource.sv_dir, new_resource.service_name) } - - before(:each) do - provider.current_resource.enabled(false) - FileTest.stub(:pipe?).with("#{service_dir_name}/supervise/ok").and_return(true) - FileTest.stub(:pipe?).with("#{service_dir_name}/log/supervise/ok").and_return(true) - end - - it 'creates the sv_dir directory' do - provider.send(:sv_dir).path.should eq(sv_dir_name) - provider.send(:sv_dir).recursive.should be_true - provider.send(:sv_dir).owner.should eq(new_resource.owner) - provider.send(:sv_dir).group.should eq(new_resource.group) - provider.send(:sv_dir).mode.should eq(00755) - end - - it 'creates the run script template' do - provider.send(:run_script).path.should eq(::File.join(sv_dir_name, 'run')) - provider.send(:run_script).owner.should eq(new_resource.owner) - provider.send(:run_script).group.should eq(new_resource.group) - provider.send(:run_script).mode.should eq(00755) - provider.send(:run_script).source.should eq("sv-#{new_resource.service_name}-run.erb") - provider.send(:run_script).cookbook.should be_empty - end - - it 'sets up the supervised log directory and run script' do - provider.send(:log_dir).path.should eq(::File.join(sv_dir_name, 'log')) - provider.send(:log_dir).recursive.should be_true - provider.send(:log_dir).owner.should eq(new_resource.owner) - provider.send(:log_dir).group.should eq(new_resource.group) - provider.send(:log_dir).mode.should eq(00755) - provider.send(:log_main_dir).path.should eq(::File.join(sv_dir_name, 'log', 'main')) - provider.send(:log_main_dir).recursive.should be_true - provider.send(:log_main_dir).owner.should eq(new_resource.owner) - provider.send(:log_main_dir).group.should eq(new_resource.group) - provider.send(:log_main_dir).mode.should eq(00755) - provider.send(:log_run_script).path.should eq(::File.join(sv_dir_name, 'log', 'run')) - provider.send(:log_run_script).owner.should eq(new_resource.owner) - provider.send(:log_run_script).group.should eq(new_resource.group) - provider.send(:log_run_script).mode.should eq(00755) - provider.send(:log_run_script).source.should eq("sv-#{new_resource.log_template_name}-log-run.erb") - provider.send(:log_run_script).cookbook.should be_empty - provider.send(:log_config_file).path.should eq(::File.join(sv_dir_name, 'log', 'config')) - provider.send(:log_config_file).owner.should eq(new_resource.owner) - provider.send(:log_config_file).group.should eq(new_resource.group) - provider.send(:log_config_file).mode.should eq(00644) - provider.send(:log_config_file).source.should eq('log-config.erb') - provider.send(:log_config_file).cookbook.should eq('runit') - end - - it 'creates log/run with default content if default_logger parameter is true' do - script_content = "exec svlogd -tt /var/log/#{new_resource.service_name}" - new_resource.default_logger(true) - provider.send(:log_run_script).path.should eq(::File.join(sv_dir_name, 'log', 'run')) - provider.send(:log_run_script).owner.should eq(new_resource.owner) - provider.send(:log_run_script).group.should eq(new_resource.group) - provider.send(:log_run_script).mode.should eq(00755) - provider.send(:log_run_script).content.should include(script_content) - provider.send(:default_log_dir).path.should eq(::File.join('/var', 'log', new_resource.service_name)) - provider.send(:default_log_dir).recursive.should be_true - provider.send(:default_log_dir).owner.should eq(new_resource.owner) - provider.send(:default_log_dir).group.should eq(new_resource.group) - provider.send(:default_log_dir).mode.should eq(00755) - end - - it 'creates env directory and files' do - provider.send(:env_dir).path.should eq(::File.join(sv_dir_name, 'env')) - provider.send(:env_dir).owner.should eq(new_resource.owner) - provider.send(:env_dir).group.should eq(new_resource.group) - provider.send(:env_dir).mode.should eq(00755) - new_resource.env({'PATH' => '$PATH:/usr/local/bin'}) - provider.send(:env_files)[0].path.should eq(::File.join(sv_dir_name, 'env', 'PATH')) - provider.send(:env_files)[0].owner.should eq(new_resource.owner) - provider.send(:env_files)[0].group.should eq(new_resource.group) - provider.send(:env_files)[0].content.should eq('$PATH:/usr/local/bin') - end - - it 'creates a finish script as a template if finish_script parameter is true' do - provider.send(:finish_script).path.should eq(::File.join(sv_dir_name, 'finish')) - provider.send(:finish_script).owner.should eq(new_resource.owner) - provider.send(:finish_script).group.should eq(new_resource.group) - provider.send(:finish_script).mode.should eq(00755) - provider.send(:finish_script).source.should eq("sv-#{new_resource.finish_script_template_name}-finish.erb") - provider.send(:finish_script).cookbook.should be_empty - end - - it 'creates control directory and signal files' do - provider.send(:control_dir).path.should eq(::File.join(sv_dir_name, 'control')) - provider.send(:control_dir).owner.should eq(new_resource.owner) - provider.send(:control_dir).group.should eq(new_resource.group) - provider.send(:control_dir).mode.should eq(00755) - new_resource.control(['s']) - provider.send(:control_signal_files)[0].path.should eq(::File.join(sv_dir_name, 'control', 's')) - provider.send(:control_signal_files)[0].owner.should eq(new_resource.owner) - provider.send(:control_signal_files)[0].group.should eq(new_resource.group) - provider.send(:control_signal_files)[0].mode.should eq(00755) - provider.send(:control_signal_files)[0].source.should eq("sv-#{new_resource.control_template_names['s']}-s.erb") - provider.send(:control_signal_files)[0].cookbook.should be_empty - end - - it 'creates a symlink for LSB script compliance unless the platform is debian' do - node.automatic['platform'] = 'not_debian' - provider.send(:lsb_init).path.should eq(::File.join('/etc', 'init.d', new_resource.service_name)) - provider.send(:lsb_init).to.should eq(sv_bin) - end - - it 'creates an init script as a template for LSB compliance if the platform is debian' do - node.automatic['platform'] = 'debian' - provider.send(:lsb_init).path.should eq(::File.join('/etc', 'init.d', new_resource.service_name)) - provider.send(:lsb_init).owner.should eq('root') - provider.send(:lsb_init).group.should eq('root') - provider.send(:lsb_init).mode.should eq(00755) - provider.send(:lsb_init).cookbook.should eq('runit') - provider.send(:lsb_init).source.should eq('init.d.erb') - provider.send(:lsb_init).variables.should have_key(:name) - provider.send(:lsb_init).variables[:name].should eq(new_resource.service_name) - end - - it 'does not create anything in the sv_dir if it is nil or false' do - current_resource.stub(:enabled).and_return(false) - new_resource.stub(:sv_templates).and_return(false) - provider.should_not_receive(:sv_dir) - provider.send(:run_script).should_not_receive(:run_action).with(:create) - provider.send(:log_run_script).should_not_receive(:run_action).with(:create) - provider.should_not_receive(:log) - provider.should_not_receive(:log_main_dir) - provider.send(:lsb_init).should_receive(:run_action).with(:create) - provider.send(:service_link).should_receive(:run_action).with(:create) - provider.run_action(:enable) - end - - it 'creates a symlink from the sv dir to the service' do - provider.send(:service_link).path.should eq(service_dir_name) - provider.send(:service_link).to.should eq(sv_dir_name) - end - - it 'enables the service with memoized resource creation methods' do - current_resource.stub(:enabled).and_return(false) - provider.send(:sv_dir).should_receive(:run_action).with(:create) - provider.send(:run_script).should_receive(:run_action).with(:create) - provider.send(:log_dir).should_receive(:run_action).with(:create) - provider.send(:log_main_dir).should_receive(:run_action).with(:create) - provider.send(:log_run_script).should_receive(:run_action).with(:create) - provider.send(:log_config_file).should_receive(:run_action).with(:create) - provider.send(:lsb_init).should_receive(:run_action).with(:create) - provider.send(:service_link).should_receive(:run_action).with(:create) - provider.run_action(:enable) - end - - describe "run_script template changes" do - before do - provider.stub(:configure_service) - provider.stub(:enable_service) - end - - context "run_script is updated" do - before { provider.send(:run_script).stub(:updated_by_last_action?).and_return(true) } - - context "restart_on_update attribute is true" do - before { new_resource.restart_on_update(true) } - - it "restarts the service" do - provider.should_receive(:restart_service) - provider.run_action(:enable) - end - end - - context "restart_on_update attribute is false" do - before { new_resource.restart_on_update(false) } - - it "does not restart the service" do - provider.should_not_receive(:restart_service) - provider.run_action(:enable) - end - end - end - - context "run script is unchanged" do - before { provider.send(:run_script).stub(:updated_by_last_action?).and_return(false) } - - context "restart_on_update attribute is true" do - before { new_resource.restart_on_update(true) } - - it "does not restart the service" do - provider.should_not_receive(:restart_service) - provider.run_action(:enable) - end - end - - context "restart_on_update attribute is false" do - before { new_resource.restart_on_update(false) } - - it "does not restart the service" do - provider.should_not_receive(:restart_service) - provider.run_action(:enable) - end - end - end - end - - describe "log_run_script template changes" do - before do - provider.stub(:configure_service) - provider.stub(:enable_service) - end - - context "log_run_script is updated" do - before { provider.send(:log_run_script).stub(:updated_by_last_action?).and_return(true) } - - context "restart_on_update attribute is true" do - before { new_resource.restart_on_update(true) } - - it "restarts the service" do - provider.should_receive(:restart_log_service) - provider.run_action(:enable) - end - end - - context "restart_on_update attribute is false" do - before { new_resource.restart_on_update(false) } - - it "does not restart the service" do - provider.should_not_receive(:restart_log_service) - provider.run_action(:enable) - end - end - end - - context "log_run_script is unchanged" do - before { provider.send(:log_run_script).stub(:updated_by_last_action?).and_return(false) } - - context "restart_on_update attribute is true" do - before { new_resource.restart_on_update(true) } - - it "does not restart the service" do - provider.should_not_receive(:restart_log_service) - provider.run_action(:enable) - end - end - - context "restart_on_update attribute is false" do - before { new_resource.restart_on_update(false) } - - it "does not restart the service" do - provider.should_not_receive(:restart_log_service) - provider.run_action(:enable) - end - end - end - end - - describe "log_config_file template changes" do - before do - provider.stub(:configure_service) - provider.stub(:enable_service) - end - - context "log_config_file is updated" do - before { provider.send(:log_config_file).stub(:updated_by_last_action?).and_return(true) } - - context "restart_on_update attribute is true" do - before { new_resource.restart_on_update(true) } - - it "restarts the service" do - provider.should_receive(:restart_log_service) - provider.run_action(:enable) - end - end - - context "restart_on_update attribute is false" do - before { new_resource.restart_on_update(false) } - - it "does not restart the service" do - provider.should_not_receive(:restart_log_service) - provider.run_action(:enable) - end - end - end - - context "log_config_file is unchanged" do - before { provider.send(:log_config_file).stub(:updated_by_last_action?).and_return(false) } - - context "restart_on_update attribute is true" do - before { new_resource.restart_on_update(true) } - - it "does not restart the service" do - provider.should_not_receive(:restart_log_service) - provider.run_action(:enable) - end - end - - context "restart_on_update attribute is false" do - before { new_resource.restart_on_update(false) } - - it "does not restart the service" do - provider.should_not_receive(:restart_log_service) - provider.run_action(:enable) - end - end - end - end - - context 'new resource conditionals' do - before(:each) do - current_resource.stub(:enabled).and_return(false) - provider.send(:sv_dir).stub(:run_action).with(:create) - provider.send(:run_script).stub(:run_action).with(:create) - provider.send(:lsb_init).stub(:run_action).with(:create) - provider.send(:service_link).stub(:run_action).with(:create) - provider.send(:log_dir).stub(:run_action).with(:create) - provider.send(:log_main_dir).stub(:run_action).with(:create) - provider.send(:log_run_script).stub(:run_action).with(:create) - provider.send(:log_config_file).stub(:run_action).with(:create) - end - - it 'doesnt create the log dir or run script if log is false' do - new_resource.stub(:log).and_return(false) - provider.should_not_receive(:log) - provider.run_action(:enable) - end - - it 'creates the env dir and config files if env is set' do - new_resource.stub(:env).and_return({'PATH' => '/bin'}) - provider.send(:env_dir).should_receive(:run_action).with(:create) - provider.send(:env_files).should_receive(:each).once - provider.run_action(:enable) - end - - it 'creates the control dir and signal files if control is set' do - new_resource.stub(:control).and_return(['s', 'u']) - provider.send(:control_dir).should_receive(:run_action).with(:create) - provider.send(:control_signal_files).should_receive(:each).once - provider.run_action(:enable) - end - end - end - end -end diff --git a/chef/cookbooks/runit/test/spec/libraries/resource_runit_service_spec.rb b/chef/cookbooks/runit/test/spec/libraries/resource_runit_service_spec.rb deleted file mode 100644 index d13ade9..0000000 --- a/chef/cookbooks/runit/test/spec/libraries/resource_runit_service_spec.rb +++ /dev/null @@ -1,284 +0,0 @@ -# -# Author:: Joshua Timberman -# Author:: Seth Chisamore -# -# Copyright:: Copyright (c) 2012, Opscode, Inc. -# -# 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. -# - -$:.unshift(File.join(File.dirname(__FILE__), '..')) -require 'spec_helper' - -describe Chef::Resource::RunitService do - - subject(:resource) { Chef::Resource::RunitService.new(service_name, run_context) } - - let(:service_name) { 'getty.service' } - let(:node) { Chef::Node.new } - let(:events) { Chef::EventDispatch::Dispatcher.new } - let(:run_context) { Chef::RunContext.new(node, {}, events) } - - its(:class) { should be Chef::Resource::RunitService } - its(:resource_name) { should eq(:runit_service)} - its(:provider) { should eq(Chef::Provider::Service::Runit) } - its(:service_name) { should eq('getty.service') } - its(:sv_dir) { should eq('/etc/sv') } - its(:sv_bin) { should eq("/usr/bin/sv") } - its(:lsb_init_dir) { should eq("/etc/init.d") } - - describe "setting supported default values from node attributes" do - let(:sv_bin) { "/fake/bin/sv_bin" } - let(:sv_dir) { "/fake/sv_dir/path" } - let(:service_dir) { "/fake/service_dir" } - let(:lsb_init_dir) { "/fake/lsb_init_dir" } - let(:node) do - node = Chef::Node.new - node.set['runit']['sv_bin'] = sv_bin - node.set['runit']['sv_dir'] = sv_dir - node.set['runit']['service_dir'] = service_dir - node.set['runit']['lsb_init_dir'] = lsb_init_dir - node - end - - its(:sv_bin) { should eq sv_bin } - its(:sv_dir) { should eq sv_dir } - its(:service_dir) { should eq service_dir } - its(:lsb_init_dir) { should eq lsb_init_dir } - end - - describe "backward compatiblility hack" do - - let(:simple_service_name) { "service[#{service_name}]" } - - it "creates a simple service with the same name" do - resource_collection = resource.run_context.resource_collection - simple_service = resource_collection.find(simple_service_name) - simple_service.to_s.should eq(simple_service_name) - simple_service.class.should be Chef::Resource::Service - simple_service.provider.should be Chef::Provider::Service::Simple - end - - end - - it 'has an sv_dir parameter that can be set' do - resource.sv_dir('/var/lib/sv') - resource.sv_dir.should eq('/var/lib/sv') - end - - it 'allows sv_dir parameter to be set false (so users can use an existing sv dir)' do - resource.sv_dir(false) - resource.sv_dir.should be_false - end - - it 'has a service_dir parameter set to /etc/service by default' do - resource.service_dir.should eq('/etc/service') - end - - it 'has a service_dir parameter that can be set' do - resource.service_dir('/var/service') - resource.service_dir.should eq('/var/service') - end - - it 'has a lsb_init_dir parameter set to /etc/init.d by default' do - resource.lsb_init_dir.should eq('/etc/init.d') - end - - it 'has a lsb_init_dir parameter that can be set' do - resource.lsb_init_dir('/other/lsb_init_dir') - resource.lsb_init_dir.should eq('/other/lsb_init_dir') - end - - it 'has a control parameter that can be set as an array of service control characters' do - resource.control(['s', 'u']) - resource.control.should eq(['s', 'u']) - end - - it 'has an options parameter that can be set as a hash of arbitrary options' do - resource.options({:binary => '/usr/bin/noodles'}) - resource.options.should have_key(:binary) - resource.options[:binary].should eq('/usr/bin/noodles') - end - - it 'has an env parameter that can be set as a hash of environment variables' do - resource.env({'PATH' => '$PATH:/usr/local/bin'}) - resource.env.should have_key('PATH') - resource.env['PATH'].should include('/usr/local/bin') - end - - it 'adds :env_dir to options if env is set' do - resource.env({'PATH' => '/bin'}) - resource.options.should have_key(:env_dir) - resource.options[:env_dir].should eq(::File.join(resource.sv_dir, resource.service_name, 'env')) - end - - it 'has a log parameter to control whether a log service is setup' do - resource.log.should be_true - end - - it 'has a log parameter that can be set to false' do - resource.log(false) - resource.log.should be_false - end - - it 'raises an exception if the log parameter is set to nil' do - resource.log(nil) - resource.log.should raise_exception - end - - it 'has a cookbook parameter that can be set' do - resource.cookbook('noodles') - resource.cookbook.should eq('noodles') - end - - it 'has a finish parameter that is false by default' do - resource.finish.should be_false - end - - it 'hash a finish parameter that controls whether a finish script is created' do - resource.finish(true) - resource.finish.should be_true - end - - it 'has an owner parameter that can be set' do - resource.owner('monkey') - resource.owner.should eq('monkey') - end - - it 'has a group parameter that can be set' do - resource.group('primates') - resource.group.should eq('primates') - end - - it 'has an enabled parameter to determine if the current resource is enabled' do - resource.enabled.should be_false - end - - it 'has a running parameter to determine if the current resource is running' do - resource.running.should be_false - end - - it 'has a default_logger parameter that is false by default' do - resource.default_logger.should be_false - end - - it 'has a default_logger parameter that controls whether a default log template should be created' do - resource.default_logger(true) - resource.default_logger.should be_true - end - - it 'has a restart_on_update parameter that is true by default' do - resource.restart_on_update.should be_true - end - - it 'has a restart_on_update parameter that controls whether a the service is restarted when the run script is updated' do - resource.restart_on_update(false) - resource.restart_on_update.should be_false - end - - it 'sets the run_template_name to the service_name by default' do - resource.run_template_name.should eq(resource.service_name) - end - - it 'sets the log_template_name to the service_name by default' do - resource.log_template_name.should eq(resource.service_name) - end - - it 'has a run_template_name parameter to allow a custom template name for the run run script' do - resource.run_template_name('foo_bar') - resource.run_template_name.should eq('foo_bar') - end - - it 'has a template_name parameter to allow a custom template name for the run run script for backwards compatiblility' do - resource.template_name('foo_baz') - resource.run_template_name.should eq('foo_baz') - end - - it 'has a log_template_name parameter to allow a custom template name for the log run script' do - resource.log_template_name('write_noodles') - resource.log_template_name.should eq('write_noodles') - end - - it 'sets the control_template_names for each control character to the service_name by default' do - resource.control(['s', 'u']) - resource.control_template_names.should have_key('s') - resource.control_template_names.should have_key('u') - resource.control_template_names['s'].should eq(resource.service_name) - resource.control_template_names['u'].should eq(resource.service_name) - end - - it 'has a control_template_names parameter to allow custom template names for the control scripts' do - resource.control_template_names({ - 's' => 'banana_start', - 'u' => 'noodle_up' - }) - resource.control_template_names.should have_key('s') - resource.control_template_names.should have_key('u') - resource.control_template_names['s'].should eq('banana_start') - resource.control_template_names['u'].should eq('noodle_up') - end - - it 'sets the finish_script_template_name to the service_name by default' do - resource.finish_script_template_name.should eq(resource.service_name) - end - - it 'has a finish_script_template_name parameter to allow a custom template name for the finish script' do - resource.finish_script_template_name('eat_bananas') - resource.finish_script_template_name.should eq('eat_bananas') - end - - it 'has a sv_templates parameter to control whether the sv_dir templates are created' do - resource.sv_templates(false) - resource.sv_templates.should be_false - end - - it "has a log_size parameter to control the maximum log size" do - resource.log_size(1000000) - resource.log_size.should eq(1000000) - end - - it "has a log_num parameter to control the maximum number of logs" do - resource.log_num(10) - resource.log_num.should eq(10) - end - - it "has a log_min parameter to control the minimum number of logs" do - resource.log_min(5) - resource.log_min.should eq(5) - end - - it "has a log_timeout parameter to control the maximum age of a log file" do - resource.log_timeout(60 * 60) - resource.log_timeout.should eq(60 * 60) - end - - it "has a log_processor parameter to allow logs to be fed through it after rotation" do - resource.log_processor("/usr/local/bin/process") - resource.log_processor.should eq("/usr/local/bin/process") - end - - it "has a log_socket parameter to allow log lines to be sent to a UDP socket" do - resource.log_socket("127.0.0.1:1514") - resource.log_socket.should eq("127.0.0.1:1514") - end - - it "has a log_prefix parameter to allow log lines to be prefixed with a fixed string" do - resource.log_prefix("myservice:") - resource.log_prefix.should eq("myservice:") - end - - it "has a log_config_append parameter to allow arbitrary configuration entries to be added to the configuration" do - resource.log_config_append("-bogus") - resource.log_config_append.should eq("-bogus") - end -end diff --git a/chef/cookbooks/runit/test/spec/spec_helper.rb b/chef/cookbooks/runit/test/spec/spec_helper.rb deleted file mode 100644 index 4efe8f9..0000000 --- a/chef/cookbooks/runit/test/spec/spec_helper.rb +++ /dev/null @@ -1,28 +0,0 @@ - -require 'chef/platform' -require 'chef/run_context' -require 'chef/resource' -require 'chef/resource/service' -require 'chef/provider/service/simple' -require 'chef/event_dispatch/base' -require 'chef/event_dispatch/dispatcher' - -$:.unshift(File.join(File.dirname(__FILE__), "..", "..", "libraries")) -require 'provider_runit_service' -require 'resource_runit_service' - -RSpec.configure do |config| - # Use color in STDOUT - config.color_enabled = true - - # Use color not only in STDOUT but also in pagers and files - config.tty = true - - # Use the specified formatter - config.formatter = :documentation # :progress, :html, :textmate - - # :focus support to allow zooming in a single test/block - config.filter_run :focus => true - config.run_all_when_everything_filtered = true - config.treat_symbols_as_metadata_keys_with_true_values = true -end diff --git a/chef/cookbooks/selinux/CHANGELOG.md b/chef/cookbooks/selinux/CHANGELOG.md index 292d83d..5d91d80 100644 --- a/chef/cookbooks/selinux/CHANGELOG.md +++ b/chef/cookbooks/selinux/CHANGELOG.md @@ -1,17 +1,41 @@ -## v0.5.6: +selinux Cookbook CHANGELOG +========================== -* [COOK-2124] - enforcing recipe fails if selinux is disabled +v0.7.2 (2014-03-24) +------------------- +handling minimal installs -## v0.5.4: -* [COOK-1277] - disabled recipe fails on systems w/o selinux installed +v0.7.0 (2014-02-27) +------------------- +[COOK-4218] Support setting SELinux boolean values -## v0.5.2: -* [COOK-789] - fix dangling commas causing syntax error on some rubies +v0.6.2 +------ +- Fixing bug introduced in 0.6.0 +- adding basic test-kitchen coverage -## v0.5.0: -* [COOK-678] - add the selinux cookbook to the repository -* Use main selinux config file (/etc/selinux/config) -* Use getenforce instead of selinuxenabled for enforcing and permissive +v0.6.0 +------ +- [COOK-760] - selinux enforce/permit/disable based on attribute + + +v0.5.6 +------ +- [COOK-2124] - enforcing recipe fails if selinux is disabled + +v0.5.4 +------ +- [COOK-1277] - disabled recipe fails on systems w/o selinux installed + +v0.5.2 +------ +- [COOK-789] - fix dangling commas causing syntax error on some rubies + +v0.5.0 +------ +- [COOK-678] - add the selinux cookbook to the repository +- Use main selinux config file (/etc/selinux/config) +- Use getenforce instead of selinuxenabled for enforcing and permissive diff --git a/chef/cookbooks/selinux/CONTRIBUTING b/chef/cookbooks/selinux/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/selinux/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/selinux/LICENSE b/chef/cookbooks/selinux/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/selinux/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/selinux/README.md b/chef/cookbooks/selinux/README.md index 2e05b7c..e26e74f 100644 --- a/chef/cookbooks/selinux/README.md +++ b/chef/cookbooks/selinux/README.md @@ -1,7 +1,7 @@ Description =========== -Provides recipes for manipulating selinux policy enforcement +Provides recipes for manipulating SELinux policy enforcement state. Requirements ============ @@ -10,12 +10,106 @@ RHEL family distribution or other Linux system that uses SELinux. ## Platform: -Tested on RHEL 5.6, 6.0 and 6.1. +Tested on RHEL 5.8, 6.3 + +Node Attributes +=============== + +* `node['selinux']['state']` - The SELinux policy enforcement state. + The state to set by default, to match the default SELinux state on + RHEL. Can be "enforcing", "permissive", "disabled" + +* `node['selinux']['booleans']` - A hash of SELinux boolean names and the + values they should be set to. Values can be off, false, or 0 to disable; + or on, true, or 1 to enable. + +Resources/Providers +=================== + +## selinux\_state + +The `selinux_state` LWRP is used to manage the SELinux state on the +system. It does this by using the `setenforce` command and rendering +the `/etc/selinux/config` file from a template. + +### Actions + +* `:nothing` - default action, does nothing +* `:enforcing` - Sets SELinux to enforcing. +* `:disabled` - Sets SELinux to disabled. +* `:permissive` - Sets SELinux to permissive. + +### Attributes + +The LWRP has no user-settable resource attributes. + +### Examples + +Simply set SELinux to enforcing or permissive: + + selinux_state "SELinux Enforcing" do + action :enforcing + end + + selinux_state "SELinux Permissive" do + action :permissive + end + +The action here is based on the value of the +`node['selinux']['state']` attribute, which we convert to lower-case +and make a symbol to pass to the action. + + selinux_state "SELinux #{node['selinux']['state'].capitalize}" do + action node['selinux']['state'].downcase.to_sym + end + +Recipes +======= + +All the recipes now leverage the LWRP described above. + +## default + +The default recipe will use the attribute `node['selinux']['state']` +in the `selinux_state` LWRP's action. By default, this will be `:enforcing`. + +## enforcing + +This recipe will use `:enforcing` as the `selinux_state` action. + +## permissive + +This recipe will use `:permissive` as the `selinux_state` action. + +## disabled + +This recipe will use `:disabled` as the `selinux_state` action. Usage ===== -SELinux is enforcing by default on RHEL family distributions, however the use of SELinux has complicated considerations when using configuration management. Often, users are recommended to set SELinux to permissive mode, or disabled completely. To ensure that SELinux is permissive or disabled, choose the appropriate recipe (`selinux::permissive`, `selinux::disabled`) and apply it to the node early in the run list. For example in a `base` role used by all RHEL systems: +By default, this cookbook will have SELinux enforcing by default, as +the default recipe uses the `node['selinux']['state']` attribute, +which is "enforcing." This is in line with the policy of enforcing by +default on RHEL family distributions. + +This has complicated considerations when changing the default +configuration of their systems, whether it is with automated +configuration management or manually. Often, third party help forums +and support sites recommend setting SELinux to "permissive." This +cookbook can help with that, in two ways. + +You can simply set the attribute in a role applied to the node: + + name "base" + description "Base role applied to all nodes." + default_attributes( + "selinux" => { + "state" => "permissive" + } + ) + +Or, you can apply the recipe to the run list (e.g., in a role): name "base" description "Base role applied to all nodes." @@ -26,17 +120,16 @@ SELinux is enforcing by default on RHEL family distributions, however the use of Roadmap ======= -Use a node attribute to determine which recipe to load automatically from selinux::default. - -Add LWRP/Libraries for manipulating security contexts for files and services managed by Chef. +Add LWRP/Libraries for manipulating security contexts for files and +services managed by Chef. License and Author ================== -Author:: Sean OMeara () -Author:: Joshua Timberman () +- Author:: Sean OMeara () +- Author:: Joshua Timberman () -Copyright:: 2011, Opscode, Inc +Copyright:: 2011-2012, Opscode, Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/selinux/attributes/default.rb b/chef/cookbooks/selinux/attributes/default.rb new file mode 100644 index 0000000..68d07f3 --- /dev/null +++ b/chef/cookbooks/selinux/attributes/default.rb @@ -0,0 +1,2 @@ +default['selinux']['state'] = 'enforcing' +default['selinux']['booleans'] = {} diff --git a/chef/cookbooks/selinux/libraries/selinux_service_helpers.rb b/chef/cookbooks/selinux/libraries/selinux_service_helpers.rb new file mode 100644 index 0000000..98fd53a --- /dev/null +++ b/chef/cookbooks/selinux/libraries/selinux_service_helpers.rb @@ -0,0 +1,13 @@ +module SELinuxServiceHelpers + def self.selinux_bool(bool) + if ['on', 'true', '1'].include? bool + 'on' + elsif ['off', 'false', '0'].include? bool + 'off' + else + Chef::Log.warn "Not a valid boolean value: #{bool}" + nil + end + end +end + \ No newline at end of file diff --git a/chef/cookbooks/selinux/metadata.json b/chef/cookbooks/selinux/metadata.json new file mode 100644 index 0000000..0f3a05f --- /dev/null +++ b/chef/cookbooks/selinux/metadata.json @@ -0,0 +1,52 @@ +{ + "name": "selinux", + "version": "0.7.2", + "description": "Manages SELinux policy state via LWRP or recipes.", + "long_description": "Description\n===========\n\nProvides recipes for manipulating SELinux policy enforcement state.\n\nRequirements\n============\n\nRHEL family distribution or other Linux system that uses SELinux.\n\n## Platform:\n\nTested on RHEL 5.8, 6.3\n\nNode Attributes\n===============\n\n* `node['selinux']['state']` - The SELinux policy enforcement state.\n The state to set by default, to match the default SELinux state on\n RHEL. Can be \"enforcing\", \"permissive\", \"disabled\"\n\n* `node['selinux']['booleans']` - A hash of SELinux boolean names and the\n values they should be set to. Values can be off, false, or 0 to disable;\n or on, true, or 1 to enable.\n\nResources/Providers\n===================\n\n## selinux\\_state\n\nThe `selinux_state` LWRP is used to manage the SELinux state on the\nsystem. It does this by using the `setenforce` command and rendering\nthe `/etc/selinux/config` file from a template.\n\n### Actions\n\n* `:nothing` - default action, does nothing\n* `:enforcing` - Sets SELinux to enforcing.\n* `:disabled` - Sets SELinux to disabled.\n* `:permissive` - Sets SELinux to permissive.\n\n### Attributes\n\nThe LWRP has no user-settable resource attributes.\n\n### Examples\n\nSimply set SELinux to enforcing or permissive:\n\n selinux_state \"SELinux Enforcing\" do\n action :enforcing\n end\n\n selinux_state \"SELinux Permissive\" do\n action :permissive\n end\n\nThe action here is based on the value of the\n`node['selinux']['state']` attribute, which we convert to lower-case\nand make a symbol to pass to the action.\n\n selinux_state \"SELinux #{node['selinux']['state'].capitalize}\" do\n action node['selinux']['state'].downcase.to_sym\n end\n\nRecipes\n=======\n\nAll the recipes now leverage the LWRP described above.\n\n## default\n\nThe default recipe will use the attribute `node['selinux']['state']`\nin the `selinux_state` LWRP's action. By default, this will be `:enforcing`.\n\n## enforcing\n\nThis recipe will use `:enforcing` as the `selinux_state` action.\n\n## permissive\n\nThis recipe will use `:permissive` as the `selinux_state` action.\n\n## disabled\n\nThis recipe will use `:disabled` as the `selinux_state` action.\n\nUsage\n=====\n\nBy default, this cookbook will have SELinux enforcing by default, as\nthe default recipe uses the `node['selinux']['state']` attribute,\nwhich is \"enforcing.\" This is in line with the policy of enforcing by\ndefault on RHEL family distributions.\n\nThis has complicated considerations when changing the default\nconfiguration of their systems, whether it is with automated\nconfiguration management or manually. Often, third party help forums\nand support sites recommend setting SELinux to \"permissive.\" This\ncookbook can help with that, in two ways.\n\nYou can simply set the attribute in a role applied to the node:\n\n name \"base\"\n description \"Base role applied to all nodes.\"\n default_attributes(\n \"selinux\" => {\n \"state\" => \"permissive\"\n }\n )\n\nOr, you can apply the recipe to the run list (e.g., in a role):\n\n name \"base\"\n description \"Base role applied to all nodes.\"\n run_list(\n \"recipe[selinux::permissive]\",\n )\n\nRoadmap\n=======\n\nAdd LWRP/Libraries for manipulating security contexts for files and\nservices managed by Chef.\n\nLicense and Author\n==================\n\n- Author:: Sean OMeara ()\n- Author:: Joshua Timberman ()\n\nCopyright:: 2011-2012, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache", + "platforms": { + "redhat": ">= 0.0.0", + "centos": ">= 0.0.0", + "scientific": ">= 0.0.0", + "oracle": ">= 0.0.0", + "amazon": ">= 0.0.0" + }, + "dependencies": { + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + "selinux/state": { + "display_name": "SELinux State", + "description": "The SELinux policy enforcement state.", + "choices": [ + "enforcing", + "permissive", + "disabled" + ], + "recipes": [ + "selinux::default" + ], + "type": "string", + "default": "enforcing" + } + }, + "groupings": { + }, + "recipes": { + "selinux": "Use LWRP with state attribute to manage SELinux state.", + "selinux::enforcing": "Use :enforcing as the action for the selinux_state.", + "selinux::permissive": "Use :permissive as the action for the selinux_state.", + "selinux::disabled": "Use :disabled as the action for the selinux_state." + } +} \ No newline at end of file diff --git a/chef/cookbooks/selinux/metadata.rb b/chef/cookbooks/selinux/metadata.rb index 9d26a2d..760db41 100644 --- a/chef/cookbooks/selinux/metadata.rb +++ b/chef/cookbooks/selinux/metadata.rb @@ -1,7 +1,24 @@ name "selinux" maintainer "Opscode, Inc." -maintainer_email "someara@opscode.com" +maintainer_email "cookbooks@opscode.com" license "Apache" -description "Installs/Configures selinux" +description "Manages SELinux policy state via LWRP or recipes." long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "0.5.6" +version "0.7.2" + +%w{redhat centos scientific oracle amazon}.each do |os| + supports os +end + +recipe "selinux", "Use LWRP with state attribute to manage SELinux state." +recipe "selinux::enforcing", "Use :enforcing as the action for the selinux_state." +recipe "selinux::permissive", "Use :permissive as the action for the selinux_state." +recipe "selinux::disabled", "Use :disabled as the action for the selinux_state." + +attribute "selinux/state", + :display_name => "SELinux State", + :description => "The SELinux policy enforcement state.", + :choices => ["enforcing", "permissive", "disabled"], + :recipes => ["selinux::default"], + :type => "string", + :default => "enforcing" diff --git a/chef/cookbooks/selinux/providers/state.rb b/chef/cookbooks/selinux/providers/state.rb new file mode 100644 index 0000000..82f67a5 --- /dev/null +++ b/chef/cookbooks/selinux/providers/state.rb @@ -0,0 +1,75 @@ +# +# Cookbook Name:: selinux +# Provider:: default +# +# Copyright 2011, Opscode, Inc. +# +# 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. + +require 'chef/mixin/shell_out' +include Chef::Mixin::ShellOut + +def whyrun_supported? + true +end + +action :enforcing do + unless @current_resource.state == "enforcing" + execute "selinux-enforcing" do + not_if "getenforce | grep -qx 'Enforcing'" + command "setenforce 1" + end + se_template = render_selinux_template("enforcing") + end +end + +action :disabled do + unless @current_resource.state == "disabled" + execute "selinux-disabled" do + only_if "selinuxenabled" + command "setenforce 0" + end + se_template = render_selinux_template("disabled") + end +end + +action :permissive do + unless @current_resource.state == "permissive" || @current_resource.state == "disabled" + execute "selinux-permissive" do + not_if "getenforce | egrep -qx 'Permissive|Disabled'" + command "setenforce 0" + end + se_template = render_selinux_template("permissive") + end +end + +def load_current_resource + @current_resource = Chef::Resource::SelinuxState.new(new_resource.name) + s = shell_out("getenforce") + @current_resource.state(s.stdout.downcase) +end + +def render_selinux_template(state) + template "#{state} selinux config" do + path "/etc/selinux/config" + source "sysconfig/selinux.erb" + cookbook "selinux" + if state == 'permissive' + not_if "getenforce | grep -qx 'Disabled'" + end + variables( + :selinux => state, + :selinuxtype => "targeted" + ) + end +end diff --git a/chef/cookbooks/selinux/recipes/_common.rb b/chef/cookbooks/selinux/recipes/_common.rb new file mode 100644 index 0000000..dd0815a --- /dev/null +++ b/chef/cookbooks/selinux/recipes/_common.rb @@ -0,0 +1,7 @@ +package 'libselinux-utils' + +directory '/etc/selinux' do + owner 'root' + mode '0644' + action :create +end diff --git a/chef/cookbooks/selinux/recipes/default.rb b/chef/cookbooks/selinux/recipes/default.rb index ecf7912..9ca70fd 100644 --- a/chef/cookbooks/selinux/recipes/default.rb +++ b/chef/cookbooks/selinux/recipes/default.rb @@ -16,3 +16,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +include_recipe 'selinux::_common' + +selinux_state "SELinux #{node['selinux']['state'].capitalize}" do + action node['selinux']['state'].downcase.to_sym +end + +node['selinux']['booleans'].each do |boolean, value| + value = SELinuxServiceHelpers.selinux_bool(value) + unless value.nil? + script "boolean_#{boolean}" do + interpreter "bash" + code "setsebool -P #{boolean} #{value}" + not_if "getsebool #{boolean} |egrep -q \" #{value}\"$" + end + end +end diff --git a/chef/cookbooks/selinux/recipes/disabled.rb b/chef/cookbooks/selinux/recipes/disabled.rb index a47c7b1..0140327 100644 --- a/chef/cookbooks/selinux/recipes/disabled.rb +++ b/chef/cookbooks/selinux/recipes/disabled.rb @@ -18,18 +18,8 @@ # limitations under the License. # -execute "disable selinux enforcement" do - only_if "which selinuxenabled && selinuxenabled" - command "setenforce 0" - action :run - notifies :create, "template[/etc/selinux/config]" -end +include_recipe 'selinux::_common' -template "/etc/selinux/config" do - source "sysconfig/selinux.erb" - variables( - :selinux => "disabled", - :selinuxtype => "targeted" - ) - action :nothing +selinux_state "SELinux Disabled" do + action :disabled end diff --git a/chef/cookbooks/selinux/recipes/enforcing.rb b/chef/cookbooks/selinux/recipes/enforcing.rb index aa83d80..cb9ebb9 100644 --- a/chef/cookbooks/selinux/recipes/enforcing.rb +++ b/chef/cookbooks/selinux/recipes/enforcing.rb @@ -18,16 +18,8 @@ # limitations under the License. # -execute "enable selinux enforcement" do - not_if "getenforce | egrep -qx 'Enforcing|Disabled'" - command "setenforce 1" - action :run -end +include_recipe 'selinux::_common' -template "/etc/selinux/config" do - source "sysconfig/selinux.erb" - variables( - :selinux => "enforcing", - :selinuxtype => "targeted" - ) +selinux_state "SELinux Enforcing" do + action :enforcing end diff --git a/chef/cookbooks/selinux/recipes/permissive.rb b/chef/cookbooks/selinux/recipes/permissive.rb index aa50956..8d40964 100644 --- a/chef/cookbooks/selinux/recipes/permissive.rb +++ b/chef/cookbooks/selinux/recipes/permissive.rb @@ -18,18 +18,8 @@ # limitations under the License. # -execute "enable selinux as permissive" do - not_if "getenforce | egrep -qx 'Permissive|Disabled'" - command "setenforce 0" - ignore_failure true - action :run -end +include_recipe 'selinux::_common' -template "/etc/selinux/config" do - source "sysconfig/selinux.erb" - not_if "getenforce | grep -qx 'Disabled'" - variables( - :selinux => "permissive", - :selinuxtype => "targeted" - ) +selinux_state "SELinux Permissive" do + action :permissive end diff --git a/chef/cookbooks/yum/recipes/yum.rb b/chef/cookbooks/selinux/resources/state.rb similarity index 78% rename from chef/cookbooks/yum/recipes/yum.rb rename to chef/cookbooks/selinux/resources/state.rb index d353f50..40498a2 100644 --- a/chef/cookbooks/yum/recipes/yum.rb +++ b/chef/cookbooks/selinux/resources/state.rb @@ -1,8 +1,7 @@ # -# Cookbook Name:: yum -# Recipe:: yum +# Cookbook Name:: selinux +# Resource:: default # -# Copyright 2011, Eric G. Wolfe # Copyright 2011, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,8 +15,8 @@ # 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. -# -template "/etc/yum.conf" do - source "yum-rhel#{node['platform_version'].to_i}.conf.erb" -end +default_action :nothing +actions :enforcing, :disabled, :permissive + +attribute :state, :default => nil diff --git a/chef/cookbooks/statsd/.foodcritic-rules b/chef/cookbooks/statsd/.foodcritic-rules new file mode 100644 index 0000000..8a0c298 --- /dev/null +++ b/chef/cookbooks/statsd/.foodcritic-rules @@ -0,0 +1,7 @@ +# Foodcritic exclude rules +# +# The build will fail on any warning raised, except rules listed in this file. +# Add one rule per line. Do not prefix rules with a tilde (~). +# +FC003 +FC023 diff --git a/chef/cookbooks/statsd/Berksfile b/chef/cookbooks/statsd/Berksfile new file mode 100644 index 0000000..850a120 --- /dev/null +++ b/chef/cookbooks/statsd/Berksfile @@ -0,0 +1 @@ +metadata diff --git a/chef/cookbooks/statsd/Berksfile.lock b/chef/cookbooks/statsd/Berksfile.lock new file mode 100644 index 0000000..23b30e5 --- /dev/null +++ b/chef/cookbooks/statsd/Berksfile.lock @@ -0,0 +1,31 @@ +{ + "sources": { + "statsd": { + "path": "." + }, + "build-essential": { + "locked_version": "2.0.0" + }, + "git": { + "locked_version": "4.0.0" + }, + "dmg": { + "locked_version": "2.2.0" + }, + "windows": { + "locked_version": "1.30.2" + }, + "chef_handler": { + "locked_version": "1.1.6" + }, + "runit": { + "locked_version": "1.5.10" + }, + "yum": { + "locked_version": "3.2.0" + }, + "yum-epel": { + "locked_version": "0.3.6" + } + } +} diff --git a/chef/cookbooks/statsd/Gemfile b/chef/cookbooks/statsd/Gemfile new file mode 100644 index 0000000..43ac442 --- /dev/null +++ b/chef/cookbooks/statsd/Gemfile @@ -0,0 +1,13 @@ +# -*- mode: ruby -*- +# vim: set ft=ruby : + +source 'https://rubygems.org' + +gem 'berkshelf', '~> 2.0.14' +gem 'chef', '~> 11.4.4' +gem 'chefspec', '~> 1.3.1' +gem 'json', '<= 1.7.7' # Chef 11 dependency +gem 'rake', '~> 10.2.2' +gem 'pry' +gem 'awesome_print' +gem 'ohai', '~> 6.22' diff --git a/chef/cookbooks/openstack-metering/Gemfile.lock b/chef/cookbooks/statsd/Gemfile.lock similarity index 65% rename from chef/cookbooks/openstack-metering/Gemfile.lock rename to chef/cookbooks/statsd/Gemfile.lock index 3142523..2ae9e4a 100644 --- a/chef/cookbooks/openstack-metering/Gemfile.lock +++ b/chef/cookbooks/statsd/Gemfile.lock @@ -1,30 +1,35 @@ GEM remote: https://rubygems.org/ specs: - activesupport (3.2.14) + activesupport (3.2.17) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.3.5) - akami (1.2.0) + addressable (2.3.6) + akami (1.2.2) gyoku (>= 0.4.0) - nokogiri (>= 1.4.0) - berkshelf (2.0.7) + nokogiri + awesome_print (1.2.0) + berkshelf (2.0.15) activesupport (~> 3.2.0) addressable (~> 2.3.4) buff-shell_out (~> 0.1) - celluloid (>= 0.14.0) chozo (>= 0.6.1) - faraday (>= 0.8.5) + faraday (~> 0.8.0) + faraday (~> 0.8.5) hashie (>= 2.0.2) minitar (~> 0.5.4) rbzip2 (~> 0.2.0) retryable (~> 1.3.3) - ridley (~> 1.2.1) - solve (>= 0.5.0) + ridley (~> 1.5.0) + solve (~> 0.8.2) thor (~> 0.18.0) + buff-config (0.4.0) + buff-extensions (~> 0.3) + varia_model (~> 0.1) buff-extensions (0.5.0) + buff-ignore (1.1.1) buff-ruby_engine (0.1.0) - buff-shell_out (0.1.0) + buff-shell_out (0.1.1) buff-ruby_engine (~> 0.1.0) builder (3.2.2) celluloid (0.14.1) @@ -56,87 +61,90 @@ GEM activesupport (>= 3.2.0) hashie (>= 2.0.2) multi_json (>= 1.3.0) - ci_reporter (1.9.0) + ci_reporter (1.9.2) builder (>= 2.1.2) - diff-lcs (1.2.4) + coderay (1.1.0) + diff-lcs (1.2.5) erubis (2.7.0) - faraday (0.8.7) - multipart-post (~> 1.1) + faraday (0.8.9) + multipart-post (~> 1.2.0) fauxhai (1.1.1) httparty net-ssh ohai - ffi (1.9.0) - foodcritic (2.2.0) - erubis - gherkin (~> 2.11.7) - nokogiri (~> 1.5.4) - treetop (~> 1.4.10) - yajl-ruby (~> 1.1.0) - gherkin (2.11.8) - multi_json (~> 1.3) + ffi (1.9.3) gssapi (1.0.3) ffi (>= 1.0.1) - gyoku (1.0.0) + gyoku (1.1.1) builder (>= 2.1.2) - hashie (2.0.5) - highline (1.6.19) + hashie (2.1.1) + highline (1.6.21) + hitimes (1.2.1) httparty (0.11.0) multi_json (~> 1.0) multi_xml (>= 0.5.2) - httpclient (2.2.0.2) + httpclient (2.3.4.1) httpi (0.9.7) rack - i18n (0.6.4) + i18n (0.6.9) ipaddress (0.8.0) json (1.7.7) little-plugger (1.1.3) - log_switch (0.4.0) - logging (1.6.2) + logging (1.8.2) little-plugger (>= 1.1.3) - mime-types (1.23) + multi_json (>= 1.8.4) + method_source (0.8.2) + mime-types (2.2) + mini_portile (0.5.3) minitar (0.5.4) minitest (4.7.5) - minitest-chef-handler (1.0.1) - chef + minitest-chef-handler (1.0.2) + chef (>= 10.12.0) ci_reporter minitest (~> 4.7.3) mixlib-authentication (1.3.0) mixlib-log mixlib-cli (1.3.0) - mixlib-config (1.1.2) + mixlib-config (2.1.0) mixlib-log (1.6.0) - mixlib-shellout (1.2.0) - multi_json (1.7.7) - multi_xml (0.5.4) + mixlib-shellout (1.4.0) + multi_json (1.9.2) + multi_xml (0.5.5) multipart-post (1.2.0) - net-http-persistent (2.9) - net-ssh (2.6.8) + net-http-persistent (2.9.4) + net-ssh (2.8.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) net-ssh-multi (1.1) net-ssh (>= 2.1.4) net-ssh-gateway (>= 0.99.0) - nio4r (0.4.6) - nokogiri (1.5.10) + nio4r (1.0.0) + nokogiri (1.6.1) + mini_portile (~> 0.5.0) nori (1.1.5) - ohai (6.18.0) + ohai (6.22.0) ipaddress mixlib-cli mixlib-config mixlib-log mixlib-shellout - systemu + systemu (~> 2.5.2) yajl-ruby - polyglot (0.3.3) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) rack (1.5.2) + rake (10.2.2) rbzip2 (0.2.0) rest-client (1.6.7) mime-types (>= 1.16) - retryable (1.3.3) - ridley (1.2.4) + retryable (1.3.5) + ridley (1.5.3) addressable + buff-config (~> 0.2) buff-extensions (~> 0.3) + buff-ignore (~> 1.1) buff-shell_out (~> 0.1) celluloid (~> 0.14.0) celluloid-io (~> 0.14.0) @@ -147,6 +155,7 @@ GEM mixlib-authentication (>= 1.3.0) net-http-persistent (>= 2.8) net-ssh + nio4r (>= 0.5.0) retryable solve (>= 0.4.4) varia_model (~> 0.1) @@ -155,10 +164,10 @@ GEM rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - rspec-core (2.14.4) - rspec-expectations (2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.1) + rspec-mocks (2.14.6) rubyntlm (0.1.1) savon (0.9.5) akami (~> 1.0) @@ -168,47 +177,37 @@ GEM nokogiri (>= 1.4.0) nori (~> 1.0) wasabi (~> 1.0) - solve (0.6.1) - strainer (3.0.5) - berkshelf (~> 2.0) + slop (3.5.0) + solve (0.8.2) systemu (2.5.2) - tailor (1.2.1) - log_switch (>= 0.3.0) - term-ansicolor (>= 1.0.5) - text-table (>= 1.2.2) - term-ansicolor (1.2.2) - tins (~> 0.8) - text-table (1.2.3) thor (0.18.1) - timers (1.1.0) - tins (0.8.3) - treetop (1.4.14) - polyglot - polyglot (>= 0.3.1) + timers (2.0.0) + hitimes uuidtools (2.1.4) - varia_model (0.1.1) + varia_model (0.3.2) buff-extensions (~> 0.2) hashie (>= 2.0.2) wasabi (1.0.0) nokogiri (>= 1.4.0) - winrm (1.1.2) + winrm (1.1.3) gssapi (~> 1.0.0) - httpclient (~> 2.2.0.2) - logging (~> 1.6.1) - nokogiri (~> 1.5.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (~> 1.6, >= 1.6.1) + nokogiri (~> 1.5) rubyntlm (~> 0.1.1) savon (= 0.9.5) uuidtools (~> 2.1.2) - yajl-ruby (1.1.0) + yajl-ruby (1.2.0) PLATFORMS ruby DEPENDENCIES - berkshelf (~> 2.0.3) + awesome_print + berkshelf (~> 2.0.14) chef (~> 11.4.4) - chefspec (~> 1.3.0) - foodcritic + chefspec (~> 1.3.1) json (<= 1.7.7) - strainer - tailor + ohai (~> 6.22) + pry + rake (~> 10.2.2) diff --git a/chef/cookbooks/statsd/README.md b/chef/cookbooks/statsd/README.md new file mode 100644 index 0000000..02228ff --- /dev/null +++ b/chef/cookbooks/statsd/README.md @@ -0,0 +1,51 @@ +Description +=========== + +Installs and sets up statsd + +Requirements +============ + +Ubuntu 12.04 + +Attributes +========== + +* `node['statsd']['port']` - The port for Statsd to listen for stats on. Defaults to 8125 +* `node['statsd']['graphite_host']` - The host to forward processed statistics to. Defaults to localhost. +* `node['statsd']['graphite_port']` - The port to forward processed statistics to. Defaults to 2003 +* `node['statsd']['package_version']` - The version to use when creating the package. Defaults to 0.6.0 +* `node['statsd']['tmp_dir']` - The temporary directory to while building the package. Defaults to /tmp +* `node['statsd']['repo']` - The gitrepo to use. Defaults to "git://github.com/etsy/statsd.git" +* `node['statsd']['sha']` - The sha checksum of the repo to use + +Usage +===== + +Including this recipe will build a dpkg from the statsd git repository and install it. + +By default statsd will attempt to send statistics to a graphite instance running on localhost. + +Testing +======= + +See SilverLining documentation + +License and Author +================== + +Author:: Scott Lampert () + +Copyright 2012-2014, AT&T Services, Inc. + +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. diff --git a/chef/cookbooks/statsd/README.rdoc b/chef/cookbooks/statsd/README.rdoc deleted file mode 100644 index ec71017..0000000 --- a/chef/cookbooks/statsd/README.rdoc +++ /dev/null @@ -1,26 +0,0 @@ -= DESCRIPTION: - -Installs and configures Statsd (http://github.com/etsy/statsd) - -= REQUIREMENTS: - -Ubuntu 11.04 (Natty) - -= ATTRIBUTES: - -* statsd/port : The port for Statsd to listen for stats on. -* statsd/graphite_host : The host to forward processed statistics to. -* statsd/graphite_port : The port to forward processed statistics to. - -= USAGE: - -Include the statsd recipe, which will checkout Statsd from git, build a Debian package from it, -and then install the package. Statsd is run under a "statsd" system user. - -By default statsd will attempt to send statistics to a graphite instance running on localhost, which -can be configured using the Graphite cookbook at http://community.opscode.com/cookbooks/graphite. - -= CAVEATS: - -This cookbook has only been tested on Ubuntu Natty (11.04). That is due to it using the "nodejs" package -rather then attempting to build Node from source. diff --git a/chef/cookbooks/statsd/attributes/default.rb b/chef/cookbooks/statsd/attributes/default.rb new file mode 100644 index 0000000..8f7dfc6 --- /dev/null +++ b/chef/cookbooks/statsd/attributes/default.rb @@ -0,0 +1,9 @@ +default['statsd']['port'] = 8125 +default['statsd']['graphite_port'] = 2003 +default['statsd']['graphite_host'] = "localhost" +default['statsd']['relay_server'] = false +default['statsd']['package_version'] = "0.6.0" +default['statsd']['sha'] = "2ccde8266bbe941ac5f79efe39103b99e1196d92" +default['statsd']['user'] = "statsd" +default['statsd']['repo'] = "git://github.com/etsy/statsd.git" +default['statsd']['tmp_dir'] = "/tmp" diff --git a/chef/cookbooks/statsd/attributes/statsd.rb b/chef/cookbooks/statsd/attributes/statsd.rb deleted file mode 100644 index 0df7e9d..0000000 --- a/chef/cookbooks/statsd/attributes/statsd.rb +++ /dev/null @@ -1,3 +0,0 @@ -default[:statsd][:port] = 8125 -default[:statsd][:graphite_port] = 2003 -default[:statsd][:graphite_host] = "localhost" diff --git a/chef/cookbooks/statsd/files/default/statsd b/chef/cookbooks/statsd/files/default/statsd deleted file mode 100644 index 0075f53..0000000 --- a/chef/cookbooks/statsd/files/default/statsd +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -exec node /usr/local/statsd/stats.js /etc/statsd/config.js diff --git a/chef/cookbooks/statsd/files/default/upstart.conf b/chef/cookbooks/statsd/files/default/upstart.conf index 9eec600..d201868 100644 --- a/chef/cookbooks/statsd/files/default/upstart.conf +++ b/chef/cookbooks/statsd/files/default/upstart.conf @@ -1,12 +1,12 @@ description "statsd" -author "ops@simplymeasured.com" +author "etsy" start on startup stop on shutdown script - echo $$ > /var/run/statsd.pid # We found $HOME is needed. Without it, we ran into problems export HOME="/root" - exec sudo -u statsd /usr/local/sbin/statsd >> /var/log/statsd.log 2>&1 + + exec sudo -u statsd /usr/share/statsd/scripts/start end script diff --git a/chef/cookbooks/statsd/files/default/upstart.start b/chef/cookbooks/statsd/files/default/upstart.start new file mode 100755 index 0000000..5c1122d --- /dev/null +++ b/chef/cookbooks/statsd/files/default/upstart.start @@ -0,0 +1,3 @@ +#!/bin/sh +# Called by Upstart, /etc/init/statsd.conf +node /usr/share/statsd/stats.js /etc/statsd/localConfig.js 2>&1 >> /tmp/statsd.log diff --git a/chef/cookbooks/statsd/metadata.rb b/chef/cookbooks/statsd/metadata.rb index 2ac8b7b..945b09a 100644 --- a/chef/cookbooks/statsd/metadata.rb +++ b/chef/cookbooks/statsd/metadata.rb @@ -1,12 +1,16 @@ -maintainer "Jon Wood" -maintainer_email "jon@blankpad.net" -license "Apache 2.0" -description "Installs/Configures statsd" -long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) -version "0.1.0" +name 'statsd' +maintainer 'AT&T Services, Inc.' +maintainer_email 'cookbooks@lists.tfoundry.com' +license 'Apache 2.0' +description 'Installs/Configures statsd' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '0.1.6' +recipe 'statsd', 'Installs stats ruby gem' +recipe 'statsd::server', 'Configures statsd server' -supports "ubuntu" +%w{ ubuntu }.each do |os| + supports os +end -depends "build-essential" -depends "git" -depends "nodejs" +depends 'build-essential' +depends 'git' diff --git a/chef/cookbooks/statsd/recipes/default.rb b/chef/cookbooks/statsd/recipes/default.rb index 3c6e098..3556922 100644 --- a/chef/cookbooks/statsd/recipes/default.rb +++ b/chef/cookbooks/statsd/recipes/default.rb @@ -2,7 +2,7 @@ # Cookbook Name:: statsd # Recipe:: default # -# Copyright 2011, Blank Pad Development +# Copyright 2013, Scott Lampert # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,47 +17,4 @@ # limitations under the License. # -include_recipe "build-essential" -include_recipe "git" -include_recipe "nodejs" - -execute "checkout statsd" do - command "git clone git://github.com/etsy/statsd" - creates "/usr/local/statsd" - cwd "/usr/local" -end - -directory "/etc/statsd" - -template "/etc/statsd/config.js" do - source "config.js.erb" - mode 0644 - variables( - :port => node[:statsd][:port], - :graphitePort => node[:statsd][:graphite_port], - :graphiteHost => node[:statsd][:graphite_host] - ) - - notifies :restart, "service[statsd]" -end - -cookbook_file "/usr/local/sbin/statsd" do - source "statsd" - mode 0755 -end - -cookbook_file "/etc/init/statsd.conf" do - source "upstart.conf" - mode 0644 -end - -user "statsd" do - comment "statsd" - system true - shell "/bin/false" -end - -service "statsd" do - provider Chef::Provider::Service::Upstart - action [ :enable, :start ] -end +gem_package "statsd-ruby" diff --git a/chef/cookbooks/statsd/recipes/server.rb b/chef/cookbooks/statsd/recipes/server.rb new file mode 100644 index 0000000..034b743 --- /dev/null +++ b/chef/cookbooks/statsd/recipes/server.rb @@ -0,0 +1,87 @@ +# +# Cookbook Name:: statsd +# Recipe:: server +# +# Copyright 2013, Scott Lampert +# +# 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. +# + +include_recipe "build-essential" +include_recipe "git" + +case node["platform"] + when "ubuntu", "debian" + + package "nodejs" + package "debhelper" + + statsd_version = node['statsd']['sha'] + + git ::File.join(node['statsd']['tmp_dir'], "statsd") do + repository node['statsd']['repo'] + reference statsd_version + action :sync + notifies :run, "execute[build debian package]" + end + + # Fix the debian changelog file of the repo + template ::File.join(node['statsd']['tmp_dir'], "statsd/debian/changelog") do + source "changelog.erb" + end + + execute "build debian package" do + command "dpkg-buildpackage -us -uc" + cwd ::File.join(node['statsd']['tmp_dir'], "statsd") + creates ::File.join(node['statsd']['tmp_dir'], "statsd_#{node['statsd']['package_version']}_all.deb") + end + + dpkg_package "statsd" do + action :install + source ::File.join(node['statsd']['tmp_dir'], "statsd_#{node['statsd']['package_version']}_all.deb") + end + + when "redhat", "centos" + raise "No support for RedHat or CentOS (yet)." +end + +template "/etc/statsd/localConfig.js" do + source "localConfig.js.erb" + mode 00644 + notifies :restart, "service[statsd]" +end + +cookbook_file "/usr/share/statsd/scripts/start" do + source "upstart.start" + owner "root" + group "root" + mode 00755 +end + +cookbook_file "/etc/init/statsd.conf" do + source "upstart.conf" + owner "root" + group "root" + mode 00644 +end + +user node['statsd']['user'] do + comment "statsd" + system true + shell "/bin/false" +end + +service "statsd" do + provider Chef::Provider::Service::Upstart + action [ :enable, :start ] +end diff --git a/chef/cookbooks/statsd/templates/default/changelog.erb b/chef/cookbooks/statsd/templates/default/changelog.erb new file mode 100644 index 0000000..837b426 --- /dev/null +++ b/chef/cookbooks/statsd/templates/default/changelog.erb @@ -0,0 +1,5 @@ +statsd (<%= node['statsd']['package_version'] %>) unstable; urgency=low + + * Dummy changelog for dpkg build + + -- Scott Lampert Thu, 14 Mar 2013 15:24:00 -0700 diff --git a/chef/cookbooks/statsd/templates/default/config.js.erb b/chef/cookbooks/statsd/templates/default/config.js.erb deleted file mode 100644 index c4afa71..0000000 --- a/chef/cookbooks/statsd/templates/default/config.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -{ -graphitePort: <%= @graphitePort %> -, graphiteHost: "<%= @graphiteHost %>" -, port: <%= @port %> -} diff --git a/chef/cookbooks/statsd/templates/default/localConfig.js.erb b/chef/cookbooks/statsd/templates/default/localConfig.js.erb new file mode 100644 index 0000000..4b3d806 --- /dev/null +++ b/chef/cookbooks/statsd/templates/default/localConfig.js.erb @@ -0,0 +1,17 @@ +/******************** +AUTOGENERATED BY CHEF +*********************/ + +{ +graphitePort: <%= node['statsd']['graphite_port'] %> +, graphiteHost: "<%= node['statsd']['graphite_host'] %>" +<% if node['statsd']['relay_server'] -%> +, address: "127.0.0.1" +, mgmt_address: "127.0.0.1" +<% else -%> +, address: "<%= node['statsd']['graphite_host'] %>" +, mgmt_address: "<%=node['statsd']['graphite_host'] %>" +<% end -%> +, port: <%= node['statsd']['port'] %> +, backends: [ "./backends/graphite" ] +} diff --git a/chef/cookbooks/windows/CHANGELOG.md b/chef/cookbooks/windows/CHANGELOG.md index 1bd0ee2..a818ab2 100644 --- a/chef/cookbooks/windows/CHANGELOG.md +++ b/chef/cookbooks/windows/CHANGELOG.md @@ -1,19 +1,79 @@ -## Future +windows Cookbook CHANGELOG +======================= +This file is used to list changes made in each version of the windows cookbook. -* package preseeding/`response_file` support -* package installation location via a `target_dir` attribute. -* [COOK-666] `windows_package` should support CoApp packages -* WindowsRebootHandler/`windows_reboot` LWRP should support kicking off subsequent chef run on reboot. +v1.31.0 (2014-05-07) +-------------------- +- [COOK-2934] - Add windows_feature support for 2 new DISM attributes: all, source -## v1.10.0: +v1.30.2 (2014-04-02) +-------------------- +- [COOK-4414] - Adding ChefSpec matchers + + +v1.30.0 (2014-02-14) +-------------------- +- [COOK-3715] - Unable to create a startup task with no login +- [COOK-4188] - Add powershell_version method to return Powershell version + + +v1.12.8 (2014-01-21) +-------------------- +[COOK-3988] Don't unescape URI before constructing it. + + +v1.12.6 (2014-01-03) +-------------------- +[COOK-4168] Circular dep on powershell - moving powershell libraries into windows. removing dependency on powershell + + +v1.12.4 +------- +Fixing depend/depends typo in metadata.rb + + +v1.12.2 +------- +### Bug +- **[COOK-4110](https://tickets.opscode.com/browse/COOK-4110)** - feature_servermanager installed? method regex bug + + +v1.12.0 +------- +### Bug +- **[COOK-3793](https://tickets.opscode.com/browse/COOK-3793)** - parens inside parens of README.md don't render + +### New Feature +- **[COOK-3714](https://tickets.opscode.com/browse/COOK-3714)** - Powershell features provider and delete support. + + +v1.11.0 +------- +### Improvement +- **[COOK-3724](https://tickets.opscode.com/browse/COOK-3724)** - Rrecommend built-in resources over cookbook resources +- **[COOK-3515](https://tickets.opscode.com/browse/COOK-3515)** - Remove unprofessional comment from library +- **[COOK-3455](https://tickets.opscode.com/browse/COOK-3455)** - Add Windows Server 2012R2 to windows cookbook version helper + +### Bug +- **[COOK-3542](https://tickets.opscode.com/browse/COOK-3542)** - Fix an issue where `windows_zipfile` fails with LoadError +- **[COOK-3447](https://tickets.opscode.com/browse/COOK-3447)** - Allow Overriding Of The Default Reboot Timeout In windows_reboot_handler +- **[COOK-3382](https://tickets.opscode.com/browse/COOK-3382)** - Allow windows_task to create `on_logon` tasks +- **[COOK-2098](https://tickets.opscode.com/browse/COOK-2098)** - Fix and issue where the `windows_reboot` handler is ignoring the reboot time + +### New Feature +- **[COOK-3458](https://tickets.opscode.com/browse/COOK-3458)** - Add support for `start_date` and `start_time` in `windows_task` + + +v1.10.0 +------- ### Improvement - [COOK-3126]: `windows_task` should support the on start frequency - [COOK-3127]: Support the force option on task create and delete -## v1.9.0: - +v1.9.0 +------ ### Bug - [COOK-2899]: windows_feature fails when a feature install requires a @@ -26,8 +86,8 @@ - [COOK-2686]: Add Windows Server 2012 to version.rb so other depending chef scripts can detect Windows Server 2012 -## v1.8.10: - +v1.8.10 +------- When using Windows qualified filepaths (C:/foo), the #absolute? method for URI returns true, because "C" is the scheme. @@ -36,66 +96,66 @@ passed off to remote_file appropriately. * [COOK-2729] - allow only http, https URI schemes -## v1.8.8: - +v1.8.8 +------ * [COOK-2729] - helper should use URI rather than regex and bare string -## v1.8.6: - +v1.8.6 +------ * [COOK-968] - `windows_package` provider should gracefully handle paths with spaces * [COOK-222] - `windows_task` resource does not declare :change action * [COOK-241] - Windows cookbook should check for redefined constants * [COOK-248] - Windows package install type is case sensitive -## v1.8.4: - +v1.8.4 +------ * [COOK-2336] - MSI That requires reboot returns with RC 3010 and causes chef run failure * [COOK-2368] - `version` attribute of the `windows_package` provider should be documented -## v1.8.2: - +v1.8.2 +------ **Important**: Use powershell in nodes expanded run lists to ensure powershell is downloaded, as powershell has a dependency on this cookbook; v1.8.0 created a circular dependency. * [COOK-2301] - windows 1.8.0 has circular dependency on powershell -## v1.8.0: - +v1.8.0 +------ * [COOK-2126] - Add checksum attribute to `windows_zipfile` * [COOK-2142] - Add printer and `printer_port` LWRPs * [COOK-2149] - Chef::Log.debug Windows Package command line * [COOK-2155] -`windows_package` does not send checksum to `cached_file` in `installer_type` -## v1.7.0: - +v1.7.0 +------ * [COOK-1745] - allow for newer versions of rubyzip -## v1.6.0: - +v1.6.0 +------ * [COOK-2048] - undefined method for Falseclass on task :change when action is :nothing (and task doesn't exist) * [COOK-2049] - Add `windows_pagefile` resource -## v1.5.0: - +v1.5.0 +------ * [COOK-1251] - Fix LWRP "NotImplementedError" * [COOK-1921] - Task LWRP will return true for resource exists when no other scheduled tasks exist * [COOK-1932] - Include :change functionality to windows task lwrp -## v1.4.0: - +v1.4.0: +------ * [COOK-1571] - `windows_package` resource (with msi provider) does not accept spaces in filename * [COOK-1581] - Windows cookbook needs a scheduled tasks LWRP * [COOK-1584] - `windows_registry` should support all registry types -## v1.3.4: - +v1.3.4 +------ * [COOK-1173] - `windows_registry` throws Win32::Registry::Error for action :remove on a nonexistent key * [COOK-1182] - windows package sets start window title instead of @@ -105,14 +165,14 @@ accept spaces in filename when "source" contains quote * [COOK-1519] - add action :remove for path lwrp -## v1.3.2: - +v1.3.2 +------ * [COOK-1033] - remove the `libraries/ruby_19_patches.rb` file which causes havoc on non-Windows systems. * [COOK-811] - add a timeout parameter attribute for `windows_package` -## v1.3.0: - +v1.3.0 +------ * [COOK-1323] - Update for changes in Chef 0.10.10. - Setting file mode doesn't make sense on Windows (package provider - and `reboot_handler` recipe) @@ -122,35 +182,35 @@ accept spaces in filename under the Ruby environment Chef runs in (reboot_handler recipe, zipfile provider) -## v1.2.12: - +v1.2.12 +------- * [COOK-1037] - specify version for rubyzip gem * [COOK-1007] - `windows_feature` does not work to remove features with dism * [COOK-667] - shortcut resource + provider for Windows platforms -## v1.2.10 - +v1.2.10 +------- * [COOK-939] - add `type` parameter to `windows_registry` to allow binary registry keys. * [COOK-940] - refactor logic so multiple values get created. -## v1.2.8 - +v1.2.8 +------ * FIX: Older Windows (Windows Server 2003) sometimes return 127 on successful forked commands * FIX: `windows_package`, ensure we pass the WOW* registry redirection flags into reg.open -## v1.2.6 - +v1.2.6 +------ * patch to fix [CHEF-2684], Open4 is named Open3 in Ruby 1.9 * Ruby 1.9's Open3 returns 0 and 42 for successful commands * retry keyword can only be used in a rescue block in Ruby 1.9 -## v1.2.4 - +v1.2.4 +------ * `windows_package` - catch Win32::Registry::Error that pops up when searching certain keys -## v1.2.2 - +v1.2.2 +------ * combined numerous helper libarires for easier sharing across libaries/LWRPs * renamed Chef::Provider::WindowsFeature::Base file to the more descriptive `feature_base.rb` * refactored `windows_path` LWRP @@ -158,8 +218,8 @@ accept spaces in filename * deleted greedy :remove action until it could be made more idempotent * added a `windows_batch` resource/provider for running batch scripts remotely -## v1.2.0 - +v1.2.0 +------ * [COOK-745] gracefully handle required server restarts on Windows platform * WindowsRebootHandler for requested and pending reboots * `windows_reboot` LWRP for requesting (receiving notifies) reboots @@ -167,21 +227,21 @@ accept spaces in filename * [COOK-714] Correct initialize misspelling * RegistryHelper - new `get_values` method which returns all values for a particular key. -## v1.0.8 - +v1.0.8 +------ * [COOK-719] resource/provider for managing windows features * [COOK-717] remove `windows_env_vars` resource as env resource exists in core chef * new `Windows::Version` helper class * refactored `Windows::Helper` mixin -## v1.0.6 - +v1.0.6 +------ * added `force_modify` action to `windows_registry` resource * add `win_friendly_path` helper * re-purpose default recipe to install useful supporting windows related gems -## v1.0.4 - +v1.0.4 +------ * [COOK-700] new resources and improvements to the `windows_registry` provider (thanks Paul Morton!) * Open the registry in the bitednes of the OS * Provide convenience methods to check if keys and values exit @@ -192,11 +252,11 @@ accept spaces in filename * re-write of the `windows_package` logic for determining current installed packages * new checksum attribute for `windows_package` resource...useful for remote packages -## v1.0.2: - +v1.0.2 +------ * [COOK-647] account for Wow6432Node registry redirecter * [COOK-656] begin/rescue on win32/registry -## 1.0.0: - +v1.0.0 +------ * [COOK-612] initial release diff --git a/chef/cookbooks/windows/CONTRIBUTING b/chef/cookbooks/windows/CONTRIBUTING deleted file mode 100644 index 89ac873..0000000 --- a/chef/cookbooks/windows/CONTRIBUTING +++ /dev/null @@ -1,29 +0,0 @@ -If you would like to contribute, please open a ticket in JIRA: - -* http://tickets.opscode.com - -Create the ticket in the COOK project and use the cookbook name as the -component. - -For all code contributions, we ask that contributors sign a -contributor license agreement (CLA). Instructions may be found here: - -* http://wiki.opscode.com/display/chef/How+to+Contribute - -When contributing changes to individual cookbooks, please do not -modify the version number in the metadata.rb. Also please do not -update the CHANGELOG.md for a new version. Not all changes to a -cookbook may be merged and released in the same versions. Opscode will -handle the version updates during the release process. You are welcome -to correct typos or otherwise make updates to documentation in the -README. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] Updated pool resource to correctly delete.' - -In the ticket itself, it is also helpful if you include log output of -a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/windows/LICENSE b/chef/cookbooks/windows/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/windows/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/windows/README.md b/chef/cookbooks/windows/README.md index 2829cbd..fa21926 100644 --- a/chef/cookbooks/windows/README.md +++ b/chef/cookbooks/windows/README.md @@ -1,16 +1,14 @@ -Description -=========== - +Windows Cookbook +================ Provides a set of Windows-specific primitives (Chef resources) meant to aid in the creation of cookbooks/recipes targeting the Windows platform. -Requirements -============ +Requirements +------------- Version 1.3.0+ of this cookbook requires Chef 0.10.10+. -Platform --------- +### Platforms * Windows XP * Windows Vista * Windows Server 2003 R2 @@ -19,63 +17,49 @@ Platform The `windows_task` LWRP requires Windows Server 2008 due to its API usage. -Cookbooks ---------- - +### Cookbooks The following cookbooks provided by Opscode are required as noted: * chef_handler (`windows::reboot_handler` leverages the chef_handler LWRP) -* powershell - The Printer and Printer Port LWRP require Powershell. - -**NOTE** We cannot specifically depend on Opscode's powershell, - because powershell depends on this cookbook. Ensure that - `recipe[powershell]` exists in the node's expanded run list so it - gets downloaded where the printer LWRPs are used. Attributes -========== - +---------- * `node['windows']['allow_pending_reboots']` - used to configure the `WindowsRebootHandler` (via the `windows::reboot_handler` recipe) to act on pending reboots. default is true (ie act on pending reboots). The value of this attribute only has an effect if the `windows::reboot_handler` is in a node's run list. + Resource/Provider -================= - -windows\_auto\_run ------------------- - -### Actions - +----------------- +### windows_auto_run +#### Actions - :create: Create an item to be run at login - :remove: Remove an item that was previously setup to run at login -### Attribute Parameters - +#### Attribute Parameters - :name: Name attribute. The name of the value to be stored in the registry - :program: The program to be run at login - :args: The arguments for the program -### Examples +#### Examples +Run BGInfo at login - # Run BGInfo at login - windows_auto_run 'BGINFO' do - program "C:/Sysinternals/bginfo.exe" - args "\"C:/Sysinternals/Config.bgi\" /NOLICPROMPT /TIMER:0" - not_if { Registry.value_exists?(AUTO_RUN_KEY, 'BGINFO') } - action :create - end +```ruby +windows_auto_run 'BGINFO' do + program 'C:/Sysinternals/bginfo.exe' + args '\'C:/Sysinternals/Config.bgi\' /NOLICPROMPT /TIMER:0' + not_if { Registry.value_exists?(AUTO_RUN_KEY, 'BGINFO') } + action :create +end +``` - -windows\_batch --------------- +### windows_batch +(Chef 11.6.0 includes a built-in [batch](http://docs.opscode.com/resource_batch.html) resource, so use that in preference to `windows_batch` if possible.) Execute a batch script using the cmd.exe interpreter (much like the script resources for bash, csh, powershell, perl, python and ruby). A temporary file is created and executed like other script resources, rather than run inline. By their nature, Script resources are not idempotent, as they are completely up to the user's imagination. Use the `not_if` or `only_if` meta parameters to guard the resource for idempotence. -### Actions - +#### Actions - :run: run the batch file -### Attribute Parameters - +#### Attribute Parameters - command: name attribute. Name of the command to execute. - code: quoted string of code to execute. - creates: a file this command creates - if the file exists, the command will not be run. @@ -84,74 +68,93 @@ Execute a batch script using the cmd.exe interpreter (much like the script resou - user: A user name or user ID that we should change to before running this command. - group: A group name or group ID that we should change to before running this command. -### Examples +#### Examples +```ruby +windows_batch 'unzip_and_move_ruby' do + code <<-EOH + 7z.exe x #{Chef::Config[:file_cache_path]}/ruby-1.8.7-p352-i386-mingw32.7z -oC:\\source -r -y + xcopy C:\\source\\ruby-1.8.7-p352-i386-mingw32 C:\\ruby /e /y + EOH +end +``` - windows_batch "unzip_and_move_ruby" do - code <<-EOH - 7z.exe x #{Chef::Config[:file_cache_path]}/ruby-1.8.7-p352-i386-mingw32.7z -oC:\\source -r -y - xcopy C:\\source\\ruby-1.8.7-p352-i386-mingw32 C:\\ruby /e /y - EOH - end - - windows_batch "echo some env vars" do - code <<-EOH - echo %TEMP% - echo %SYSTEMDRIVE% - echo %PATH% - echo %WINDIR% - EOH - end - -windows\_feature ----------------- +```ruby +windows_batch 'echo some env vars' do + code <<-EOH + echo %TEMP% + echo %SYSTEMDRIVE% + echo %PATH% + echo %WINDIR% + EOH +end +``` +### windows_feature Windows Roles and Features can be thought of as built-in operating system packages that ship with the OS. A server role is a set of software programs that, when they are installed and properly configured, lets a computer perform a specific function for multiple users or other computers within a network. A Role can have multiple Role Services that provide functionality to the Role. Role services are software programs that provide the functionality of a role. Features are software programs that, although they are not directly parts of roles, can support or augment the functionality of one or more roles, or improve the functionality of the server, regardless of which roles are installed. Collectively we refer to all of these attributes as 'features'. This resource allows you to manage these 'features' in an unattended, idempotent way. -There are two providers for the `windows_features` which map into Microsoft's two major tools for managing roles/features: [Deployment Image Servicing and Management (DISM)](http://msdn.microsoft.com/en-us/library/dd371719(v=vs.85).aspx) and [Servermanagercmd](http://technet.microsoft.com/en-us/library/ee344834(WS.10).aspx) (The CLI for Server Manager). As Servermanagercmd is deprecated, Chef will set the default provider to `Chef::Provider::WindowsFeature::DISM` if DISM is present on the system being configured. The default provider will fall back to `Chef::Provider::WindowsFeature::ServerManagerCmd`. +There are two providers for the `windows_features` which map into Microsoft's two major tools for managing roles/features: [Deployment Image Servicing and Management (DISM)](http://msdn.microsoft.com/en-us/library/dd371719%28v=vs.85%29.aspx) and [Servermanagercmd](http://technet.microsoft.com/en-us/library/ee344834%28WS.10%29.aspx) (The CLI for Server Manager). As Servermanagercmd is deprecated, Chef will set the default provider to `Chef::Provider::WindowsFeature::DISM` if DISM is present on the system being configured. The default provider will fall back to `Chef::Provider::WindowsFeature::ServerManagerCmd`. For more information on Roles, Role Services and Features see the [Microsoft TechNet article on the topic](http://technet.microsoft.com/en-us/library/cc754923.aspx). For a complete list of all features that are available on a node type either of the following commands at a command prompt: - dism /online /Get-Features - servermanagercmd -query - -### Actions +```text +dism /online /Get-Features +servermanagercmd -query +``` +#### Actions - :install: install a Windows role/feature - :remove: remove a Windows role/feature -### Attribute Parameters - +#### Attribute Parameters - feature_name: name of the feature/role to install. The same feature may have different names depending on the provider used (ie DHCPServer vs DHCP; DNS-Server-Full-Role vs DNS). +- all: Boolean. Optional. Default: false. DISM provider only. Forces all dependencies to be installed. +- source: String. Optional. DISM provider only. Uses local repository for feature install. -### Providers - +#### Providers - **Chef::Provider::WindowsFeature::DISM**: Uses Deployment Image Servicing and Management (DISM) to manage roles/features. - **Chef::Provider::WindowsFeature::ServerManagerCmd**: Uses Server Manager to manage roles/features. -### Examples +#### Examples +Enable the node as a DHCP Server - # enable the node as a DHCP Server - windows_feature "DHCPServer" do - action :install - end +```ruby +windows_feature 'DHCPServer' do + action :install +end +``` - # enable TFTP - windows_feature "TFTP" do - action :install - end +Enable TFTP - # disable Telnet client/server - %w{ TelnetServer TelnetClient }.each do |feature| - windows_feature feature do - action :remove - end - end +```ruby +windows_feature 'TFTP' do + action :install +end +``` -windows\_package ----------------- +Enable .Net 3.5.1 on Server 2012 using repository files on DVD and +install all dependencies +```ruby +windows_feature "NetFx3" do + action :install + all true + source "d:\sources\sxs" +end +``` + +Disable Telnet client/server + +```ruby +%w[TelnetServer TelnetClient].each do |feature| + windows_feature feature do + action :remove + end +end +``` + +### windows_package Manage Windows application packages in an unattended, idempotent way. The following application installers are currently supported: @@ -172,13 +175,11 @@ __PLEASE NOTE__ - For proper idempotence the resource's `package_name` should be For maximum flexibility the `source` attribute supports both remote and local installation packages. -### Actions - +#### Actions - :install: install a package - :remove: remove a package. The remove action is completely hit or miss as many application uninstallers do not support a full silent/quiet mode. -### Attribute Parameters - +#### Attribute Parameters - package_name: name attribute. The 'DisplayName' of the application installation package. - source: The source of the windows installer. This can either be a URI or a local path. - installer_type: They type of windows installation package. valid values are: :msi, :inno, :nsis, :wise, :installshield, :custom. If this value is not provided, the provider will do it's best to identify the installer type through introspection of the file. @@ -191,206 +192,212 @@ For maximum flexibility the `source` attribute supports both remote and local in have a different return code, e.g. 3010 for reboot required. Must be an array, and defaults to `[0, 42, 127]`. -### Examples +#### Examples - # install PuTTY (InnoSetup installer) - windows_package "PuTTY version 0.60" do - source "http://the.earth.li/~sgtatham/putty/latest/x86/putty-0.60-installer.exe" - installer_type :inno - action :install - end +Install PuTTY (InnoSetup installer) +```ruby +windows_package 'PuTTY version 0.60' do + source 'http://the.earth.li/~sgtatham/putty/latest/x86/putty-0.60-installer.exe' + installer_type :inno + action :install +end +``` - # install 7-Zip (MSI installer) - windows_package "7-Zip 9.20 (x64 edition)" do - source "http://downloads.sourceforge.net/sevenzip/7z920-x64.msi" - action :install - end +Install 7-Zip (MSI installer) +```ruby +windows_package '7-Zip 9.20 (x64 edition)' do + source 'http://downloads.sourceforge.net/sevenzip/7z920-x64.msi' + action :install +end +``` - # install Notepad++ (Y U No Emacs?) using a local installer - windows_package "Notepad++" do - source "c:/installation_files/npp.5.9.2.Installer.exe" - action :install - end +Install Notepad++ (Y U No Emacs?) using a local installer +```ruby +windows_package 'Notepad++' do + source 'c:/installation_files/npp.5.9.2.Installer.exe' + action :install +end +``` - # install VLC for that Xvid (NSIS installer) - windows_package "VLC media player 1.1.10" do - source "http://superb-sea2.dl.sourceforge.net/project/vlc/1.1.10/win32/vlc-1.1.10-win32.exe" - action :install - end +Install VLC for that Xvid (NSIS installer) +```ruby +windows_package 'VLC media player 1.1.10' do + source 'http://superb-sea2.dl.sourceforge.net/project/vlc/1.1.10/win32/vlc-1.1.10-win32.exe' + action :install +end +``` - # install Firefox as custom installer and manually set the silent install flags - windows_package "Mozilla Firefox 5.0 (x86 en-US)" do - source "http://archive.mozilla.org/pub/mozilla.org/mozilla.org/firefox/releases/5.0/win32/en-US/Firefox%20Setup%205.0.exe" - options "-ms" - installer_type :custom - action :install - end +Install Firefox as custom installer and manually set the silent install flags +```ruby +windows_package 'Mozilla Firefox 5.0 (x86 en-US)' do + source 'http://archive.mozilla.org/pub/mozilla.org/mozilla.org/firefox/releases/5.0/win32/en-US/Firefox%20Setup%205.0.exe' + options '-ms' + installer_type :custom + action :install +end +``` - # Google Chrome FTW (MSI installer) - windows_package "Google Chrome" do - source "https://dl-ssl.google.com/tag/s/appguid%3D%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26iid%3D%7B806F36C0-CB54-4A84-A3F3-0CF8A86575E0%7D%26lang%3Den%26browser%3D3%26usagestats%3D0%26appname%3DGoogle%2520Chrome%26needsadmin%3Dfalse/edgedl/chrome/install/GoogleChromeStandaloneEnterprise.msi" - action :install - end +Google Chrome FTW (MSI installer) +```ruby +windows_package 'Google Chrome' do + source 'https://dl-ssl.google.com/tag/s/appguid%3D%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26iid%3D%7B806F36C0-CB54-4A84-A3F3-0CF8A86575E0%7D%26lang%3Den%26browser%3D3%26usagestats%3D0%26appname%3DGoogle%2520Chrome%26needsadmin%3Dfalse/edgedl/chrome/install/GoogleChromeStandaloneEnterprise.msi' + action :install +end +``` - # remove Google Chrome (but why??) - windows_package "Google Chrome" do - action :remove - end +Remove Google Chrome +```ruby +windows_package 'Google Chrome' do + action :remove +end +``` - # remove 7-Zip - windows_package "7-Zip 9.20 (x64 edition)" do - action :remove - end +Remove 7-Zip +```ruby +windows_package '7-Zip 9.20 (x64 edition)' do + action :remove +end +``` - -windows\_printer\_port ----------------------- - -**Note** Include `recipe[powershell]` on the node's expanded run list - to ensure the powershell cookbook is downloaded to avoid circular - dependency. +### windows_printer_port Create and delete TCP/IPv4 printer ports. -### Actions - +#### Actions - :create: Create a TCIP/IPv4 printer port. This is the default action. - :delete: Delete a TCIP/IPv4 printer port -### Attribute Parameters - -- :ipv4_address: Name attribute. Required. IPv4 address, e.g. "10.0.24.34" -- :port_name: Port name. Optional. Defaults to "IP_" + :ipv4_address +#### Attribute Parameters +- :ipv4_address: Name attribute. Required. IPv4 address, e.g. '10.0.24.34' +- :port_name: Port name. Optional. Defaults to 'IP_' + :ipv4_address - :port_number: Port number. Optional. Defaults to 9100. - :port_description: Port description. Optional. - :snmp_enabled: Boolean. Optional. Defaults to false. - :port_protocol: Port protocol, 1 (RAW), or 2 (LPR). Optional. Defaults to 1. -### Examples +#### Examples - # simplest example. Creates a TCP/IP printer port named "IP_10.4.64.37" - # with all defaults - windows_printer_port '10.4.64.37' do - end +Create a TCP/IP printer port named 'IP_10.4.64.37' with all defaults +```ruby +windows_printer_port '10.4.64.37' do +end +``` - # delete a printer port - windows_printer_port '10.4.64.37' do - action :delete - end +Delete a printer port +```ruby +windows_printer_port '10.4.64.37' do + action :delete +end +``` - # delete a port with a custom port_name - windows_printer_port '10.4.64.38' do - port_name "My awesome port" - action :delete - end +Delete a port with a custom port_name +```ruby +windows_printer_port '10.4.64.38' do + port_name 'My awesome port' + action :delete +end +``` - # Create a port with more options - windows_printer_port '10.4.64.39' do - port_name "My awesome port" - snmp_enabled true - port_protocol 2 - end +Create a port with more options +```ruby +windows_printer_port '10.4.64.39' do + port_name 'My awesome port' + snmp_enabled true + port_protocol 2 +end +``` - -windows\_printer ----------------- - -**Note** Include `recipe[powershell]` on the node's expanded run list - to ensure the powershell cookbook is downloaded to avoid circular - dependency. +### windows_printer Create Windows printer. Note that this doesn't currently install a printer driver. You must already have the driver installed on the system. The Windows Printer LWRP will automatically create a TCP/IP printer port for you using the `ipv4_address` property. If you want more granular control over the printer port, just create it using the `windows_printer_port` LWRP before creating the printer. -### Actions - +#### Actions - :create: Create a new printer - :delete: Delete a new printer -### Attribute Parameters - -- :device_id: Name attribute. Required. Printer queue name, e.g. "HP LJ 5200 in fifth floor copy room" +#### Attribute Parameters +- :device_id: Name attribute. Required. Printer queue name, e.g. 'HP LJ 5200 in fifth floor copy room' - :comment: Optional string describing the printer queue. - :default: Boolean. Optional. Defaults to false. Note that Windows sets the first printer defined to the default printer regardless of this setting. - :driver_name: String. Required. Exact name of printer driver. Note that the printer driver must already be installed on the node. -- :location: Printer location, e.g. "Fifth floor copy room", or "US/NYC/Floor42/Room4207" +- :location: Printer location, e.g. 'Fifth floor copy room', or 'US/NYC/Floor42/Room4207' - :shared: Boolean. Defaults to false. - :share_name: Printer share name. -- :ipv4_address: Printer IPv4 address, e.g. "10.4.64.23". You don't have to be able to ping the IP addresss to set it. Required. +- :ipv4_address: Printer IPv4 address, e.g. '10.4.64.23'. You don't have to be able to ping the IP addresss to set it. Required. +An error of "Set-WmiInstance : Generic failure" is most likely due to the printer driver name not matching or not being installed. -### Examples +#### Examples - # create a printer - windows_printer 'HP LaserJet 5th Floor' do - driver_name 'HP LaserJet 4100 Series PCL6' - ipv4_address '10.4.64.38' - end +Create a printer +```ruby +windows_printer 'HP LaserJet 5th Floor' do + driver_name 'HP LaserJet 4100 Series PCL6' + ipv4_address '10.4.64.38' +end +``` - # delete a printer - # Note: this doesn't delete the associated printer port. - # See `windows_printer_port` above for how to delete the port. - windows_printer 'HP LaserJet 5th Floor' do - action :delete - end - - -windows\_reboot ---------------- +Delete a printer. Note: this doesn't delete the associated printer port. See `windows_printer_port` above for how to delete the port. +```ruby +windows_printer 'HP LaserJet 5th Floor' do + action :delete +end +``` +### windows_reboot Sets required data in the node's run_state to notify `WindowsRebootHandler` a reboot is requested. If Chef run completes successfully a reboot will occur if the `WindowsRebootHandler` is properly registered as a report handler. As an action of `:request` will cause a node to reboot every Chef run, this resource is usually notified by other resources...ie restart node after a package is installed (see example below). -### Actions - +#### Actions - :request: requests a reboot at completion of successful Cher run. requires `WindowsRebootHandler` to be registered as a report handler. - :cancel: remove reboot request from node.run_state. this will cancel *ALL* previously requested reboots as this is a binary state. -### Attribute Parameters - +#### Attribute Parameters - :timeout: Name attribute. timeout delay in seconds to wait before proceeding with the requested reboot. default is 60 seconds - :reason: comment on the reason for the reboot. default is 'Opscode Chef initiated reboot' -### Examples +#### Examples +If the package installs, schedule a reboot at end of chef run +```ruby +windows_reboot 60 do + reason 'cause chef said so' + action :nothing +end - # if the package installs, schedule a reboot at end of chef run - windows_reboot 60 do - reason 'cause chef said so' - action :nothing - end - windows_package 'some_package' do - action :install - notifies :request, 'windows_reboot[60]' - end +windows_package 'some_package' do + action :install + notifies :request, 'windows_reboot[60]' +end +``` - # cancel the previously requested reboot - windows_reboot 60 do - action :cancel - end +Cancel the previously requested reboot +```ruby +windows_reboot 60 do + action :cancel +end +``` -windows\_registry ------------------ +### windows_registry +(Chef 11.6.0 includes a built-in [registry_key](http://docs.opscode.com/resource_registry_key.html) resource, so use that in preference to `windows_registry` if possible.) Creates and modifies Windows registry keys. *Change in v1.3.0: The Win32 classes use `::Win32` to avoid namespace conflict with `Chef::Win32` (introduced in Chef 0.10.10).* -### Actions - +#### Actions - :create: create a new registry key with the provided values. - :modify: modify an existing registry key with the provided values. - :force_modify: modify an existing registry key with the provided values. ensures the value is actually set by checking multiple times. useful for fighting race conditions where two processes are trying to set the same registry key. This will be updated in the near future to use 'RegNotifyChangeKeyValue' which is exposed by the WinAPI and allows a process to register for notification on a registry key change. - :remove: removes a value from an existing registry key -### Attribute Parameters - +#### Attribute Parameters - key_name: name attribute. The registry key to create/modify. - values: hash of the values to set under the registry key. The individual hash items will become respective 'Value name' => 'Value data' items in the registry key. - type: Type of key to create, defaults to REG_SZ. Must be a symbol, see the overview below for valid values. -### Registry key types - +#### Registry key types - :binary: REG_BINARY - :string: REG_SZ - :multi_string: REG_MULTI_SZ @@ -399,77 +406,83 @@ Creates and modifies Windows registry keys. - :dword_big_endian: REG_DWORD_BIG_ENDIAN - :qword: REG_QWORD -### Examples +#### Examples - # make the local windows proxy match the one set for Chef - proxy = URI.parse(Chef::Config[:http_proxy]) - windows_registry 'HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings' do - values 'ProxyEnable' => 1, 'ProxyServer' => "#{proxy.host}:#{proxy.port}", 'ProxyOverride' => '' - end +Make the local windows proxy match the one set for Chef +```ruby +proxy = URI.parse(Chef::Config[:http_proxy]) +windows_registry 'HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings' do + values 'ProxyEnable' => 1, 'ProxyServer' => "#{proxy.host}:#{proxy.port}", 'ProxyOverride' => '' +end +``` - # enable Remote Desktop and poke the firewall hole - windows_registry 'HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server' do - values 'FdenyTSConnections' => 0 - end +Enable Remote Desktop and poke the firewall hole +```ruby +windows_registry 'HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server' do + values 'FdenyTSConnections' => 0 +end +``` - # Delete an item from the registry - windows_registry 'HKCU\Software\Test' do - #Key is the name of the value that you want to delete the value is always empty - values 'ValueToDelete' => '' - action :remove - end +Delete an item from the registry +```ruby +windows_registry 'HKCU\Software\Test' do + #Key is the name of the value that you want to delete the value is always empty + values 'ValueToDelete' => '' + action :remove +end +``` - # Add a REG_MULTI_SZ value to the registry - windows_registry 'HKCU\Software\Test' do - values 'MultiString' => ['line 1', 'line 2', 'line 3'] - type :multi_string - end +Add a REG_MULTI_SZ value to the registry +```ruby +windows_registry 'HKCU\Software\Test' do + values 'MultiString' => ['line 1', 'line 2', 'line 3'] + type :multi_string +end +``` -### Library Methods +#### Library Methods - Registry.value_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run','BGINFO') - Registry.key_exists?('HKLM\SOFTWARE\Microsoft') - BgInfo = Registry.get_value('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run','BGINFO') - -windows\_path -------------- - -### Actions +```ruby +Registry.value_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run','BGINFO') +Registry.key_exists?('HKLM\SOFTWARE\Microsoft') +BgInfo = Registry.get_value('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run','BGINFO') +``` +### windows_path +#### Actions - :add: Add an item to the system path - :remove: Remove an item from the system path -### Attribute Parameters - +#### Attribute Parameters - :path: Name attribute. The name of the value to add to the system path -### Examples +#### Examples - #Add Sysinternals to the system path - windows_path 'C:\Sysinternals' do - action :add - end +Add Sysinternals to the system path +```ruby +windows_path 'C:\Sysinternals' do + action :add +end +``` - #Remove 7-Zip from the system path - windows_path 'C:\7-Zip' do - action :remove - end - -windows\_task -------------- +Remove 7-Zip from the system path +```ruby +windows_path 'C:\7-Zip' do + action :remove +end +``` +### windows_task Creates, deletes or runs a Windows scheduled task. Requires Windows Server 2008 due to API usage. -### Actions - +#### Actions - :create: creates a task - :delete: deletes a task - :run: runs a task - :change: changes the un/pw or command of a task -### Attribute Parameters - +#### Attribute Parameters - name: name attribute, The task name. - command: The command the task will run. - cwd: The directory the task will be run from. @@ -478,123 +491,165 @@ Server 2008 due to API usage. - run_level: Run with limited or highest privileges. - frequency: Frequency with which to run the task. (hourly, daily, ect.) - frequency_modifier: Multiple for frequency. (15 minutes, 2 days) +- start_day: Specifies the first date on which the task runs. Optional string (MM/DD/YYYY) +- start_time: Specifies the start time to run the task. Optional string (HH:mm) -### Examples +#### Examples - # Run Chef every 15 minutes - windows_task "Chef client" do - user "Administrator" - password "$ecR3t" - cwd "C:\chef\bin" - command "chef-client -L C:\tmp\" - run_level :highest - frequency :minute - frequency_modifier 15 - end +Run Chef every 15 minutes +```ruby +windows_task 'Chef client' do + user 'Administrator' + password '$ecR3t' + cwd 'C:\chef\bin' + command 'chef-client -L C:\tmp\' + run_level :highest + frequency :minute + frequency_modifier 15 +end +``` - # Update Chef Client task with new password and log location - windows_task "Chef client" do - user "Administrator" - password "N3wPassW0Rd" - cwd "C:\chef\bin" - command "chef-client -L C:\chef\logs\" - action :change - end +Update Chef Client task with new password and log location +```ruby +windows_task 'Chef client' do + user 'Administrator' + password 'N3wPassW0Rd' + cwd 'C:\chef\bin' + command 'chef-client -L C:\chef\logs\' + action :change +end +``` - # Delete a taks named "old task" - windows_task "old task" do - action :delete - end - -windows\_zipfile ----------------- +Delete a taks named 'old task' +```ruby +windows_task 'old task' do + action :delete +end +``` +### windows_zipfile Most version of Windows do not ship with native cli utility for managing compressed files. This resource provides a pure-ruby implementation for managing zip files. Be sure to use the `not_if` or `only_if` meta parameters to guard the resource for idempotence or action will be taken on the zip file every Chef run. -### Actions - +#### Actions - :unzip: unzip a compressed file -### Attribute Parameters - +#### Attribute Parameters - path: name attribute. The path where files will be unzipped to. - source: The source of the zip file. This can either be a URI or a local path. - overwrite: force an overwrite of the files if the already exists. - checksum: useful if source is remote, the SHA-256 checksum of the file--if the local file matches the checksum, Chef will not download it -### Examples +#### Examples - # unzip a remote zip file locally - windows_zipfile "c:/bin" do - source "http://download.sysinternals.com/Files/SysinternalsSuite.zip" - action :unzip - not_if {::File.exists?("c:/bin/PsExec.exe")} - end +Unzip a remote zip file locally +```ruby +windows_zipfile 'c:/bin' do + source 'http://download.sysinternals.com/Files/SysinternalsSuite.zip' + action :unzip + not_if {::File.exists?('c:/bin/PsExec.exe')} +end +``` - # unzip a local zipfile - windows_zipfile "c:/the_codez" do - source "c:/foo/baz/the_codez.zip" - action :unzip - end +Unzip a local zipfile +```ruby +windows_zipfile 'c:/the_codez' do + source 'c:/foo/baz/the_codez.zip' + action :unzip +end +``` Exception/Report Handlers -========================= - -WindowsRebootHandler --------------------- - +------------------------- +### WindowsRebootHandler Required reboots are a necessary evil of configuring and managing Windows nodes. This report handler (ie fires at the end of successful Chef runs) acts on requested (Chef initiated) or pending (as determined by the OS per configuration action we performed) reboots. The `allow_pending_reboots` initialization argument should be set to false if you do not want the handler to automatically reboot a node if it has been determined a reboot is pending. Reboots can still be requested explicitly via the `windows_reboot` LWRP. -## Initialization Arguments - +### Initialization Arguments - `allow_pending_reboots`: indicator on whether the handler should act on a the Window's 'pending reboot' state. default is true - `timeout`: timeout delay in seconds to wait before proceeding with the reboot. default is 60 seconds - `reason`: comment on the reason for the reboot. default is 'Opscode Chef initiated reboot' + +Windows ChefSpec Matchers +------------------------- +The Windows cookbook includes custom [ChefSpec](https://github.com/sethvargo/chefspec) matchers you can use to test your own cookbooks that consume Windows cookbook LWRPs. + +###Example Matcher Usage +```ruby +expect(chef_run).to install_windows_package('Node.js').with( + source: 'http://nodejs.org/dist/v0.10.26/x64/node-v0.10.26-x64.msi') +``` + +###Windows Cookbook Matchers +* install_windows_package +* remove_windows_package +* install_windows_feature +* remove_windows_feature +* delete_windows_feature +* create_windows_task +* delete_windows_task +* run_windows_task +* change_windows_task +* add_windows_path +* remove_windows_path +* run_windows_batch +* set_windows_pagefile +* unzip_windows_zipfile_to +* zip_windows_zipfile_to +* create_windows_shortcut +* create_windows_auto_run +* remove_windows_auto_run +* create_windows_printer +* delete_windows_printer +* create_windows_printer_port +* delete_windows_printer_port +* request_windows_reboot +* cancel_windows_reboot +* create_windows_shortcut + + Usage -===== +----- Place an explicit dependency on this cookbook (using depends in the cookbook's metadata.rb) from any cookbook where you would like to use the Windows-specific resources/providers that ship with this cookbook. - depends "windows" - -default -------- +```ruby +depends 'windows' +``` +### default Convenience recipe that installs supporting gems for many of the resources/providers that ship with this cookbook. *Change in v1.3.0: Uses chef_gem instead of gem_package to ensure gem installation in Chef 0.10.10.* -reboot\_handler --------------- - +### reboot_handler Leverages the `chef_handler` LWRP to register the `WindowsRebootHandler` report handler that ships as part of this cookbook. By default this handler is set to automatically act on pending reboots. If you would like to change this behavior override `node['windows']['allow_pending_reboots']` and set the value to false. For example: - % cat roles/base.rb - name "base" - description "base role" - override_attributes( - "windows" => { - "allow_pending_reboots" => false - } - ) +```ruby +name 'base' +description 'base role' +override_attributes( + 'windows' => { + 'allow_pending_reboots' => false + } +) +``` This will still allow a reboot to be explicitly requested via the `windows_reboot` LWRP. -License and Author -================== -Author:: Seth Chisamore () -Author:: Doug MacEachern () -Author:: Paul Morton () -Author:: Doug Ireton () - -Copyright:: 2011, Opscode, Inc. -Copyright:: 2010, VMware, Inc. -Copyright:: 2011, Business Intelligence Associates, Inc -Copyright:: 2012, Nordstrom, Inc. +License & Authors +----------------- +- Author:: Seth Chisamore () +- Author:: Doug MacEachern () +- Author:: Paul Morton () +- Author:: Doug Ireton () +```text +Copyright 2011-2013, Opscode, Inc. +Copyright 2010, VMware, Inc. +Copyright 2011, Business Intelligence Associates, Inc +Copyright 2012, Nordstrom, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -607,3 +662,4 @@ 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. +``` diff --git a/chef/cookbooks/windows/libraries/feature_base.rb b/chef/cookbooks/windows/libraries/feature_base.rb index 66cbc42..bce3eea 100644 --- a/chef/cookbooks/windows/libraries/feature_base.rb +++ b/chef/cookbooks/windows/libraries/feature_base.rb @@ -23,6 +23,16 @@ class Chef end end + def action_delete + if available? + delete_feature(@new_resource.feature_name) + @new_resource.updated_by_last_action(true) + Chef::Log.info("#{@new_resource} deleted") + else + Chef::Log.debug("#{@new_resource} feature is not installed - nothing to do") + end + end + def install_feature(name) raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :install" end @@ -31,9 +41,17 @@ class Chef raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remove" end + def delete_feature(name) + raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :delete" + end + def installed? raise Chef::Exceptions::Override, "You must override installed? in #{self.to_s}" end + + def available? + raise Chef::Exceptions::Override, "You must override available? in #{self.to_s}" + end end end end diff --git a/chef/cookbooks/windows/libraries/matchers.rb b/chef/cookbooks/windows/libraries/matchers.rb new file mode 100644 index 0000000..7dc3f73 --- /dev/null +++ b/chef/cookbooks/windows/libraries/matchers.rb @@ -0,0 +1,451 @@ +if defined?(ChefSpec) + + # + # Assert that a +windows_package+ resource exists in the Chef run with the + # action +:install+. Given a Chef Recipe that installs "Node.js" as a + # +windows_package+: + # + # windows_package 'Node.js' do + # source 'http://nodejs.org/dist/v0.10.26/x64/node-v0.10.26-x64.msi' + # action :install + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_package+ resource with ChefSpec. + # + # @example Assert that a +windows_package+ was installed + # expect(chef_run).to install_windows_package('Node.js') + # + # @example Assert that a +windows_package+ was _not_ installed + # expect(chef_run).to_not install_windows_package('7-zip') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def install_windows_package(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_package, :install, resource_name) + end + + # + # Assert that a +windows_package+ resource exists in the Chef run with the + # action +:remove+. Given a Chef Recipe that removes "Node.js" as a + # +windows_package+: + # + # windows_package 'Node.js' do + # action :remove + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_package+ resource with ChefSpec. + # + # @example Assert that a +windows_package+ was installed + # expect(chef_run).to remove_windows_package('Node.js') + # + # @example Assert that a +windows_package+ was _not_ removed + # expect(chef_run).to_not remove_windows_package('7-zip') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def remove_windows_package(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_package, :remove, resource_name) + end + + + + # + # Assert that a +windows_feature+ resource exists in the Chef run with the + # action +:install+. Given a Chef Recipe that installs "NetFX3" as a + # +windows_feature+: + # + # windows_feature 'NetFX3' do + # action :install + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_feature+ resource with ChefSpec. + # + # @example Assert that a +windows_feature+ was installed + # expect(chef_run).to install_windows_feature('NetFX3') + # + # @example Assert that a +windows_feature+ was _not_ installed + # expect(chef_run).to_not install_windows_feature('NetFX3') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def install_windows_feature(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_feature, :install, resource_name) + end + + # + # Assert that a +windows_feature+ resource exists in the Chef run with the + # action +:remove+. Given a Chef Recipe that removes "NetFX3" as a + # +windows_feature+: + # + # windows_feature 'NetFX3' do + # action :remove + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_feature+ resource with ChefSpec. + # + # @example Assert that a +windows_feature+ was removed + # expect(chef_run).to remove_windows_feature('NetFX3') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def remove_windows_feature(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_feature, :remove, resource_name) + end + + # + # Assert that a +windows_feature+ resource exists in the Chef run with the + # action +:delete+. Given a Chef Recipe that deletes "NetFX3" as a + # +windows_feature+: + # + # windows_feature 'NetFX3' do + # action :delete + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_feature+ resource with ChefSpec. + # + # @example Assert that a +windows_feature+ was deleted + # expect(chef_run).to delete_windows_feature('NetFX3') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def delete_windows_feature(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_feature, :delete, resource_name) + end + + + + # + # Assert that a +windows_task+ resource exists in the Chef run with the + # action +:create+. Given a Chef Recipe that creates "mytask" as a + # +windows_task+: + # + # windows_task 'mytask' do + # command 'mybatch.bat' + # action :create + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_task+ resource with ChefSpec. + # + # @example Assert that a +windows_task+ was created + # expect(chef_run).to create_windows_task('mytask') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def create_windows_task(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_task, :create, resource_name) + end + + # + # Assert that a +windows_task+ resource exists in the Chef run with the + # action +:delete+. Given a Chef Recipe that deletes "mytask" as a + # +windows_task+: + # + # windows_task 'mytask' do + # action :delete + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_task+ resource with ChefSpec. + # + # @example Assert that a +windows_task+ was deleted + # expect(chef_run).to delete_windows_task('mytask') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def delete_windows_task(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_task, :delete, resource_name) + end + + # + # Assert that a +windows_task+ resource exists in the Chef run with the + # action +:run+. Given a Chef Recipe that runs "mytask" as a + # +windows_task+: + # + # windows_task 'mytask' do + # action :run + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_task+ resource with ChefSpec. + # + # @example Assert that a +windows_task+ was run + # expect(chef_run).to run_windows_task('mytask') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def run_windows_task(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_task, :run, resource_name) + end + + # + # Assert that a +windows_task+ resource exists in the Chef run with the + # action +:change+. Given a Chef Recipe that changes "mytask" as a + # +windows_task+: + # + # windows_task 'mytask' do + # action :change + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_task+ resource with ChefSpec. + # + # @example Assert that a +windows_task+ was changed + # expect(chef_run).to change_windows_task('mytask') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def change_windows_task(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_task, :change, resource_name) + end + + + + # + # Assert that a +windows_path+ resource exists in the Chef run with the + # action +:add+. Given a Chef Recipe that adds "C:\7-Zip" to the Windows + # PATH env var + # + # windows_path 'C:\7-Zip' do + # action :add + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_path+ resource with ChefSpec. + # + # @example Assert that a +windows_path+ was added + # expect(chef_run).to add_windows_path('C:\7-Zip') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def add_windows_path(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_path, :add, resource_name) + end + + # + # Assert that a +windows_path+ resource exists in the Chef run with the + # action +:remove+. Given a Chef Recipe that removes "C:\7-Zip" from the + # Windows PATH env var + # + # windows_path 'C:\7-Zip' do + # action :remove + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_path+ resource with ChefSpec. + # + # @example Assert that a +windows_path+ was removed + # expect(chef_run).to remove_windows_path('C:\7-Zip') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def remove_windows_path(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_path, :remove, resource_name) + end + + + + # + # Assert that a +windows_batch+ resource exists in the Chef run with the + # action +:run+. Given a Chef Recipe that runs a batch script + # + # windows_batch "unzip_and_move_ruby" do + # code <<-EOH + # 7z.exe x #{Chef::Config[:file_cache_path]}/ruby-1.8.7-p352-i386-mingw32.7z + # -oC:\\source -r -y + # xcopy C:\\source\\ruby-1.8.7-p352-i386-mingw32 C:\\ruby /e /y + # EOH + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_path+ resource with ChefSpec. + # + # @example Assert that a +windows_path+ was removed + # expect(chef_run).to run_windows_batch('unzip_and_move_ruby') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def run_windows_batch(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_batch, :run, resource_name) + end + + + + # + # Assert that a +windows_pagefile+ resource exists in the Chef run with the + # action +:set+. Given a Chef Recipe that sets a pagefile + # + # windows_pagefile "pagefile" do + # system_managed true + # initial_size 1024 + # maximum_size 4096 + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_pagefile+ resource with ChefSpec. + # + # @example Assert that a +windows_pagefile+ was set + # expect(chef_run).to set_windows_pagefile('pagefile').with( + # initial_size: 1024) + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def set_windows_pagefile(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_pagefile, :set, resource_name) + end + + + + # + # Assert that a +windows_zipfile+ resource exists in the Chef run with the + # action +:unzip+. Given a Chef Recipe that extracts "SysinternalsSuite.zip" + # to c:/bin + # + # windows_zipfile "c:/bin" do + # source "http://download.sysinternals.com/Files/SysinternalsSuite.zip" + # action :unzip + # not_if {::File.exists?("c:/bin/PsExec.exe")} + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_zipfile+ resource with ChefSpec. + # + # @example Assert that a +windows_zipfile+ was unzipped + # expect(chef_run).to unzip_windows_zipfile_to('c:/bin') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def unzip_windows_zipfile_to(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_zipfile, :unzip, resource_name) + end + + # + # Assert that a +windows_zipfile+ resource exists in the Chef run with the + # action +:zip+. Given a Chef Recipe that zips "c:/src" + # to c:/code.zip + # + # windows_zipfile "c:/code.zip" do + # source "c:/src" + # action :zip + # end + # + # The Examples section demonstrates the different ways to test a + # +windows_zipfile+ resource with ChefSpec. + # + # @example Assert that a +windows_zipfile+ was zipped + # expect(chef_run).to zip_windows_zipfile_to('c:/code.zip') + # + # + # @param [String, Regex] resource_name + # the name of the resource to match + # + # @return [ChefSpec::Matchers::ResourceMatcher] + # + def zip_windows_zipfile_to(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_zipfile, :zip, resource_name) + end + + + # All the other less commonly used LWRPs + def create_windows_shortcut(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_shortcut, :create, resource_name) + end + + def create_windows_auto_run(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_auto_run, :create, resource_name) + end + + def remove_windows_auto_run(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_auto_run, :remove, resource_name) + end + + def create_windows_printer(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_printer, :create, resource_name) + end + + def delete_windows_printer(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_printer, :delete, resource_name) + end + + def create_windows_printer_port(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_printer_port, :create, resource_name) + end + + def delete_windows_printer_port(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_printer_port, :delete, resource_name) + end + + def request_windows_reboot(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_reboot, :request, resource_name) + end + + def cancel_windows_reboot(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_reboot, :cancel, resource_name) + end + + def create_windows_shortcut(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:windows_shortcut, :create, resource_name) + end + +end diff --git a/chef/cookbooks/windows/libraries/powershell_helper.rb b/chef/cookbooks/windows/libraries/powershell_helper.rb new file mode 100644 index 0000000..7bc04eb --- /dev/null +++ b/chef/cookbooks/windows/libraries/powershell_helper.rb @@ -0,0 +1,59 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Library:: helper +# +# Copyright:: 2011, Opscode, Inc. +# +# 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. +# + +require 'chef/mixin/shell_out' + +module Powershell + module Helper + include Chef::Mixin::ShellOut + + def powershell_installed? + !powershell_version.nil? + end + + def interpreter + # force 64-bit powershell from 32-bit ruby process + if ::File.exist?("#{ENV['WINDIR']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe") + "#{ENV['WINDIR']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe" + elsif ::File.exist?("#{ENV['WINDIR']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe") + "#{ENV['WINDIR']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe" + else + "powershell.exe" + end + end + + def powershell_version + begin + cmd = shell_out("#{interpreter} -InputFormat none -Command \"& echo $PSVersionTable.psversion.major\"") + if cmd.stdout.empty? # PowerShell 1.0 doesn't have a $PSVersionTable + 1 + else + if cmd.stdout =~ /^(\d+)/ + $1.to_i + else + nil + end + end + rescue Errno::ENOENT + nil + end + end + end +end diff --git a/chef/cookbooks/windows/libraries/powershell_out.rb b/chef/cookbooks/windows/libraries/powershell_out.rb new file mode 100644 index 0000000..9edeb57 --- /dev/null +++ b/chef/cookbooks/windows/libraries/powershell_out.rb @@ -0,0 +1,79 @@ +class Chef + module Mixin + module PowershellOut + include Chef::Mixin::ShellOut + + begin + include Chef::Mixin::WindowsArchitectureHelper + rescue + # nothing to do, as the include will happen when windows_architecture_helper.rb + # is loaded. This is for ease of removal of that library when either + # powershell_out is core chef or powershell cookbook depends upon version + # of chef that has Chef::Mixin::WindowsArchitectureHelper in core chef + end + + def powershell_out(*command_args) + script = command_args.first + options = command_args.last.is_a?(Hash) ? command_args.last : nil + + run_command(script, options) + end + + def powershell_out!(*command_args) + cmd = powershell_out(*command_args) + cmd.error! + cmd + end + + private + def run_command(script, options) + if options && options[:architecture] + architecture = options[:architecture] + options.delete(:architecture) + else + architecture = node_windows_architecture(node) + end + + disable_redirection = wow64_architecture_override_required?(node, architecture) + + if disable_redirection + original_redirection_state = disable_wow64_file_redirection(node) + end + + command = build_command(script) + + if options + cmd = shell_out(command, options) + else + cmd = shell_out(command) + end + + if disable_redirection + restore_wow64_file_redirection(node, original_redirection_state) + end + + cmd + end + + def build_command(script) + flags = [ + # Hides the copyright banner at startup. + "-NoLogo", + # Does not present an interactive prompt to the user. + "-NonInteractive", + # Does not load the Windows PowerShell profile. + "-NoProfile", + # always set the ExecutionPolicy flag + # see http://technet.microsoft.com/en-us/library/ee176961.aspx + "-ExecutionPolicy RemoteSigned", + # Powershell will hang if STDIN is redirected + # http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected + "-InputFormat None" + ] + + command = "powershell.exe #{flags.join(' ')} -Command \"#{script}\"" + command + end + end + end +end diff --git a/chef/cookbooks/windows/libraries/version.rb b/chef/cookbooks/windows/libraries/version.rb index 512e14a..5c5a2cc 100644 --- a/chef/cookbooks/windows/libraries/version.rb +++ b/chef/cookbooks/windows/libraries/version.rb @@ -71,7 +71,6 @@ module Windows SM_SERVERR2 = 89.freeze unless defined?(SM_SERVERR2) # http://msdn.microsoft.com/en-us/library/ms724358(v=vs.85).aspx - # this is what it sounds like...when kittens die SKU = { 0x00000006 => {:ms_const => 'PRODUCT_BUSINESS', :name => 'Business'}, 0x00000010 => {:ms_const => 'PRODUCT_BUSINESS_N', :name => 'Business N'}, diff --git a/chef/cookbooks/windows/libraries/windows_architecture_helper.rb b/chef/cookbooks/windows/libraries/windows_architecture_helper.rb new file mode 100644 index 0000000..697a264 --- /dev/null +++ b/chef/cookbooks/windows/libraries/windows_architecture_helper.rb @@ -0,0 +1,86 @@ +# Try to include from core chef, if error then monkey patch it in. + +begin + include Chef::Mixin::WindowsArchitectureHelper +rescue + Chef::Log.debug("Chef::Mixin::WindowsArchitectureHelper not in core version, Monkey patching in.") + + require 'chef/exceptions' + require 'win32/api' if Chef::Platform.windows? + + class Chef + module Mixin + module WindowsArchitectureHelper + + def node_windows_architecture(node) + node['kernel']['machine'].to_sym + end + + def wow64_architecture_override_required?(node, desired_architecture) + is_i386_windows_process? && + node_windows_architecture(node) == :x86_64 && + desired_architecture == :x86_64 + end + + def node_supports_windows_architecture?(node, desired_architecture) + assert_valid_windows_architecture!(desired_architecture) + return (node_windows_architecture(node) == :x86_64 || + desired_architecture == :i386) ? true : false + end + + def valid_windows_architecture?(architecture) + return (architecture == :x86_64) || (architecture == :i386) + end + + def assert_valid_windows_architecture!(architecture) + if ! valid_windows_architecture?(architecture) + raise Chef::Exceptions::Win32ArchitectureIncorrect, + "The specified architecture was not valid. It must be one of :i386 or :x86_64" + end + end + + def is_i386_windows_process? + Chef::Platform.windows? && 'X86'.casecmp(ENV['PROCESSOR_ARCHITECTURE']) == 0 + end + + def disable_wow64_file_redirection(node) + original_redirection_state = ['0'].pack('P') + + if ((node_windows_architecture(node) == :x86_64) && ::Chef::Platform.windows?) + win32_wow_64_disable_wow_64_fs_redirection = + ::Win32::API.new('Wow64DisableWow64FsRedirection', 'P', 'L', 'kernel32') + + succeeded = win32_wow_64_disable_wow_64_fs_redirection.call(original_redirection_state) + + if succeeded == 0 + raise Win32APIError "Failed to disable Wow64 file redirection" + end + + end + + original_redirection_state + end + + def restore_wow64_file_redirection(node, original_redirection_state) + if ( (node_windows_architecture(node) == :x86_64) && ::Chef::Platform.windows?) + win32_wow_64_revert_wow_64_fs_redirection = + ::Win32::API.new('Wow64RevertWow64FsRedirection', 'P', 'L', 'kernel32') + + succeeded = win32_wow_64_revert_wow_64_fs_redirection.call(original_redirection_state) + + if succeeded == 0 + raise Win32APIError "Failed to revert Wow64 file redirection" + end + end + end + end + end + end +end + +# Making sure this library is available to Chef::Mixin::PowershellOut +# Required for clients that don't have Chef::Mixin::WindowsArchitectureHelper in +# core chef. +if ::Chef::Platform.windows? + Chef::Mixin::PowershellOut.send(:include, Chef::Mixin::WindowsArchitectureHelper) +end diff --git a/chef/cookbooks/windows/libraries/helper.rb b/chef/cookbooks/windows/libraries/windows_helper.rb similarity index 94% rename from chef/cookbooks/windows/libraries/helper.rb rename to chef/cookbooks/windows/libraries/windows_helper.rb index 821d3df..9161e10 100644 --- a/chef/cookbooks/windows/libraries/helper.rb +++ b/chef/cookbooks/windows/libraries/windows_helper.rb @@ -16,7 +16,7 @@ # 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. -# + require 'uri' module Windows @@ -65,9 +65,9 @@ module Windows def cached_file(source, checksum=nil, windows_path=true) @installer_file_path ||= begin - if source =~ ::URI::ABS_URI && %w[http https].include?(URI.parse(source).scheme) - uri = ::URI.parse(::URI.unescape(source)) - cache_file_path = "#{Chef::Config[:file_cache_path]}/#{::File.basename(uri.path)}" + if source =~ ::URI::ABS_URI && %w[ftp http https].include?(URI.parse(source).scheme) + uri = ::URI.parse(source) + cache_file_path = "#{Chef::Config[:file_cache_path]}/#{::File.basename(::URI.unescape(uri.path))}" Chef::Log.debug("Caching a copy of file #{source} at #{cache_file_path}") r = Chef::Resource::RemoteFile.new(cache_file_path, run_context) r.source(source) diff --git a/chef/cookbooks/windows/metadata.json b/chef/cookbooks/windows/metadata.json new file mode 100644 index 0000000..806d738 --- /dev/null +++ b/chef/cookbooks/windows/metadata.json @@ -0,0 +1,31 @@ +{ + "name": "windows", + "version": "1.31.0", + "description": "Provides a set of useful Windows-specific primitives.", + "long_description": "Windows Cookbook\n================\nProvides a set of Windows-specific primitives (Chef resources) meant to aid in the creation of cookbooks/recipes targeting the Windows platform.\n\n\nRequirements\n-------------\nVersion 1.3.0+ of this cookbook requires Chef 0.10.10+.\n\n\n### Platforms\n* Windows XP\n* Windows Vista\n* Windows Server 2003 R2\n* Windows 7\n* Windows Server 2008 (R1, R2)\n\nThe `windows_task` LWRP requires Windows Server 2008 due to its API usage.\n\n### Cookbooks\nThe following cookbooks provided by Opscode are required as noted:\n\n* chef_handler (`windows::reboot_handler` leverages the chef_handler LWRP)\n\nAttributes\n----------\n* `node['windows']['allow_pending_reboots']` - used to configure the `WindowsRebootHandler` (via the `windows::reboot_handler` recipe) to act on pending reboots. default is true (ie act on pending reboots). The value of this attribute only has an effect if the `windows::reboot_handler` is in a node's run list.\n\n\nResource/Provider\n-----------------\n### windows_auto_run\n#### Actions\n- :create: Create an item to be run at login\n- :remove: Remove an item that was previously setup to run at login\n\n#### Attribute Parameters\n- :name: Name attribute. The name of the value to be stored in the registry\n- :program: The program to be run at login\n- :args: The arguments for the program\n\n#### Examples\nRun BGInfo at login\n\n```ruby\nwindows_auto_run 'BGINFO' do\n program 'C:/Sysinternals/bginfo.exe'\n args '\\'C:/Sysinternals/Config.bgi\\' /NOLICPROMPT /TIMER:0'\n not_if { Registry.value_exists?(AUTO_RUN_KEY, 'BGINFO') }\n action :create\nend\n```\n\n### windows_batch\n(Chef 11.6.0 includes a built-in [batch](http://docs.opscode.com/resource_batch.html) resource, so use that in preference to `windows_batch` if possible.)\n\nExecute a batch script using the cmd.exe interpreter (much like the script resources for bash, csh, powershell, perl, python and ruby). A temporary file is created and executed like other script resources, rather than run inline. By their nature, Script resources are not idempotent, as they are completely up to the user's imagination. Use the `not_if` or `only_if` meta parameters to guard the resource for idempotence.\n\n#### Actions\n- :run: run the batch file\n\n#### Attribute Parameters\n- command: name attribute. Name of the command to execute.\n- code: quoted string of code to execute.\n- creates: a file this command creates - if the file exists, the command will not be run.\n- cwd: current working directory to run the command from.\n- flags: command line flags to pass to the interpreter when invoking.\n- user: A user name or user ID that we should change to before running this command.\n- group: A group name or group ID that we should change to before running this command.\n\n#### Examples\n```ruby\nwindows_batch 'unzip_and_move_ruby' do\n code <<-EOH\n 7z.exe x #{Chef::Config[:file_cache_path]}/ruby-1.8.7-p352-i386-mingw32.7z -oC:\\\\source -r -y\n xcopy C:\\\\source\\\\ruby-1.8.7-p352-i386-mingw32 C:\\\\ruby /e /y\n EOH\nend\n```\n\n```ruby\nwindows_batch 'echo some env vars' do\n code <<-EOH\n echo %TEMP%\n echo %SYSTEMDRIVE%\n echo %PATH%\n echo %WINDIR%\n EOH\nend\n```\n\n### windows_feature\nWindows Roles and Features can be thought of as built-in operating system packages that ship with the OS. A server role is a set of software programs that, when they are installed and properly configured, lets a computer perform a specific function for multiple users or other computers within a network. A Role can have multiple Role Services that provide functionality to the Role. Role services are software programs that provide the functionality of a role. Features are software programs that, although they are not directly parts of roles, can support or augment the functionality of one or more roles, or improve the functionality of the server, regardless of which roles are installed. Collectively we refer to all of these attributes as 'features'.\n\nThis resource allows you to manage these 'features' in an unattended, idempotent way.\n\nThere are two providers for the `windows_features` which map into Microsoft's two major tools for managing roles/features: [Deployment Image Servicing and Management (DISM)](http://msdn.microsoft.com/en-us/library/dd371719%28v=vs.85%29.aspx) and [Servermanagercmd](http://technet.microsoft.com/en-us/library/ee344834%28WS.10%29.aspx) (The CLI for Server Manager). As Servermanagercmd is deprecated, Chef will set the default provider to `Chef::Provider::WindowsFeature::DISM` if DISM is present on the system being configured. The default provider will fall back to `Chef::Provider::WindowsFeature::ServerManagerCmd`.\n\nFor more information on Roles, Role Services and Features see the [Microsoft TechNet article on the topic](http://technet.microsoft.com/en-us/library/cc754923.aspx). For a complete list of all features that are available on a node type either of the following commands at a command prompt:\n\n```text\ndism /online /Get-Features\nservermanagercmd -query\n```\n\n#### Actions\n- :install: install a Windows role/feature\n- :remove: remove a Windows role/feature\n\n#### Attribute Parameters\n- feature_name: name of the feature/role to install. The same feature may have different names depending on the provider used (ie DHCPServer vs DHCP; DNS-Server-Full-Role vs DNS).\n- all: Boolean. Optional. Default: false. DISM provider only. Forces all dependencies to be installed.\n- source: String. Optional. DISM provider only. Uses local repository for feature install.\n\n#### Providers\n- **Chef::Provider::WindowsFeature::DISM**: Uses Deployment Image Servicing and Management (DISM) to manage roles/features.\n- **Chef::Provider::WindowsFeature::ServerManagerCmd**: Uses Server Manager to manage roles/features.\n\n#### Examples\nEnable the node as a DHCP Server\n\n```ruby\nwindows_feature 'DHCPServer' do\n action :install\nend\n```\n\nEnable TFTP\n\n```ruby\nwindows_feature 'TFTP' do\n action :install\nend\n```\n\nEnable .Net 3.5.1 on Server 2012 using repository files on DVD and\ninstall all dependencies\n\n```ruby\nwindows_feature \"NetFx3\" do\n action :install\n all true\n source \"d:\\sources\\sxs\"\nend\n```\n\nDisable Telnet client/server\n\n```ruby\n%w[TelnetServer TelnetClient].each do |feature|\n windows_feature feature do\n action :remove\n end\nend\n```\n\n### windows_package\nManage Windows application packages in an unattended, idempotent way.\n\nThe following application installers are currently supported:\n\n* MSI packages\n* InstallShield\n* Wise InstallMaster\n* Inno Setup\n* Nullsoft Scriptable Install System\n\nIf the proper installer type is not passed into the resource's installer_type attribute, the provider will do it's best to identify the type by introspecting the installation package. If the installation type cannot be properly identified the `:custom` value can be passed into the installer_type attribute along with the proper flags for silent/quiet installation (using the `options` attribute..see example below).\n\n__PLEASE NOTE__ - For proper idempotence the resource's `package_name` should be the same as the 'DisplayName' registry value in the uninstallation data that is created during package installation. The easiest way to definitively find the proper 'DisplayName' value is to install the package on a machine and search for the uninstall information under the following registry keys:\n\n* `HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall`\n* `HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall`\n* `HKEY_LOCAL_MACHINE\\Software\\Wow6464Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall`\n\nFor maximum flexibility the `source` attribute supports both remote and local installation packages.\n\n#### Actions\n- :install: install a package\n- :remove: remove a package. The remove action is completely hit or miss as many application uninstallers do not support a full silent/quiet mode.\n\n#### Attribute Parameters\n- package_name: name attribute. The 'DisplayName' of the application installation package.\n- source: The source of the windows installer. This can either be a URI or a local path.\n- installer_type: They type of windows installation package. valid values are: :msi, :inno, :nsis, :wise, :installshield, :custom. If this value is not provided, the provider will do it's best to identify the installer type through introspection of the file.\n- checksum: useful if source is remote, the SHA-256 checksum of the file--if the local file matches the checksum, Chef will not download it\n- options: Additional options to pass the underlying installation command\n- timeout: set a timeout for the package download (default 600 seconds)\n- version: The version number of this package, as indicated by the 'DisplayVersion' value in one of the 'Uninstall' registry keys. If the given version number does equal the 'DisplayVersion' in the registry, the package will be installed.\n- success_codes: set an array of possible successful installation\n return codes. Previously this was hardcoded, but certain MSIs may\n have a different return code, e.g. 3010 for reboot required. Must be\n an array, and defaults to `[0, 42, 127]`.\n\n#### Examples\n\nInstall PuTTY (InnoSetup installer)\n```ruby\nwindows_package 'PuTTY version 0.60' do\n source 'http://the.earth.li/~sgtatham/putty/latest/x86/putty-0.60-installer.exe'\n installer_type :inno\n action :install\nend\n```\n\nInstall 7-Zip (MSI installer)\n```ruby\nwindows_package '7-Zip 9.20 (x64 edition)' do\n source 'http://downloads.sourceforge.net/sevenzip/7z920-x64.msi'\n action :install\nend\n```\n\nInstall Notepad++ (Y U No Emacs?) using a local installer\n```ruby\nwindows_package 'Notepad++' do\n source 'c:/installation_files/npp.5.9.2.Installer.exe'\n action :install\nend\n```\n\nInstall VLC for that Xvid (NSIS installer)\n```ruby\nwindows_package 'VLC media player 1.1.10' do\n source 'http://superb-sea2.dl.sourceforge.net/project/vlc/1.1.10/win32/vlc-1.1.10-win32.exe'\n action :install\nend\n```\n\nInstall Firefox as custom installer and manually set the silent install flags\n```ruby\nwindows_package 'Mozilla Firefox 5.0 (x86 en-US)' do\n source 'http://archive.mozilla.org/pub/mozilla.org/mozilla.org/firefox/releases/5.0/win32/en-US/Firefox%20Setup%205.0.exe'\n options '-ms'\n installer_type :custom\n action :install\nend\n```\n\nGoogle Chrome FTW (MSI installer)\n```ruby\nwindows_package 'Google Chrome' do\n source 'https://dl-ssl.google.com/tag/s/appguid%3D%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26iid%3D%7B806F36C0-CB54-4A84-A3F3-0CF8A86575E0%7D%26lang%3Den%26browser%3D3%26usagestats%3D0%26appname%3DGoogle%2520Chrome%26needsadmin%3Dfalse/edgedl/chrome/install/GoogleChromeStandaloneEnterprise.msi'\n action :install\nend\n```\n\nRemove Google Chrome\n```ruby\nwindows_package 'Google Chrome' do\n action :remove\nend\n```\n\nRemove 7-Zip\n```ruby\nwindows_package '7-Zip 9.20 (x64 edition)' do\n action :remove\nend\n```\n\n### windows_printer_port\n\nCreate and delete TCP/IPv4 printer ports.\n\n#### Actions\n- :create: Create a TCIP/IPv4 printer port. This is the default action.\n- :delete: Delete a TCIP/IPv4 printer port\n\n#### Attribute Parameters\n- :ipv4_address: Name attribute. Required. IPv4 address, e.g. '10.0.24.34'\n- :port_name: Port name. Optional. Defaults to 'IP_' + :ipv4_address\n- :port_number: Port number. Optional. Defaults to 9100.\n- :port_description: Port description. Optional.\n- :snmp_enabled: Boolean. Optional. Defaults to false.\n- :port_protocol: Port protocol, 1 (RAW), or 2 (LPR). Optional. Defaults to 1.\n\n#### Examples\n\nCreate a TCP/IP printer port named 'IP_10.4.64.37' with all defaults\n```ruby\nwindows_printer_port '10.4.64.37' do\nend\n```\n\nDelete a printer port\n```ruby\nwindows_printer_port '10.4.64.37' do\n action :delete\nend\n```\n\nDelete a port with a custom port_name\n```ruby\nwindows_printer_port '10.4.64.38' do\n port_name 'My awesome port'\n action :delete\nend\n```\n\nCreate a port with more options\n```ruby\nwindows_printer_port '10.4.64.39' do\n port_name 'My awesome port'\n snmp_enabled true\n port_protocol 2\nend\n```\n\n### windows_printer\n\nCreate Windows printer. Note that this doesn't currently install a printer\ndriver. You must already have the driver installed on the system.\n\nThe Windows Printer LWRP will automatically create a TCP/IP printer port for you using the `ipv4_address` property. If you want more granular control over the printer port, just create it using the `windows_printer_port` LWRP before creating the printer.\n\n#### Actions\n- :create: Create a new printer\n- :delete: Delete a new printer\n\n#### Attribute Parameters\n- :device_id: Name attribute. Required. Printer queue name, e.g. 'HP LJ 5200 in fifth floor copy room'\n- :comment: Optional string describing the printer queue.\n- :default: Boolean. Optional. Defaults to false. Note that Windows sets the first printer defined to the default printer regardless of this setting.\n- :driver_name: String. Required. Exact name of printer driver. Note that the printer driver must already be installed on the node.\n- :location: Printer location, e.g. 'Fifth floor copy room', or 'US/NYC/Floor42/Room4207'\n- :shared: Boolean. Defaults to false.\n- :share_name: Printer share name.\n- :ipv4_address: Printer IPv4 address, e.g. '10.4.64.23'. You don't have to be able to ping the IP addresss to set it. Required.\n\nAn error of \"Set-WmiInstance : Generic failure\" is most likely due to the printer driver name not matching or not being installed.\n\n#### Examples\n\nCreate a printer\n```ruby\nwindows_printer 'HP LaserJet 5th Floor' do\n driver_name 'HP LaserJet 4100 Series PCL6'\n ipv4_address '10.4.64.38'\nend\n```\n\nDelete a printer. Note: this doesn't delete the associated printer port. See `windows_printer_port` above for how to delete the port.\n```ruby\nwindows_printer 'HP LaserJet 5th Floor' do\n action :delete\nend\n```\n\n### windows_reboot\nSets required data in the node's run_state to notify `WindowsRebootHandler` a reboot is requested. If Chef run completes successfully a reboot will occur if the `WindowsRebootHandler` is properly registered as a report handler. As an action of `:request` will cause a node to reboot every Chef run, this resource is usually notified by other resources...ie restart node after a package is installed (see example below).\n\n#### Actions\n- :request: requests a reboot at completion of successful Cher run. requires `WindowsRebootHandler` to be registered as a report handler.\n- :cancel: remove reboot request from node.run_state. this will cancel *ALL* previously requested reboots as this is a binary state.\n\n#### Attribute Parameters\n- :timeout: Name attribute. timeout delay in seconds to wait before proceeding with the requested reboot. default is 60 seconds\n- :reason: comment on the reason for the reboot. default is 'Opscode Chef initiated reboot'\n\n#### Examples\nIf the package installs, schedule a reboot at end of chef run\n```ruby\nwindows_reboot 60 do\n reason 'cause chef said so'\n action :nothing\nend\n\nwindows_package 'some_package' do\n action :install\n notifies :request, 'windows_reboot[60]'\nend\n```\n\nCancel the previously requested reboot\n```ruby\nwindows_reboot 60 do\n action :cancel\nend\n```\n\n### windows_registry\n(Chef 11.6.0 includes a built-in [registry_key](http://docs.opscode.com/resource_registry_key.html) resource, so use that in preference to `windows_registry` if possible.)\n\nCreates and modifies Windows registry keys.\n\n*Change in v1.3.0: The Win32 classes use `::Win32` to avoid namespace conflict with `Chef::Win32` (introduced in Chef 0.10.10).*\n\n#### Actions\n- :create: create a new registry key with the provided values.\n- :modify: modify an existing registry key with the provided values.\n- :force_modify: modify an existing registry key with the provided values. ensures the value is actually set by checking multiple times. useful for fighting race conditions where two processes are trying to set the same registry key. This will be updated in the near future to use 'RegNotifyChangeKeyValue' which is exposed by the WinAPI and allows a process to register for notification on a registry key change.\n- :remove: removes a value from an existing registry key\n\n#### Attribute Parameters\n- key_name: name attribute. The registry key to create/modify.\n- values: hash of the values to set under the registry key. The individual hash items will become respective 'Value name' => 'Value data' items in the registry key.\n- type: Type of key to create, defaults to REG_SZ. Must be a symbol, see the overview below for valid values.\n\n#### Registry key types\n- :binary: REG_BINARY\n- :string: REG_SZ\n- :multi_string: REG_MULTI_SZ\n- :expand_string: REG_EXPAND_SZ\n- :dword: REG_DWORD\n- :dword_big_endian: REG_DWORD_BIG_ENDIAN\n- :qword: REG_QWORD\n\n#### Examples\n\nMake the local windows proxy match the one set for Chef\n```ruby\nproxy = URI.parse(Chef::Config[:http_proxy])\nwindows_registry 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' do\n values 'ProxyEnable' => 1, 'ProxyServer' => \"#{proxy.host}:#{proxy.port}\", 'ProxyOverride' => ''\nend\n```\n\nEnable Remote Desktop and poke the firewall hole\n```ruby\nwindows_registry 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server' do\n values 'FdenyTSConnections' => 0\nend\n```\n\nDelete an item from the registry\n```ruby\nwindows_registry 'HKCU\\Software\\Test' do\n #Key is the name of the value that you want to delete the value is always empty\n values 'ValueToDelete' => ''\n action :remove\nend\n```\n\nAdd a REG_MULTI_SZ value to the registry\n```ruby\nwindows_registry 'HKCU\\Software\\Test' do\n values 'MultiString' => ['line 1', 'line 2', 'line 3']\n type :multi_string\nend\n```\n\n#### Library Methods\n\n```ruby\nRegistry.value_exists?('HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run','BGINFO')\nRegistry.key_exists?('HKLM\\SOFTWARE\\Microsoft')\nBgInfo = Registry.get_value('HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run','BGINFO')\n```\n\n### windows_path\n#### Actions\n- :add: Add an item to the system path\n- :remove: Remove an item from the system path\n\n#### Attribute Parameters\n- :path: Name attribute. The name of the value to add to the system path\n\n#### Examples\n\nAdd Sysinternals to the system path\n```ruby\nwindows_path 'C:\\Sysinternals' do\n action :add\nend\n```\n\nRemove 7-Zip from the system path\n```ruby\nwindows_path 'C:\\7-Zip' do\n action :remove\nend\n```\n\n### windows_task\nCreates, deletes or runs a Windows scheduled task. Requires Windows\nServer 2008 due to API usage.\n\n#### Actions\n- :create: creates a task\n- :delete: deletes a task\n- :run: runs a task\n- :change: changes the un/pw or command of a task\n\n#### Attribute Parameters\n- name: name attribute, The task name.\n- command: The command the task will run.\n- cwd: The directory the task will be run from.\n- user: The user to run the task as. (requires password)\n- password: The user's password. (requires user)\n- run_level: Run with limited or highest privileges.\n- frequency: Frequency with which to run the task. (hourly, daily, ect.)\n- frequency_modifier: Multiple for frequency. (15 minutes, 2 days)\n- start_day: Specifies the first date on which the task runs. Optional string (MM/DD/YYYY)\n- start_time: Specifies the start time to run the task. Optional string (HH:mm)\n\n#### Examples\n\nRun Chef every 15 minutes\n```ruby\nwindows_task 'Chef client' do\n user 'Administrator'\n password '$ecR3t'\n cwd 'C:\\chef\\bin'\n command 'chef-client -L C:\\tmp\\'\n run_level :highest\n frequency :minute\n frequency_modifier 15\nend\n```\n\nUpdate Chef Client task with new password and log location\n```ruby\nwindows_task 'Chef client' do\n user 'Administrator'\n password 'N3wPassW0Rd'\n cwd 'C:\\chef\\bin'\n command 'chef-client -L C:\\chef\\logs\\'\n action :change\nend\n```\n\nDelete a taks named 'old task'\n```ruby\nwindows_task 'old task' do\n action :delete\nend\n```\n\n### windows_zipfile\nMost version of Windows do not ship with native cli utility for managing compressed files. This resource provides a pure-ruby implementation for managing zip files. Be sure to use the `not_if` or `only_if` meta parameters to guard the resource for idempotence or action will be taken on the zip file every Chef run.\n\n#### Actions\n- :unzip: unzip a compressed file\n\n#### Attribute Parameters\n- path: name attribute. The path where files will be unzipped to.\n- source: The source of the zip file. This can either be a URI or a local path.\n- overwrite: force an overwrite of the files if the already exists.\n- checksum: useful if source is remote, the SHA-256 checksum of the file--if the local file matches the checksum, Chef will not download it\n\n#### Examples\n\nUnzip a remote zip file locally\n```ruby\nwindows_zipfile 'c:/bin' do\n source 'http://download.sysinternals.com/Files/SysinternalsSuite.zip'\n action :unzip\n not_if {::File.exists?('c:/bin/PsExec.exe')}\nend\n```\n\nUnzip a local zipfile\n```ruby\nwindows_zipfile 'c:/the_codez' do\n source 'c:/foo/baz/the_codez.zip'\n action :unzip\nend\n```\n\n\nException/Report Handlers\n-------------------------\n### WindowsRebootHandler\nRequired reboots are a necessary evil of configuring and managing Windows nodes. This report handler (ie fires at the end of successful Chef runs) acts on requested (Chef initiated) or pending (as determined by the OS per configuration action we performed) reboots. The `allow_pending_reboots` initialization argument should be set to false if you do not want the handler to automatically reboot a node if it has been determined a reboot is pending. Reboots can still be requested explicitly via the `windows_reboot` LWRP.\n\n### Initialization Arguments\n- `allow_pending_reboots`: indicator on whether the handler should act on a the Window's 'pending reboot' state. default is true\n- `timeout`: timeout delay in seconds to wait before proceeding with the reboot. default is 60 seconds\n- `reason`: comment on the reason for the reboot. default is 'Opscode Chef initiated reboot'\n\n\nWindows ChefSpec Matchers\n-------------------------\nThe Windows cookbook includes custom [ChefSpec](https://github.com/sethvargo/chefspec) matchers you can use to test your own cookbooks that consume Windows cookbook LWRPs.\n\n###Example Matcher Usage\n```ruby\nexpect(chef_run).to install_windows_package('Node.js').with(\n source: 'http://nodejs.org/dist/v0.10.26/x64/node-v0.10.26-x64.msi')\n```\n\n###Windows Cookbook Matchers\n* install_windows_package\n* remove_windows_package\n* install_windows_feature\n* remove_windows_feature\n* delete_windows_feature\n* create_windows_task\n* delete_windows_task\n* run_windows_task\n* change_windows_task\n* add_windows_path\n* remove_windows_path\n* run_windows_batch\n* set_windows_pagefile\n* unzip_windows_zipfile_to\n* zip_windows_zipfile_to\n* create_windows_shortcut\n* create_windows_auto_run\n* remove_windows_auto_run\n* create_windows_printer\n* delete_windows_printer\n* create_windows_printer_port\n* delete_windows_printer_port\n* request_windows_reboot\n* cancel_windows_reboot\n* create_windows_shortcut\n\n\nUsage\n-----\n\nPlace an explicit dependency on this cookbook (using depends in the cookbook's metadata.rb) from any cookbook where you would like to use the Windows-specific resources/providers that ship with this cookbook.\n\n```ruby\ndepends 'windows'\n```\n\n### default\nConvenience recipe that installs supporting gems for many of the resources/providers that ship with this cookbook.\n\n*Change in v1.3.0: Uses chef_gem instead of gem_package to ensure gem installation in Chef 0.10.10.*\n\n### reboot_handler\nLeverages the `chef_handler` LWRP to register the `WindowsRebootHandler` report handler that ships as part of this cookbook. By default this handler is set to automatically act on pending reboots. If you would like to change this behavior override `node['windows']['allow_pending_reboots']` and set the value to false. For example:\n\n```ruby\nname 'base'\ndescription 'base role'\noverride_attributes(\n 'windows' => {\n 'allow_pending_reboots' => false\n }\n)\n```\n\nThis will still allow a reboot to be explicitly requested via the `windows_reboot` LWRP.\n\n\nLicense & Authors\n-----------------\n- Author:: Seth Chisamore ()\n- Author:: Doug MacEachern ()\n- Author:: Paul Morton ()\n- Author:: Doug Ireton ()\n\n```text\nCopyright 2011-2013, Opscode, Inc.\nCopyright 2010, VMware, Inc.\nCopyright 2011, Business Intelligence Associates, Inc\nCopyright 2012, Nordstrom, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "windows": ">= 0.0.0" + }, + "dependencies": { + "chef_handler": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + } +} \ No newline at end of file diff --git a/chef/cookbooks/windows/metadata.rb b/chef/cookbooks/windows/metadata.rb index 4dc43af..ca7a580 100644 --- a/chef/cookbooks/windows/metadata.rb +++ b/chef/cookbooks/windows/metadata.rb @@ -1,9 +1,9 @@ -name "windows" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" -description "Provides a set of useful Windows-specific primitives." +name 'windows' +maintainer 'Opscode, Inc.' +maintainer_email 'cookbooks@opscode.com' +license 'Apache 2.0' +description 'Provides a set of useful Windows-specific primitives.' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "1.10.1" -supports "windows" -depends "chef_handler" +version '1.31.0' +supports 'windows' +depends 'chef_handler' diff --git a/chef/cookbooks/windows/providers/feature_dism.rb b/chef/cookbooks/windows/providers/feature_dism.rb index 5db9d63..0d670cc 100644 --- a/chef/cookbooks/windows/providers/feature_dism.rb +++ b/chef/cookbooks/windows/providers/feature_dism.rb @@ -23,15 +23,23 @@ include Chef::Mixin::ShellOut include Windows::Helper def install_feature(name) - # return code 3010 is valid, it indicates a reboot is required - shell_out!("#{dism} /online /enable-feature /featurename:#{@new_resource.feature_name} /norestart", {:returns => [0,42,127,3010]}) + addsource = @new_resource.source ? "/LimitAccess /Source:\"#{@new_resource.source}\"" : "" + addall = @new_resource.all ? "/All" : "" + shell_out!("#{dism} /online /enable-feature /featurename:#{@new_resource.feature_name} /norestart #{addsource} #{addall}", {:returns => [0,42,127,3010]}) end def remove_feature(name) - # return code 3010 is valid, it indicates a reboot is required shell_out!("#{dism} /online /disable-feature /featurename:#{@new_resource.feature_name} /norestart", {:returns => [0,42,127,3010]}) end +def delete_feature(name) + if win_version.major_version >= 6 and win_version.minor_version >=2 + shell_out!("#{dism} /online /disable-feature /featurename:#{@new_resource.feature_name} /Remove /norestart", {:returns => [0,42,127,3010]}) + else + raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} :delete action not support on #{win_version.sku}" + end +end + def installed? @installed ||= begin cmd = shell_out("#{dism} /online /Get-Features", {:returns => [0,42,127]}) @@ -39,6 +47,13 @@ def installed? end end +def available? + @available ||= begin + cmd = shell_out("#{dism} /online /Get-Features", {:returns => [0,42,127]}) + cmd.stderr.empty? && (cmd.stdout !~ /^Feature Name : #{@new_resource.feature_name}.?$\n^State : .* with payload removed.?$/i) + end +end + private # account for File System Redirector # http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx diff --git a/chef/cookbooks/windows/providers/feature_powershell.rb b/chef/cookbooks/windows/providers/feature_powershell.rb new file mode 100644 index 0000000..44cd96c --- /dev/null +++ b/chef/cookbooks/windows/providers/feature_powershell.rb @@ -0,0 +1,38 @@ +# +# Author:: Greg Zapp () +# Cookbook Name:: windows +# Provider:: feature_powershell +# + +include Chef::Provider::WindowsFeature::Base +include Chef::Mixin::PowershellOut +include Windows::Helper + +def install_feature(name) + cmd = powershell_out("Install-WindowsFeature #{@new_resource.feature_name}") + Chef::Log.info(cmd.stdout) +end + +def remove_feature(name) + cmd = powershell_out("Uninstall-WindowsFeature #{@new_resource.feature_name}") + Chef::Log.info(cmd.stdout) +end + +def delete_feature(name) + cmd = powershell_out("Uninstall-WindowsFeature #{@new_resource.feature_name} -Remove") + Chef::Log.info(cmd.stdout) +end + +def installed? + @installed ||= begin + cmd = powershell_out("Get-WindowsFeature #{@new_resource.feature_name} | Select Installed | % { Write-Host $_.Installed }") + cmd.stderr.empty? && cmd.stdout =~ /True/i + end +end + +def available? + @available ||= begin + cmd = powershell_out("Get-WindowsFeature #{@new_resource.feature_name}") + cmd.stderr.empty? && cmd.stdout !~ /Removed/i + end +end diff --git a/chef/cookbooks/windows/providers/feature_servermanagercmd.rb b/chef/cookbooks/windows/providers/feature_servermanagercmd.rb index b43749b..467b72b 100644 --- a/chef/cookbooks/windows/providers/feature_servermanagercmd.rb +++ b/chef/cookbooks/windows/providers/feature_servermanagercmd.rb @@ -33,7 +33,7 @@ end def installed? @installed ||= begin cmd = shell_out("#{servermanagercmd} -query", {:returns => [0,42,127]}) - cmd.stderr.empty? && (cmd.stdout =~ /^\s*?\[X\]\s.+?\s\[#{@new_resource.feature_name}\]$/i) + cmd.stderr.empty? && (cmd.stdout =~ /^\s*?\[X\]\s.+?\s\[#{@new_resource.feature_name}\]\s*$/i) end end diff --git a/chef/cookbooks/windows/providers/printer.rb b/chef/cookbooks/windows/providers/printer.rb index d53e287..89cec4a 100644 --- a/chef/cookbooks/windows/providers/printer.rb +++ b/chef/cookbooks/windows/providers/printer.rb @@ -72,7 +72,7 @@ def create_printer port_name = "IP_#{ new_resource.ipv4_address }" - powershell "Creating printer: #{ new_resource.name }" do + powershell_script "Creating printer: #{ new_resource.name }" do code <<-EOH Set-WmiInstance -class Win32_Printer ` @@ -91,7 +91,7 @@ def create_printer end def delete_printer - powershell "Deleting printer: #{ new_resource.name }" do + powershell_script "Deleting printer: #{ new_resource.name }" do code <<-EOH $printer = Get-WMIObject -class Win32_Printer -EnableAllPrivileges -Filter "name = '#{ new_resource.name }'" $printer.Delete() diff --git a/chef/cookbooks/windows/providers/printer_port.rb b/chef/cookbooks/windows/providers/printer_port.rb index ab88247..fe01db5 100644 --- a/chef/cookbooks/windows/providers/printer_port.rb +++ b/chef/cookbooks/windows/providers/printer_port.rb @@ -73,7 +73,7 @@ def create_printer_port port_name = new_resource.port_name || "IP_#{ new_resource.ipv4_address }" # create the printer port using PowerShell - powershell "Creating printer port #{ new_resource.port_name }" do + powershell_script "Creating printer port #{ new_resource.port_name }" do code <<-EOH Set-WmiInstance -class Win32_TCPIPPrinterPort ` @@ -93,7 +93,7 @@ def delete_printer_port port_name = new_resource.port_name || "IP_#{ new_resource.ipv4_address }" - powershell "Deleting printer port: #{ new_resource.port_name }" do + powershell_script "Deleting printer port: #{ new_resource.port_name }" do code <<-EOH $port = Get-WMIObject -class Win32_TCPIPPrinterPort -EnableAllPrivileges -Filter "name = '#{ port_name }'" $port.Delete() diff --git a/chef/cookbooks/windows/providers/task.rb b/chef/cookbooks/windows/providers/task.rb index fbe8bd2..6b7d8e5 100644 --- a/chef/cookbooks/windows/providers/task.rb +++ b/chef/cookbooks/windows/providers/task.rb @@ -25,16 +25,17 @@ action :create do if @current_resource.exists Chef::Log.info "#{@new_resource} task already exists - nothing to do" else + if @new_resource.user and @new_resource.password.nil? then Chef::Log.debug "#{@new_resource} did not specify a password, creating task without a password" end use_force = @new_resource.force ? '/F' : '' cmd = "schtasks /Create #{use_force} /TN \"#{@new_resource.name}\" " - cmd += "/SC #{@new_resource.frequency} " + schedule = @new_resource.frequency == :on_logon ? "ONLOGON" : @new_resource.frequency + cmd += "/SC #{schedule} " cmd += "/MO #{@new_resource.frequency_modifier} " if [:minute, :hourly, :daily, :weekly, :monthly].include?(@new_resource.frequency) + cmd += "/SD \"#{@new_resource.start_day}\" " unless @new_resource.start_day.nil? + cmd += "/ST \"#{@new_resource.start_time}\" " unless @new_resource.start_time.nil? cmd += "/TR \"#{@new_resource.command}\" " - if @new_resource.user && @new_resource.password - cmd += "/RU \"#{@new_resource.user}\" /RP \"#{@new_resource.password}\" " - elsif (@new_resource.user and !@new_resource.password) || (@new_resource.password and !@new_resource.user) - Chef::Log.fatal "#{@new_resource.name}: Can't specify user or password without both!" - end + cmd += "/RU \"#{@new_resource.user}\" " if @new_resource.user + cmd += "/RP \"#{@new_resource.password}\" " if @new_resource.user and @new_resource.password cmd += "/RL HIGHEST " if @new_resource.run_level == :highest shell_out!(cmd, {:returns => [0]}) @new_resource.updated_by_last_action true diff --git a/chef/cookbooks/windows/providers/zipfile.rb b/chef/cookbooks/windows/providers/zipfile.rb index 44b3731..2760d81 100644 --- a/chef/cookbooks/windows/providers/zipfile.rb +++ b/chef/cookbooks/windows/providers/zipfile.rb @@ -28,7 +28,7 @@ action :unzip do ensure_rubyzip_gem_installed Chef::Log.debug("unzip #{@new_resource.source} => #{@new_resource.path} (overwrite=#{@new_resource.overwrite})") - Zip::ZipFile.open(cached_file(@new_resource.source, @new_resource.checksum)) do |zip| + Zip::File.open(cached_file(@new_resource.source, @new_resource.checksum)) do |zip| zip.each do |entry| path = ::File.join(@new_resource.path, entry.name) FileUtils.mkdir_p(::File.dirname(path)) @@ -57,7 +57,7 @@ action :zip do end # only supporting compression of a single directory (recursively). if ::File.directory?(@new_resource.source) - z = Zip::ZipFile.new(@new_resource.path, true) + z = Zip::File.new(@new_resource.path, true) unless @new_resource.source =~ /::File::ALT_SEPARATOR$/ @new_resource.source << ::File::ALT_SEPARATOR end @@ -80,12 +80,12 @@ end private def ensure_rubyzip_gem_installed begin - require 'zip/zip' + require 'zip' rescue LoadError Chef::Log.info("Missing gem 'rubyzip'...installing now.") chef_gem "rubyzip" do version node['windows']['rubyzipversion'] end - require 'zip/zip' + require 'zip' end end diff --git a/chef/cookbooks/windows/resources/feature.rb b/chef/cookbooks/windows/resources/feature.rb index b67c0fb..235166a 100644 --- a/chef/cookbooks/windows/resources/feature.rb +++ b/chef/cookbooks/windows/resources/feature.rb @@ -20,9 +20,11 @@ include Windows::Helper -actions :install, :remove +actions :install, :remove, :delete attribute :feature_name, :kind_of => String, :name_attribute => true +attribute :source, :kind_of => String +attribute :all, :kind_of => [ TrueClass, FalseClass ], :default => false def initialize(name, run_context=nil) super @@ -32,9 +34,11 @@ end private def locate_default_provider - if ::File.exists?(locate_sysnative_cmd('dism.exe')) + if node['windows'].attribute?(:feature_provider) + "windows_feature_#{node['windows']['feature_provider']}" + elsif ::File.exists?(locate_sysnative_cmd('dism.exe')) :windows_feature_dism elsif ::File.exists?(locate_sysnative_cmd('servermanagercmd.exe')) :windows_feature_servermanagercmd end -end \ No newline at end of file +end diff --git a/chef/cookbooks/windows/resources/registry.rb b/chef/cookbooks/windows/resources/registry.rb index 1289dbf..92b63d2 100644 --- a/chef/cookbooks/windows/resources/registry.rb +++ b/chef/cookbooks/windows/resources/registry.rb @@ -30,4 +30,5 @@ def initialize(name, run_context=nil) super @action = :modify @key_name = name + Chef::Log.warn("Please use the registry_key resource in Chef Client 11. The windows_registry LWRP is still supported for Chef Client 10, but is deprecated in future versions.") end diff --git a/chef/cookbooks/windows/resources/task.rb b/chef/cookbooks/windows/resources/task.rb index 3a696f5..867b891 100644 --- a/chef/cookbooks/windows/resources/task.rb +++ b/chef/cookbooks/windows/resources/task.rb @@ -39,6 +39,8 @@ attribute :frequency, :equal_to => [:minute, :on_logon, :onstart, :on_idle], :default => :hourly +attribute :start_day, :kind_of => String, :default => nil +attribute :start_time, :kind_of => String, :default => nil attr_accessor :exists, :status diff --git a/chef/cookbooks/xfs/.kitchen.yml b/chef/cookbooks/xfs/.kitchen.yml deleted file mode 100644 index 4a6ae2e..0000000 --- a/chef/cookbooks/xfs/.kitchen.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: ubuntu-12.04 - driver_config: - box: opscode-ubuntu-12.04 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box - run_list: - - recipe[apt] - -- name: ubuntu-10.04 - driver_config: - box: opscode-ubuntu-10.04 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box - run_list: - - recipe[apt] - -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - -suites: -- name: default - run_list: ["recipe[xfs]"] - attributes: {} diff --git a/chef/cookbooks/xfs/TESTING.md b/chef/cookbooks/xfs/TESTING.md deleted file mode 100644 index e29ff7c..0000000 --- a/chef/cookbooks/xfs/TESTING.md +++ /dev/null @@ -1,25 +0,0 @@ -This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. - -1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. -2. You must have Vagrant 1.1 installed. -3. You must have a "sane" Ruby 1.9.3 environment. - -Once the above requirements are met, install the additional requirements: - -Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. - - vagrant plugin install vagrant-berkshelf - gem install berkshelf - -Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). - - gem install test-kitchen --pre - -Install the Vagrant driver for Test Kitchen. - - gem install kitchen-vagrant - -Once the above are installed, you should be able to run Test Kitchen: - - kitchen list - kitchen test diff --git a/chef/cookbooks/xfs/metadata.json b/chef/cookbooks/xfs/metadata.json new file mode 100644 index 0000000..ffc38fa --- /dev/null +++ b/chef/cookbooks/xfs/metadata.json @@ -0,0 +1,37 @@ +{ + "name": "xfs", + "description": "Installs packages for working with XFS", + "long_description": "Description\n====\n\nInstalls packages for working with XFS filesystems.\n\nRequirements\n====\n\nTested on Ubuntu 10.04, CentOS 5.5, Amazon Linux and Scientific Linux 6.0.\n\nShould work on any Debian or Red Hat family Linux distributions that\nhave the xfs packages in a default repository.\n\nLicense and Author\n====\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009-2011, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "maintainer": "Opscode, Inc.", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + "amazon": ">= 0.0.0", + "debian": ">= 0.0.0", + "ubuntu": ">= 0.0.0", + "redhat": ">= 0.0.0", + "centos": ">= 0.0.0", + "scientific": ">= 0.0.0", + "fedora": ">= 0.0.0" + }, + "dependencies": { + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "xfs": "Installs packages for working with XFS" + }, + "version": "1.1.0" +} \ No newline at end of file diff --git a/chef/cookbooks/yum-epel/CHANGELOG.md b/chef/cookbooks/yum-epel/CHANGELOG.md new file mode 100644 index 0000000..fc2e56c --- /dev/null +++ b/chef/cookbooks/yum-epel/CHANGELOG.md @@ -0,0 +1,37 @@ +yum-epel Cookbook CHANGELOG +====================== +This file is used to list changes made in each version of the yum-centos cookbook. + +v0.3.4 (2014-02-19) +------------------- +COOK-4353 - Fixing typo in readme + + +v0.3.2 (2014-02-13) +------------------- +Updating README to explain the 'managed' parameter + + +v0.3.0 (2014-02-12) +------------------- +[COOK-4292] - Do not manage secondary repos by default + + +v0.2.0 +------ +Adding Amazon Linux support + + +v0.1.6 +------ +Fixing up attribute values for EL6 + + +v0.1.4 +------ +Adding CHANGELOG.md + + +v0.1.0 +------ +initial release diff --git a/chef/cookbooks/yum-epel/README.md b/chef/cookbooks/yum-epel/README.md new file mode 100644 index 0000000..c4550f3 --- /dev/null +++ b/chef/cookbooks/yum-epel/README.md @@ -0,0 +1,158 @@ +yum-epel Cookbook +============ + +The yum-epel cookbook takes over management of the default +repositoryids shipped with epel-release. It allows attribute +manipulation of `epel`, `epel-debuginfo`, `epel-source`, `epel-testing`, +`epel-testing-debuginfo`, and `epel-testing-source`. + +Requirements +------------ +* Chef 11 or higher +* yum cookbook version 3.0.0 or higher + +Attributes +---------- +The following attributes are set by default + +``` ruby +default['yum']['epel']['repositoryid'] = 'epel' +default['yum']['epel']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch' +default['yum']['epel']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-5&arch=$basearch' +default['yum']['epel']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +default['yum']['epel']['failovermethod'] = 'priority' +default['yum']['epel']['gpgcheck'] = true +default['yum']['epel']['enabled'] = true +default['yum']['epel']['managed'] = true +``` + +``` ruby +default['yum']['epel-debuginfo']['repositoryid'] = 'epel-debuginfo' +default['yum']['epel-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch - Debug' +default['yum']['epel-debuginfo']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch' +default['yum']['epel-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +default['yum']['epel-debuginfo']['failovermethod'] = 'priority' +default['yum']['epel-debuginfo']['gpgcheck'] = true +default['yum']['epel-debuginfo']['enabled'] = false +default['yum']['epel-debuginfo']['managed'] = false +``` + +``` ruby +default['yum']['epel-source']['repositoryid'] = 'epel-source' +default['yum']['epel-source']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch - Source' +default['yum']['epel-source']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-source-6&arch=$basearch' +default['yum']['epel-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +default['yum']['epel-source']['failovermethod'] = 'priority' +default['yum']['epel-source']['gpgcheck'] = true +default['yum']['epel-source']['enabled'] = false +default['yum']['epel-source']['managed'] = false +``` + +``` ruby +default['yum']['epel-testing']['repositoryid'] = 'epel-testing' +default['yum']['epel-testing']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch' +default['yum']['epel-testing']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-epel6&arch=$basearch' +default['yum']['epel-testing']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6r' +default['yum']['epel-testing']['failovermethod'] = 'priority' +default['yum']['epel-testing']['gpgcheck'] = true +default['yum']['epel-testing']['enabled'] = false +default['yum']['epel-testing']['managed'] = false +``` + +``` ruby +default['yum']['epel-testing-debuginfo']['repositoryid'] = 'epel-testing-debuginfo' +default['yum']['epel-testing-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch Debug' +default['yum']['epel-testing-debuginfo']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-debug-epel6&arch=$basearch' +default['yum']['epel-testing-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +default['yum']['epel-testing-debuginfo']['failovermethod'] = 'priority' +default['yum']['epel-testing-debuginfo']['gpgcheck'] = true +default['yum']['epel-testing-debuginfo']['enabled'] = false +default['yum']['epel-testing-debuginfo']['managed'] = false +``` + +``` ruby +default['yum']['epel-testing-source']['repositoryid'] = 'epel-testing-source' +default['yum']['epel-testing-source']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch Source' +default['yum']['epel-testing-source']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-source-epel6&arch=$basearch' +default['yum']['epel-testing-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +default['yum']['epel-testing-source']['failovermethod'] = 'priority' +default['yum']['epel-testing-source']['gpgcheck'] = true +default['yum']['epel-testing-source']['enabled'] = false +default['yum']['epel-testing-source']['managed'] = false +``` + +Recipes +------- +* default - Walks through node attributes and feeds a yum_resource + parameters. The following is an example a resource generated by the + recipe during compilation. + +```ruby + yum_repository 'epel' do + mirrorlist 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-5&arch=$basearch' + description 'Extra Packages for Enterprise Linux 5 - $basearch' + enabled true + gpgcheck true + gpgkey 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL' + end +``` + +Usage Example +------------- +To disable the epel repository through a Role or Environment definition + +``` +default_attributes( + :yum => { + :epel => { + :enabled => { + false + } + } + } + ) +``` + +Uncommonly used repositoryids are not managed by default. This is +speeds up integration testing pipelines by avoiding yum-cache builds +that nobody cares about. To enable the epel-testing repository with a +wrapper cookbook, place the following in a recipe: + +``` +node.default['yum']['epel-testing']['enabled'] = true +node.default['yum']['epel-testing']['managed'] = true +include_recipe 'yum-epel' +``` + +More Examples +------------- +Point the epel repositories at an internally hosted server. + +``` +node.default['yum']['epel']['enabled'] = true +node.default['yum']['epel']['mirrorlist'] = nil +node.default['yum']['epel']['baseurl'] = 'https://internal.example.com/centos/6/os/x86_64' +node.default['yum']['epel']['sslverify'] = false + +include_recipe 'yum-epel' +``` + +License & Authors +----------------- +- Author:: Sean OMeara () + +```text +Copyright:: 2011-2013 Opscode, Inc. + +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. +``` diff --git a/chef/cookbooks/yum-epel/attributes/epel-debuginfo.rb b/chef/cookbooks/yum-epel/attributes/epel-debuginfo.rb new file mode 100644 index 0000000..cc1ac65 --- /dev/null +++ b/chef/cookbooks/yum-epel/attributes/epel-debuginfo.rb @@ -0,0 +1,24 @@ +default['yum']['epel-debuginfo']['repositoryid'] = 'epel-debuginfo' + +case node['platform'] +when 'amazon' + default['yum']['epel-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch' + default['yum']['epel-debuginfo']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch' + default['yum']['epel-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +else + case node['platform_version'].to_i + when 5 + default['yum']['epel-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 5 - $basearch - Debug' + default['yum']['epel-debuginfo']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-debug-5&arch=$basearch' + default['yum']['epel-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL' + when 6 + default['yum']['epel-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch - Debug' + default['yum']['epel-debuginfo']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch' + default['yum']['epel-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' + end +end + +default['yum']['epel-debuginfo']['failovermethod'] = 'priority' +default['yum']['epel-debuginfo']['gpgcheck'] = true +default['yum']['epel-debuginfo']['enabled'] = false +default['yum']['epel-debuginfo']['managed'] = false diff --git a/chef/cookbooks/yum-epel/attributes/epel-source.rb b/chef/cookbooks/yum-epel/attributes/epel-source.rb new file mode 100644 index 0000000..e3b6c30 --- /dev/null +++ b/chef/cookbooks/yum-epel/attributes/epel-source.rb @@ -0,0 +1,24 @@ +default['yum']['epel-source']['repositoryid'] = 'epel-source' + +case node['platform'] +when 'amazon' + default['yum']['epel-source']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch' + default['yum']['epel-source']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch' + default['yum']['epel-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +else + case node['platform_version'].to_i + when 5 + default['yum']['epel-source']['description'] = 'Extra Packages for Enterprise Linux 5 - $basearch - Source' + default['yum']['epel-source']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-source-5&arch=$basearch' + default['yum']['epel-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL' + when 6 + default['yum']['epel-source']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch - Source' + default['yum']['epel-source']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-source-6&arch=$basearch' + default['yum']['epel-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' + end +end + +default['yum']['epel-source']['failovermethod'] = 'priority' +default['yum']['epel-source']['gpgcheck'] = true +default['yum']['epel-source']['enabled'] = false +default['yum']['epel-source']['managed'] = false diff --git a/chef/cookbooks/yum-epel/attributes/epel-testing-debuginfo.rb b/chef/cookbooks/yum-epel/attributes/epel-testing-debuginfo.rb new file mode 100644 index 0000000..14353dc --- /dev/null +++ b/chef/cookbooks/yum-epel/attributes/epel-testing-debuginfo.rb @@ -0,0 +1,24 @@ +default['yum']['epel-testing-debuginfo']['repositoryid'] = 'epel-testing-debuginfo' + +case node['platform'] +when 'amazon' + default['yum']['epel-testing-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch' + default['yum']['epel-testing-debuginfo']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch' + default['yum']['epel-testing-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +else + case node['platform_version'].to_i + when 5 + default['yum']['epel-testing-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 5 - Testing - $basearch Debug' + default['yum']['epel-testing-debuginfo']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=testing-debug-epel5&arch=$basearch' + default['yum']['epel-testing-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL' + when 6 + default['yum']['epel-testing-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch Debug' + default['yum']['epel-testing-debuginfo']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-debug-epel6&arch=$basearch' + default['yum']['epel-testing-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' + end +end + +default['yum']['epel-testing-debuginfo']['failovermethod'] = 'priority' +default['yum']['epel-testing-debuginfo']['gpgcheck'] = true +default['yum']['epel-testing-debuginfo']['enabled'] = false +default['yum']['epel-testing-debuginfo']['managed'] = false diff --git a/chef/cookbooks/yum-epel/attributes/epel-testing-source.rb b/chef/cookbooks/yum-epel/attributes/epel-testing-source.rb new file mode 100644 index 0000000..7f82192 --- /dev/null +++ b/chef/cookbooks/yum-epel/attributes/epel-testing-source.rb @@ -0,0 +1,24 @@ +default['yum']['epel-testing-source']['repositoryid'] = 'epel-testing-source' + +case node['platform'] +when 'amazon' + default['yum']['epel-testing-source']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch' + default['yum']['epel-testing-source']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch' + default['yum']['epel-testing-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +else + case node['platform_version'].to_i + when 5 + default['yum']['epel-testing-source']['description'] = 'Extra Packages for Enterprise Linux 5 - Testing - $basearch Source' + default['yum']['epel-testing-source']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=testing-source-epel5&arch=$basearch' + default['yum']['epel-testing-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL' + when 6 + default['yum']['epel-testing-source']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch Source' + default['yum']['epel-testing-source']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-source-epel6&arch=$basearch' + default['yum']['epel-testing-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' + end +end + +default['yum']['epel-testing-source']['failovermethod'] = 'priority' +default['yum']['epel-testing-source']['gpgcheck'] = true +default['yum']['epel-testing-source']['enabled'] = false +default['yum']['epel-testing-source']['managed'] = false diff --git a/chef/cookbooks/yum-epel/attributes/epel-testing.rb b/chef/cookbooks/yum-epel/attributes/epel-testing.rb new file mode 100644 index 0000000..d54a0f1 --- /dev/null +++ b/chef/cookbooks/yum-epel/attributes/epel-testing.rb @@ -0,0 +1,24 @@ +default['yum']['epel-testing']['repositoryid'] = 'epel-testing' + +case node['platform'] +when 'amazon' + default['yum']['epel-testing']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch' + default['yum']['epel-testing']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch' + default['yum']['epel-testing']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +else + case node['platform_version'].to_i + when 5 + default['yum']['epel-testing']['description'] = 'Extra Packages for Enterprise Linux 5 - Testing - $basearch' + default['yum']['epel-testing']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=testing-epel5&arch=$basearch' + default['yum']['epel-testing']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL' + when 6 + default['yum']['epel-testing']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch' + default['yum']['epel-testing']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-epel6&arch=$basearch' + default['yum']['epel-testing']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' + end +end + +default['yum']['epel-testing']['failovermethod'] = 'priority' +default['yum']['epel-testing']['gpgcheck'] = true +default['yum']['epel-testing']['enabled'] = false +default['yum']['epel-testing']['managed'] = false diff --git a/chef/cookbooks/yum-epel/attributes/epel.rb b/chef/cookbooks/yum-epel/attributes/epel.rb new file mode 100644 index 0000000..c859692 --- /dev/null +++ b/chef/cookbooks/yum-epel/attributes/epel.rb @@ -0,0 +1,24 @@ +default['yum']['epel']['repositoryid'] = 'epel' + +case node['platform'] +when 'amazon' + default['yum']['epel']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch' + default['yum']['epel']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch' + default['yum']['epel']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' +else + case node['platform_version'].to_i + when 5 + default['yum']['epel']['description'] = 'Extra Packages for Enterprise Linux 5 - $basearch' + default['yum']['epel']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-5&arch=$basearch' + default['yum']['epel']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL' + when 6 + default['yum']['epel']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch' + default['yum']['epel']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch' + default['yum']['epel']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' + end +end + +default['yum']['epel']['failovermethod'] = 'priority' +default['yum']['epel']['gpgcheck'] = true +default['yum']['epel']['enabled'] = true +default['yum']['epel']['managed'] = true diff --git a/chef/cookbooks/yum-epel/metadata.json b/chef/cookbooks/yum-epel/metadata.json new file mode 100644 index 0000000..03ee260 --- /dev/null +++ b/chef/cookbooks/yum-epel/metadata.json @@ -0,0 +1,30 @@ +{ + "name": "yum-epel", + "version": "0.3.4", + "description": "Installs/Configures yum-epel", + "long_description": "yum-epel Cookbook\n============\n\nThe yum-epel cookbook takes over management of the default\nrepositoryids shipped with epel-release. It allows attribute\nmanipulation of `epel`, `epel-debuginfo`, `epel-source`, `epel-testing`,\n`epel-testing-debuginfo`, and `epel-testing-source`.\n\nRequirements\n------------\n* Chef 11 or higher\n* yum cookbook version 3.0.0 or higher\n\nAttributes\n----------\nThe following attributes are set by default\n\n``` ruby\ndefault['yum']['epel']['repositoryid'] = 'epel'\ndefault['yum']['epel']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch'\ndefault['yum']['epel']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-5&arch=$basearch'\ndefault['yum']['epel']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'\ndefault['yum']['epel']['failovermethod'] = 'priority'\ndefault['yum']['epel']['gpgcheck'] = true\ndefault['yum']['epel']['enabled'] = true\ndefault['yum']['epel']['managed'] = true\n```\n\n``` ruby\ndefault['yum']['epel-debuginfo']['repositoryid'] = 'epel-debuginfo'\ndefault['yum']['epel-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch - Debug'\ndefault['yum']['epel-debuginfo']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch'\ndefault['yum']['epel-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'\ndefault['yum']['epel-debuginfo']['failovermethod'] = 'priority'\ndefault['yum']['epel-debuginfo']['gpgcheck'] = true\ndefault['yum']['epel-debuginfo']['enabled'] = false\ndefault['yum']['epel-debuginfo']['managed'] = false\n```\n\n``` ruby\ndefault['yum']['epel-source']['repositoryid'] = 'epel-source'\ndefault['yum']['epel-source']['description'] = 'Extra Packages for Enterprise Linux 6 - $basearch - Source'\ndefault['yum']['epel-source']['mirrorlist'] = 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-source-6&arch=$basearch'\ndefault['yum']['epel-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'\ndefault['yum']['epel-source']['failovermethod'] = 'priority'\ndefault['yum']['epel-source']['gpgcheck'] = true\ndefault['yum']['epel-source']['enabled'] = false\ndefault['yum']['epel-source']['managed'] = false\n```\n\n``` ruby\ndefault['yum']['epel-testing']['repositoryid'] = 'epel-testing'\ndefault['yum']['epel-testing']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch'\ndefault['yum']['epel-testing']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-epel6&arch=$basearch'\ndefault['yum']['epel-testing']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6r'\ndefault['yum']['epel-testing']['failovermethod'] = 'priority'\ndefault['yum']['epel-testing']['gpgcheck'] = true\ndefault['yum']['epel-testing']['enabled'] = false\ndefault['yum']['epel-testing']['managed'] = false\n```\n\n``` ruby\ndefault['yum']['epel-testing-debuginfo']['repositoryid'] = 'epel-testing-debuginfo'\ndefault['yum']['epel-testing-debuginfo']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch Debug'\ndefault['yum']['epel-testing-debuginfo']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-debug-epel6&arch=$basearch'\ndefault['yum']['epel-testing-debuginfo']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'\ndefault['yum']['epel-testing-debuginfo']['failovermethod'] = 'priority'\ndefault['yum']['epel-testing-debuginfo']['gpgcheck'] = true\ndefault['yum']['epel-testing-debuginfo']['enabled'] = false\ndefault['yum']['epel-testing-debuginfo']['managed'] = false\n```\n\n``` ruby\ndefault['yum']['epel-testing-source']['repositoryid'] = 'epel-testing-source'\ndefault['yum']['epel-testing-source']['description'] = 'Extra Packages for Enterprise Linux 6 - Testing - $basearch Source'\ndefault['yum']['epel-testing-source']['mirrorlist'] = 'https://mirrors.fedoraproject.org/metalink?repo=testing-source-epel6&arch=$basearch'\ndefault['yum']['epel-testing-source']['gpgkey'] = 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'\ndefault['yum']['epel-testing-source']['failovermethod'] = 'priority'\ndefault['yum']['epel-testing-source']['gpgcheck'] = true\ndefault['yum']['epel-testing-source']['enabled'] = false\ndefault['yum']['epel-testing-source']['managed'] = false\n```\n\nRecipes\n-------\n* default - Walks through node attributes and feeds a yum_resource\n parameters. The following is an example a resource generated by the\n recipe during compilation.\n\n```ruby\n yum_repository 'epel' do\n mirrorlist 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-5&arch=$basearch'\n description 'Extra Packages for Enterprise Linux 5 - $basearch'\n enabled true\n gpgcheck true\n gpgkey 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL'\n end\n```\n\nUsage Example\n-------------\nTo disable the epel repository through a Role or Environment definition\n\n```\ndefault_attributes(\n :yum => {\n :epel => {\n :enabled => {\n false\n }\n }\n }\n )\n```\n\nUncommonly used repositoryids are not managed by default. This is\nspeeds up integration testing pipelines by avoiding yum-cache builds\nthat nobody cares about. To enable the epel-testing repository with a\nwrapper cookbook, place the following in a recipe:\n\n```\nnode.default['yum']['epel-testing']['enabled'] = true\nnode.default['yum']['epel-testing']['managed'] = true\ninclude_recipe 'yum-epel'\n```\n\nMore Examples\n-------------\nPoint the epel repositories at an internally hosted server.\n\n```\nnode.default['yum']['epel']['enabled'] = true\nnode.default['yum']['epel']['mirrorlist'] = nil\nnode.default['yum']['epel']['baseurl'] = 'https://internal.example.com/centos/6/os/x86_64'\nnode.default['yum']['epel']['sslverify'] = false\n\ninclude_recipe 'yum-epel'\n```\n\nLicense & Authors\n-----------------\n- Author:: Sean OMeara ()\n\n```text\nCopyright:: 2011-2013 Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Chef", + "maintainer_email": "Sean OMeara ", + "license": "Apache 2.0", + "platforms": { + }, + "dependencies": { + "yum": "~> 3.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + } +} \ No newline at end of file diff --git a/chef/cookbooks/yum-epel/metadata.rb b/chef/cookbooks/yum-epel/metadata.rb new file mode 100644 index 0000000..187e9ae --- /dev/null +++ b/chef/cookbooks/yum-epel/metadata.rb @@ -0,0 +1,9 @@ +name 'yum-epel' +maintainer 'Chef' +maintainer_email 'Sean OMeara ' +license 'Apache 2.0' +description 'Installs/Configures yum-epel' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '0.3.4' + +depends 'yum', '~> 3.0' diff --git a/chef/cookbooks/yum-epel/recipes/default.rb b/chef/cookbooks/yum-epel/recipes/default.rb new file mode 100644 index 0000000..b8811ba --- /dev/null +++ b/chef/cookbooks/yum-epel/recipes/default.rb @@ -0,0 +1,56 @@ +# +# Author:: Sean OMeara () +# Recipe:: yum-epel::default +# +# Copyright 2013, Chef +# +# 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. + +%w{ + epel epel-debuginfo epel-source + epel-testing epel-testing-debuginfo epel-testing-source + }.each do |repo| + + if node['yum'][repo]['managed'] + yum_repository repo do + description node['yum'][repo]['description'] + baseurl node['yum'][repo]['baseurl'] + mirrorlist node['yum'][repo]['mirrorlist'] + gpgcheck node['yum'][repo]['gpgcheck'] + gpgkey node['yum'][repo]['gpgkey'] + enabled node['yum'][repo]['enabled'] + cost node['yum'][repo]['cost'] + exclude node['yum'][repo]['exclude'] + enablegroups node['yum'][repo]['enablegroups'] + failovermethod node['yum'][repo]['failovermethod'] + http_caching node['yum'][repo]['http_caching'] + include_config node['yum'][repo]['include_config'] + includepkgs node['yum'][repo]['includepkgs'] + keepalive node['yum'][repo]['keepalive'] + max_retries node['yum'][repo]['max_retries'] + metadata_expire node['yum'][repo]['metadata_expire'] + mirror_expire node['yum'][repo]['mirror_expire'] + priority node['yum'][repo]['priority'] + proxy node['yum'][repo]['proxy'] + proxy_username node['yum'][repo]['proxy_username'] + proxy_password node['yum'][repo]['proxy_password'] + repositoryid node['yum'][repo]['repositoryid'] + sslcacert node['yum'][repo]['sslcacert'] + sslclientcert node['yum'][repo]['sslclientcert'] + sslclientkey node['yum'][repo]['sslclientkey'] + sslverify node['yum'][repo]['sslverify'] + timeout node['yum'][repo]['timeout'] + action :create + end + end +end diff --git a/chef/cookbooks/yum-epel/templates/default/epel.repo.erb b/chef/cookbooks/yum-epel/templates/default/epel.repo.erb new file mode 100644 index 0000000..fd39019 --- /dev/null +++ b/chef/cookbooks/yum-epel/templates/default/epel.repo.erb @@ -0,0 +1,26 @@ +[epel] +name=Extra Packages for Enterprise Linux 6 - $basearch +#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch +baseurl=http://<%= node['yum']['centos']['repo_host'] %>/epel-6/$basearch +failovermethod=priority +enabled=1 +gpgcheck=0 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 + +[epel-debuginfo] +name=Extra Packages for Enterprise Linux 6 - $basearch - Debug +#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch/debug +baseurl=http://<%= node['yum']['centos']['repo_host'] %>/epel-6/$basearch/debug +failovermethod=priority +enabled=0 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 +gpgcheck=0 + +[epel-source] +name=Extra Packages for Enterprise Linux 6 - $basearch - Source +#baseurl=http://download.fedoraproject.org/pub/epel/6/SRPMS +baseurl=http://<%= node['yum']['centos']['repo_host'] %>/epel-6/SRPMS +failovermethod=priority +enabled=0 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 +gpgcheck=0 diff --git a/chef/cookbooks/yum-erlang_solutions/CHANGELOG.md b/chef/cookbooks/yum-erlang_solutions/CHANGELOG.md new file mode 100644 index 0000000..27ed141 --- /dev/null +++ b/chef/cookbooks/yum-erlang_solutions/CHANGELOG.md @@ -0,0 +1,17 @@ +yum-fedora Cookbook CHANGELOG +====================== +This file is used to list changes made in each version of the yum-centos cookbook. + +v0.2.0 (2014-02-14) +------------------- +Updating test harness + + +v0.1.4 +------ +adding CHANGELOG + + +v0.1.0 +------ +initial release diff --git a/chef/cookbooks/yum-erlang_solutions/README.md b/chef/cookbooks/yum-erlang_solutions/README.md new file mode 100644 index 0000000..e9f1f12 --- /dev/null +++ b/chef/cookbooks/yum-erlang_solutions/README.md @@ -0,0 +1,94 @@ +yum-erlang_solutions Cookbook +============ + +The yum-erlang_solutions cookbook takes over management of the default +repositoryids used by erlang_solutions. It allows attribute manipulation of +`erlang_solutions`. + +Requirements +------------ +* Chef 11 or higher +* yum cookbook version 3.0.0 or higher + +Attributes +---------- +The following attributes are set by default + +``` ruby +default['yum']['erlang_solutions']['baseurl'] = 'http://packages.erlang-solutions.com/rpm/centos/$releasever/$basearch' +default['yum']['erlang_solutions']['description'] = 'Centos $releasever - $basearch - Erlang Solutions' +default['yum']['erlang_solutions']['gpgkey'] = 'http://packages.erlang-solutions.com/debian/erlang_solutions.asc' +default['yum']['erlang_solutions']['enabled'] = true +``` + +Recipes +------- +* default - Walks through node attributes and feeds a yum_resource + parameters. The following is an example a resource generated by the + recipe during compilation. + +```ruby + yum_repository 'erlang_solutions' do + baseurl 'http://packages.erlang-solutions.com/rpm/centos/$releasever/$basearch' + description 'Centos $releasever - $basearch - Erlang Solutions' + enabled true + gpgcheck true + gpgkey 'http://packages.erlang-solutions.com/debian/erlang_solutions.asc' + end +``` + +Usage Example +------------- +To disable the erlang_solutions repository through a Role or Environment definition + +``` +default_attributes( + :yum => { + :erlang_solutions => { + :enabled => { + false + } + } + } + ) +``` + +To enable the erlang_solutions repository with a wrapper cookbook, place +the following in a recipe: + +``` +node.default['yum']['erlang_solutions']['enabled'] = true +include_recipe 'yum-erlang_solutions' +``` + +More Examples +------------- +Point the erlang_solutions repositories at an internally hosted server. + +``` +node.default['yum']['erlang_solutions']['enabled'] = true +node.default['yum']['erlang_solutions']['baseurl'] = 'https://internal.example.com/erlang_solutions' +node.default['yum']['erlang_solutions']['sslverify'] = false + +include_recipe 'yum-erlang_solutions' +``` + +License & Authors +----------------- +- Author:: Sean OMeara () + +```text +Copyright:: 2011-2013 Opscode, Inc. + +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. +``` diff --git a/chef/cookbooks/yum-erlang_solutions/attributes/erlang_solutions.rb b/chef/cookbooks/yum-erlang_solutions/attributes/erlang_solutions.rb new file mode 100644 index 0000000..a8c39fe --- /dev/null +++ b/chef/cookbooks/yum-erlang_solutions/attributes/erlang_solutions.rb @@ -0,0 +1,10 @@ +case node['platform_family'] +when 'rhel' + default['yum']['erlang_solutions']['baseurl'] = 'http://packages.erlang-solutions.com/rpm/centos/6/$basearch' +else + default['yum']['erlang_solutions']['baseurl'] = 'http://packages.erlang-solutions.com/rpm/centos/$releasever/$basearch' +end +default['yum']['erlang_solutions']['description'] = 'Centos $releasever - $basearch - Erlang Solutions' +default['yum']['erlang_solutions']['gpgkey'] = 'http://packages.erlang-solutions.com/debian/erlang_solutions.asc' +default['yum']['erlang_solutions']['gpgcheck'] = false +default['yum']['erlang_solutions']['enabled'] = true diff --git a/chef/cookbooks/yum-erlang_solutions/metadata.json b/chef/cookbooks/yum-erlang_solutions/metadata.json new file mode 100644 index 0000000..e37f285 --- /dev/null +++ b/chef/cookbooks/yum-erlang_solutions/metadata.json @@ -0,0 +1,30 @@ +{ + "name": "yum-erlang_solutions", + "version": "0.2.0", + "description": "Installs/Configures yum-erlang_solutions", + "long_description": "yum-erlang_solutions Cookbook\n============\n\nThe yum-erlang_solutions cookbook takes over management of the default\nrepositoryids used by erlang_solutions. It allows attribute manipulation of\n`erlang_solutions`.\n\nRequirements\n------------\n* Chef 11 or higher\n* yum cookbook version 3.0.0 or higher\n\nAttributes\n----------\nThe following attributes are set by default\n\n``` ruby\ndefault['yum']['erlang_solutions']['baseurl'] = 'http://packages.erlang-solutions.com/rpm/centos/$releasever/$basearch'\ndefault['yum']['erlang_solutions']['description'] = 'Centos $releasever - $basearch - Erlang Solutions'\ndefault['yum']['erlang_solutions']['gpgkey'] = 'http://packages.erlang-solutions.com/debian/erlang_solutions.asc'\ndefault['yum']['erlang_solutions']['enabled'] = true\n```\n\nRecipes\n-------\n* default - Walks through node attributes and feeds a yum_resource\n parameters. The following is an example a resource generated by the\n recipe during compilation.\n\n```ruby\n yum_repository 'erlang_solutions' do\n baseurl 'http://packages.erlang-solutions.com/rpm/centos/$releasever/$basearch'\n description 'Centos $releasever - $basearch - Erlang Solutions'\n enabled true\n gpgcheck true\n gpgkey 'http://packages.erlang-solutions.com/debian/erlang_solutions.asc'\n end\n```\n\nUsage Example\n-------------\nTo disable the erlang_solutions repository through a Role or Environment definition\n\n```\ndefault_attributes(\n :yum => {\n :erlang_solutions => {\n :enabled => {\n false\n }\n }\n }\n )\n```\n\nTo enable the erlang_solutions repository with a wrapper cookbook, place\nthe following in a recipe:\n\n```\nnode.default['yum']['erlang_solutions']['enabled'] = true\ninclude_recipe 'yum-erlang_solutions'\n```\n\nMore Examples\n-------------\nPoint the erlang_solutions repositories at an internally hosted server.\n\n```\nnode.default['yum']['erlang_solutions']['enabled'] = true\nnode.default['yum']['erlang_solutions']['baseurl'] = 'https://internal.example.com/erlang_solutions'\nnode.default['yum']['erlang_solutions']['sslverify'] = false\n\ninclude_recipe 'yum-erlang_solutions'\n```\n\nLicense & Authors\n-----------------\n- Author:: Sean OMeara ()\n\n```text\nCopyright:: 2011-2013 Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "maintainer": "Chef", + "maintainer_email": "Sean OMeara ", + "license": "Apache 2.0", + "platforms": { + }, + "dependencies": { + "yum": "~> 3.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + } +} \ No newline at end of file diff --git a/chef/cookbooks/yum-erlang_solutions/metadata.rb b/chef/cookbooks/yum-erlang_solutions/metadata.rb new file mode 100644 index 0000000..d0852b9 --- /dev/null +++ b/chef/cookbooks/yum-erlang_solutions/metadata.rb @@ -0,0 +1,9 @@ +name 'yum-erlang_solutions' +maintainer 'Chef' +maintainer_email 'Sean OMeara ' +license 'Apache 2.0' +description 'Installs/Configures yum-erlang_solutions' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '0.2.0' + +depends 'yum', '~> 3.0' diff --git a/chef/cookbooks/yum-erlang_solutions/recipes/default.rb b/chef/cookbooks/yum-erlang_solutions/recipes/default.rb new file mode 100644 index 0000000..fb1dd53 --- /dev/null +++ b/chef/cookbooks/yum-erlang_solutions/recipes/default.rb @@ -0,0 +1,48 @@ +# +# Author:: Sean OMeara () +# Recipe:: yum-erlang_solutions::default +# +# Copyright 2013, Chef +# +# 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. + +yum_repository 'erlang_solutions' do + description node['yum']['erlang_solutions']['description'] + baseurl node['yum']['erlang_solutions']['baseurl'] + mirrorlist node['yum']['erlang_solutions']['mirrorlist'] + gpgcheck node['yum']['erlang_solutions']['gpgcheck'] + gpgkey node['yum']['erlang_solutions']['gpgkey'] + enabled node['yum']['erlang_solutions']['enabled'] + cost node['yum']['erlang_solutions']['cost'] + exclude node['yum']['erlang_solutions']['exclude'] + enablegroups node['yum']['erlang_solutions']['enablegroups'] + failovermethod node['yum']['erlang_solutions']['failovermethod'] + http_caching node['yum']['erlang_solutions']['http_caching'] + include_config node['yum']['erlang_solutions']['include_config'] + includepkgs node['yum']['erlang_solutions']['includepkgs'] + keepalive node['yum']['erlang_solutions']['keepalive'] + max_retries node['yum']['erlang_solutions']['max_retries'] + metadata_expire node['yum']['erlang_solutions']['metadata_expire'] + mirror_expire node['yum']['erlang_solutions']['mirror_expire'] + priority node['yum']['erlang_solutions']['priority'] + proxy node['yum']['erlang_solutions']['proxy'] + proxy_username node['yum']['erlang_solutions']['proxy_username'] + proxy_password node['yum']['erlang_solutions']['proxy_password'] + repositoryid node['yum']['erlang_solutions']['repositoryid'] + sslcacert node['yum']['erlang_solutions']['sslcacert'] + sslclientcert node['yum']['erlang_solutions']['sslclientcert'] + sslclientkey node['yum']['erlang_solutions']['sslclientkey'] + sslverify node['yum']['erlang_solutions']['sslverify'] + timeout node['yum']['erlang_solutions']['timeout'] + action :create +end diff --git a/chef/cookbooks/yum/.kitchen.yml b/chef/cookbooks/yum/.kitchen.yml deleted file mode 100644 index 8c7770a..0000000 --- a/chef/cookbooks/yum/.kitchen.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -driver_plugin: vagrant -driver_config: - require_chef_omnibus: true - -platforms: -- name: centos-6.4 - driver_config: - box: opscode-centos-6.4 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box - -- name: centos-5.9 - driver_config: - box: opscode-centos-5.9 - box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box - -suites: -- name: test - run_list: - - recipe[minitest-handler] - - recipe[yum::test] - attributes: {} diff --git a/chef/cookbooks/yum/Berksfile b/chef/cookbooks/yum/Berksfile deleted file mode 100644 index 6a8a4a9..0000000 --- a/chef/cookbooks/yum/Berksfile +++ /dev/null @@ -1,7 +0,0 @@ -site :opscode - -metadata - -group :integration do - cookbook "minitest-handler" -end diff --git a/chef/cookbooks/yum/CHANGELOG.md b/chef/cookbooks/yum/CHANGELOG.md index 0763d67..921d761 100644 --- a/chef/cookbooks/yum/CHANGELOG.md +++ b/chef/cookbooks/yum/CHANGELOG.md @@ -3,6 +3,88 @@ yum Cookbook CHANGELOG This file is used to list changes made in each version of the yum cookbook. +v3.1.4 (2014-03-12) +------------------- +- [COOK-4417] Expand test harness to encompass 32-bit boxes + + +v3.1.2 (2014-02-23) +------------------- +Fixing bugs around :delete action and cache clean +Fixing specs to cover :remove and :delete aliasing properly +Adding Travis-ci build matrix bits + + +v3.1.0 (2014-02-13) +------------------- +- Updating testing harness for integration testing on Travis-ci +- Adding TESTING.md and Guardfile +- PR #67 - Add skip_if_unvailable repository option +- PR #64 - Fix validation of 'metadata_expire' option to match documentation +- [COOK-3591] - removing node.name from repo template rendering +- [COOK-4275] - Enhancements to yum cookbook +- Adding full spec coverage +- Adding support for custom source template to yum_repository + + +v3.0.8 (2014-01-27) +------------------- +Fixing typo in default.rb. yum_globalconfig now passes proxy attribute correctly. + + +v3.0.6 (2014-01-27) +------------------- +Updating default.rb to consume node['yum']['main']['proxy'] + + +v3.0.4 (2013-12-29) +------------------- +### Bug +- **[COOK-4156](https://tickets.opscode.com/browse/COOK-4156)** - yum cookbook creates a yum.conf with "cachefir" directive + + +v3.0.2 +------ +Updating globalconfig provider for Chef 10 compatability + + +v3.0.0 +------ +3.0.0 +Major rewrite with breaking changes. +Recipes broken out into individual cookbooks +yum_key resource has been removed +yum_repository resource now takes gpgkey as a URL directly +yum_repository actions have been reduced to :create and :delete +'name' has been changed to repositoryid to avoid ambiguity +chefspec test coverage +gpgcheck is set to 'true' by default and must be explicitly disabled + + +v2.4.4 +------ +Reverting to Ruby 1.8 hash syntax. + + +v2.4.2 +------ +[COOK-3275] LWRP repository.rb :add method fails to create yum repo in +some cases which causes :update to fail Amazon rhel + + +v2.4.0 +------ +### Improvement +- [COOK-3025] - Allow per-repo proxy definitions + + +v2.3.4 +------ +### Improvement +- **[COOK-3689](https://tickets.opscode.com/browse/COOK-3689)** - Fix warnings about resource cloning +- **[COOK-3574](https://tickets.opscode.com/browse/COOK-3574)** - Add missing "description" field in metadata + + v2.3.2 ------ ### Bug @@ -60,7 +142,7 @@ v2.0.2 v2.0.0 ------ -This version changes the behavior of the EPEL recipe (most commonly used in other Opscode cookbooks) on Amazon, and removes an attribute, `node['yum']['epel_release']`. See the README for details. +This version changes the behavior of the EPEL recipe (most commonly used in other Chef cookbooks) on Amazon, and removes an attribute, `node['yum']['epel_release']`. See the README for details. - [COOK-1772] - Simplify management of EPEL with LWRP diff --git a/chef/cookbooks/yum/CONTRIBUTING.md b/chef/cookbooks/yum/CONTRIBUTING.md deleted file mode 100644 index 3a99897..0000000 --- a/chef/cookbooks/yum/CONTRIBUTING.md +++ /dev/null @@ -1,257 +0,0 @@ -# Contributing to Opscode Cookbooks - -We are glad you want to contribute to Opscode Cookbooks! The first -step is the desire to improve the project. - -You can find the answers to additional frequently asked questions -[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). - -You can find additional information about -[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) -on the wiki as well. - -## Quick-contribute - -* Create an account on our [bug tracker](http://tickets.opscode.com) -* Sign our contributor agreement (CLA) -[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) -(keep reading if you're contributing on behalf of your employer) -* Create a ticket for your change on the - [bug tracker](http://tickets.opscode.com) -* Link to your patch as a rebased git branch or pull request from the - ticket -* Resolve the ticket as fixed - -We regularly review contributions and will get back to you if we have -any suggestions or concerns. - -## The Apache License and the CLA/CCLA - -Licensing is very important to open source projects, it helps ensure -the software continues to be available under the terms that the author -desired. Chef uses the Apache 2.0 license to strike a balance between -open contribution and allowing you to use the software however you -would like to. - -The license tells you what rights you have that are provided by the -copyright holder. It is important that the contributor fully -understands what rights they are licensing and agrees to them. -Sometimes the copyright holder isn't the contributor, most often when -the contributor is doing work for a company. - -To make a good faith effort to ensure these criteria are met, Opscode -requires a Contributor License Agreement (CLA) or a Corporate -Contributor License Agreement (CCLA) for all contributions. This is -without exception due to some matters not being related to copyright -and to avoid having to continually check with our lawyers about small -patches. - -It only takes a few minutes to complete a CLA, and you retain the -copyright to your contribution. - -You can complete our contributor agreement (CLA) -[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). -If you're contributing on behalf of your employer, have your employer -fill out our -[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) -instead. - -## Ticket Tracker (JIRA) - -The [ticket tracker](http://tickets.opscode.com) is the most important -documentation for the code base. It provides significant historical -information, such as: - -* Which release a bug fix is included in -* Discussion regarding the design and merits of features -* Error output to aid in finding similar bugs - -Each ticket should aim to fix one bug or add one feature. - -## Using git - -You can get a quick copy of the repository for this cookbook by -running `git clone -git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. - -For collaboration purposes, it is best if you create a Github account -and fork the repository to your own account. Once you do this you will -be able to push your changes to your Github repository for others to -see and use. - -If you have another repository in your GitHub account named the same -as the cookbook, we suggest you suffix the repository with -cookbook. - -### Branches and Commits - -You should submit your patch as a git branch named after the ticket, -such as COOK-1337. This is called a _topic branch_ and allows users to -associate a branch of code with the ticket. - -It is a best practice to have your commit message have a _summary -line_ that includes the ticket number, followed by an empty line and -then a brief description of the commit. This also helps other -contributors understand the purpose of changes to the code. - - [COOK-1757] - platform_family and style - - * use platform_family for platform checking - * update notifies syntax to "resource_type[resource_name]" instead of - resources() lookup - * COOK-692 - delete config files dropped off by packages in conf.d - * dropped debian 4 support because all other platforms have the same - values, and it is older than "old stable" debian release - -Remember that not all users use Chef in the same way or on the same -operating systems as you, so it is helpful to be clear about your use -case and change so they can understand it even when it doesn't apply -to them. - -### Github and Pull Requests - -All of Opscode's open source cookbook projects are available on -[Github](http://www.github.com/opscode-cookbooks). - -We don't require you to use Github, and we will even take patch diffs -attached to tickets on the tracker. However Github has a lot of -convenient features, such as being able to see a diff of changes -between a pull request and the main repository quickly without -downloading the branch. - -If you do choose to use a pull request, please provide a link to the -pull request from the ticket __and__ a link to the ticket from the -pull request. Because pull requests only have two states, open and -closed, we can't easily filter pull requests that are waiting for a -reply from the author for various reasons. - -### More information - -Additional help with git is available on the -[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) -wiki page. - -## Functional and Unit Tests - -This cookbook is set up to run tests under -[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It -uses minitest-chef to run integration tests after the node has been -converged to verify that the state of the node. - -Test kitchen should run completely without exception using the default -[baseboxes provided by Opscode](https://github.com/opscode/bento). -Because Test Kitchen creates VirtualBox machines and runs through -every configuration in the Kitchenfile, it may take some time for -these tests to complete. - -If your changes are only for a specific recipe, run only its -configuration with Test Kitchen. If you are adding a new recipe, or -other functionality such as a LWRP or definition, please add -appropriate tests and ensure they run with Test Kitchen. - -If any don't pass, investigate them before submitting your patch. - -Any new feature should have unit tests included with the patch with -good code coverage to help protect it from future changes. Similarly, -patches that fix a bug or regression should have a _regression test_. -Simply put, this is a test that would fail without your patch but -passes with it. The goal is to ensure this bug doesn't regress in the -future. Consider a regular expression that doesn't match a certain -pattern that it should, so you provide a patch and a test to ensure -that the part of the code that uses this regular expression works as -expected. Later another contributor may modify this regular expression -in a way that breaks your use cases. The test you wrote will fail, -signalling to them to research your ticket and use case and accounting -for it. - -If you need help writing tests, please ask on the Chef Developer's -mailing list, or the #chef-hacking IRC channel. - -## Code Review - -Opscode regularly reviews code contributions and provides suggestions -for improvement in the code itself or the implementation. - -We find contributions by searching the ticket tracker for _resolved_ -tickets with a status of _fixed_. If we have feedback we will reopen -the ticket and you should resolve it again when you've made the -changes or have a response to our feedback. When we believe the patch -is ready to be merged, we will tag the _Code Reviewed_ field with -_Reviewed_. - -Depending on the project, these tickets are then merged within a week -or two, depending on the current release cycle. - -## Release Cycle - -The versioning for Opscode Cookbook projects is X.Y.Z. - -* X is a major release, which may not be fully compatible with prior - major releases -* Y is a minor release, which adds both new features and bug fixes -* Z is a patch release, which adds just bug fixes - -A released version of a cookbook will end in an even number, e.g. -"1.2.4" or "0.8.0". When development for the next version of the -cookbook begins, the "Z" patch number is incremented to the next odd -number, however the next release of the cookbook may be a major or -minor incrementing version. - -Releases of Opscode's cookbooks are usually announced on the Chef user -mailing list. Releases of several cookbooks may be batched together -and announced on the [Opscode Blog](http://www.opscode.com/blog). - -## Working with the community - -These resources will help you learn more about Chef and connect to -other members of the Chef community: - -* [chef](http://lists.opscode.com/sympa/info/chef) and - [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing - lists -* #chef and #chef-hacking IRC channels on irc.freenode.net -* [Community Cookbook site](http://community.opscode.com) -* [Chef wiki](http://wiki.opscode.com/display/chef) -* Opscode Chef [product page](http://www.opscode.com/chef) - - -## Cookbook Contribution Do's and Don't's - -Please do include tests for your contribution. If you need help, ask -on the -[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) -or the -[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). -Not all platforms that a cookbook supports may be supported by Test -Kitchen. Please provide evidence of testing your contribution if it -isn't trivial so we don't have to duplicate effort in testing. Chef -10.14+ "doc" formatted output is sufficient. - -Please do indicate new platform (families) or platform versions in the -commit message, and update the relevant ticket. - -If a contribution adds new platforms or platform versions, indicate -such in the body of the commit message(s), and update the relevant -COOK ticket. When writing commit messages, it is helpful for others if -you indicate the COOK ticket. For example: - - git commit -m '[COOK-1041] - Updated pool resource to correctly - delete.' - -Please do use [foodcritic](http://acrmp.github.com/foodcritic) to -lint-check the cookbook. Except FC007, it should pass all correctness -rules. FC007 is okay as long as the dependent cookbooks are *required* -for the default behavior of the cookbook, such as to support an -uncommon platform, secondary recipe, etc. - -Please do ensure that your changes do not break or modify behavior for -other platforms supported by the cookbook. For example if your changes -are for Debian, make sure that they do not break on CentOS. - -Please do not modify the version number in the metadata.rb, Opscode -will select the appropriate version based on the release cycle -information above. - -Please do not update the CHANGELOG.md for a new version. Not all -changes to a cookbook may be merged and released in the same versions. -Opscode will update the CHANGELOG.md when releasing a new version of -the cookbook. diff --git a/chef/cookbooks/yum/LICENSE b/chef/cookbooks/yum/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/chef/cookbooks/yum/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/chef/cookbooks/yum/README.md b/chef/cookbooks/yum/README.md index 65d6b2a..ef8d96b 100644 --- a/chef/cookbooks/yum/README.md +++ b/chef/cookbooks/yum/README.md @@ -1,212 +1,258 @@ yum Cookbook ============ -Configures various YUM components on Red Hat-like systems. Includes LWRP for managing repositories and their GPG keys. -Based on the work done by Eric Wolfe and Charles Duffy on the [yumrepo](https://github.com/atomic-penguin/cookbook-yumrepo) cookbook. +The Yum cookbook exposes the `yum_globalconfig` and `yum_repository` +resources that allows a user to both control global behavior and make +individual Yum repositories available for use. These resources aim to +allow the user to configure all options listed in the `yum.conf` man +page, found at http://linux.die.net/man/5/yum.conf +NOTES +----- +WARNING: Yum cookbook version 3.0.0 and above contain non-backwards +compatible breaking changes and will not work with cookbooks written +against the 2.x and 1.x series. Changes have been made to the +yum_repository resource, and the yum_key resource has been eliminated +entirely. Recipes have been eliminated and moved into their own +cookbooks. Please lock yum to the 2.x series in your Chef environments +until all dependent cookbooks have been ported. Requirements ------------ -Red Hat Enterprise Linux 5, and 6 distributions within this platform family. - - -Attributes ----------- -* `yum['exclude']` - - An array containing a list of packages to exclude from updates or installs. Wildcards and shell globs are supported. - - Defaults to an empty exclude list. - -* `yum['installonlypkgs']` - - An array containing a list of packages which should only be - installed, never updated. - - Defaults to an empty install-only list. - -* `yum['ius_release']` - - Set the IUS release to install. - - Defaults to the current release of the IUS repo. - -* `yum['repoforge_release']` - - Set the RepoForge release to install. - - Defaults to the current release of the repoforge repo. - -EPEL attributes used in the `yum::epel` recipe, see `attributes/epel.rb` for default values: - -* `yum['epel']['key']` - - Name of the GPG key used for the repo. - -* `yum['epel']['baseurl']` - - Base URL to an EPEL mirror. - -* `yum['epel']['url']` - - URL to the EPEL mirrorlist. - -* `yum['epel']['key_url']` - - URL to the GPG key for the repo. - -* `yum['epel']['includepkgs']` - - list of packages you want to use for the repo. - -* `yum['epel']['exclude']` - - list of packages you do NOT want to use for the repo. - -The `node['yum']['epel_release']` attribute is removed, see the __epel__ recipe information below. - -remi attributes used in the `yum::remi` recipe, see `attributes/remi.rb` for default values: - -* `yum['remi']['key']` - - Name of the GPG key used for the repo. - -* `yum['remi']['url']` - - URL to the remi mirrorlist. - -* `yum['remi']['key_url']` - - URL to the GPG key for the repo. - -* `yum['remi']['includepkgs']` - - list of packages you want to use for the repo. - -* `yum['remi']['exclude']` - - list of packages you do NOT want to use for the repo. - -Proxy settings used in yum.conf on RHEL family 5 and 6: - -* `yum['proxy']` - - Set the URL for an HTTP proxy - - None of the proxy settings are used if this is an empty string - (default) - -* `yum['proxy_username']` - - Set the username for the proxy - - not used if `yum['proxy']` above is an empty string - -* `yum['proxy_password']` - - Set the password for the proxy - - not used if `yum['proxy']` above is an empty string - - -Recipes -------- -### default -The default recipe does nothing. - -### yum -Manages the configuration of the `/etc/yum.conf` via attributes. See the aforementioned Array attributes `yum['exclude']` and `yum['installonlypkgs']`. - -### epel -Uses the `yum_key` and `yum_repository` resources from this cookbook are used to manage the main EPEL repository. If you need other EPEL repositories (source, debug-info), use the `yum_repository` LWRP in your own cookbook where those packages are required. The recipe will use the `yum['epel']` attributes (see above) to configure the key, url and download the GPG key for the repo. The defaults are detected by platform and version and should just work without modification in most use cases. - -On all platforms except Amazon, the action is to add the repository. On Amazon, the action is add and update. - -Amazon Linux has the EPEL repositories already added in the AMI. In previous versions of this cookbook, they were enabled with `yum-config-manager`, however in the current version, we manage the repository using the LWRP. The main difference is that the source and debuginfo repositories are not available, but if they're needed, add them using the `yum_repository` LWRP in your own cookbook(s). - -### ius -Installs the [IUS Community repositories](http://iuscommunity.org/Repos) via RPM. Uses the `node['yum']['ius_release']` attribute to select the right version of the package to install. - -The IUS repository requires EPEL, and includes `yum::epel` as a dependency. - -### repoforge -Installs the [RepoForge repositories](http://repoforge.org/) via RPM. Uses the `node['yum']['repoforge_release']` attribute to select the right version of the package to install. - -The RepoForge repository requires EPEL, and includes `yum::epel` as a dependency. - -### remi -Install the [Les RPM de Remi - Repository](http://rpms.famillecollet.com/) with the `yum_key` and `yum_repository` resources from this cookbook are used to manage the remi repository. Use the `yum['remi']` attributes (see above) to configure the key, url and download the GPG key for the repo. The defaults are detected by platform and should just work without modification in most use cases. - +* Chef 11 or higher +* Ruby 1.9 (preferably from the Chef full-stack installer) +* RHEL5, RHEL6, or other platforms within the family Resources/Providers ------------------- -### yum_key -This LWRP handles importing GPG keys for YUM repositories. Keys can be imported by the `url` parameter or placed in `/etc/pki/rpm-gpg/` by a recipe and then installed with the LWRP without passing the URL. - -#### Actions -- :add: installs the GPG key into `/etc/pki/rpm-gpg/` -- :remove: removes the GPG key from `/etc/pki/rpm-gpg/` - -#### Attribute Parameters -- key: name attribute. The name of the GPG key to install. -- url: if the key needs to be downloaded, the URL providing the download. - -#### Example - -``` ruby -# add the Zenoss GPG key -yum_key "RPM-GPG-KEY-zenoss" do - url "http://dev.zenoss.com/yum/RPM-GPG-KEY-zenoss" - action :add -end - -# remove Zenoss GPG key -yum_key "RPM-GPG-KEY-zenoss" do - action :remove -end -``` - ### yum_repository -This LWRP provides an easy way to manage additional YUM repositories. GPG keys can be managed with the `yum_key` LWRP. The LWRP automatically updates the package management cache upon the first run, when a new repo is added. - -#### Actions -- :create: creates a repository file and builds the repository listing -- :add: runs create action if repository file is missing (default) -- :remove: removes the repository file -- :update: updates the repository - -#### Attribute Parameters -- repo_name: name attribute. The name of the channel to discover -- description. The description of the repository -- url: The URL providing the packages, used for baseurl in the config -- mirrorlist: Set this as a string containing the URI to the - mirrorlist, start with "http://", "ftp://", "file://"; use "file://" - if the mirrorlist is a text file on the system. -- key: Optional, the name of the GPG key file installed by the `key` - LWRP. -- enabled: Default is `1`, set to `0` if the repository is disabled. -- type: Optional, alternate type of repository -- failovermethod: Optional, failovermethod -- bootstrapurl: Optional, bootstrapurl -- make_cache: Optional, Default is `true`, if `false` then `yum -q - makecache` will not be ran -- metadata_expire: Optional, Default is nil (or not applied) -- type: Optional, Default is nil (or not applied) - -*Note*: When using both url (to set baseurl) and mirrorlist, it is probably a good idea to also install the fastestmirror plugin, and use failovermethod "priority". +This resource manages a yum repository configuration file at +/etc/yum.repos.d/`repositoryid`.repo. When the file needs to be +repaired, it calls yum-makecache so packages in the repo become +available to the next resource. #### Example ``` ruby # add the Zenoss repository -yum_repository "zenoss" do - repo_name "zenoss" +yum_repository 'zenoss' do description "Zenoss Stable repo" - url "http://dev.zenoss.com/yum/stable/" - key "RPM-GPG-KEY-zenoss" - action :add + baseurl "http://dev.zenoss.com/yum/stable/" + gpgkey 'http://dev.zenoss.com/yum/RPM-GPG-KEY-zenoss' + action :create end -# remove Zenoss repo -yum_repository "zenoss" do - action :remove +# add the EPEL repo +yum_repository 'epel' do + description 'Extra Packages for Enterprise Linux' + mirrorlist 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch' + gpgkey 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6' + action :create end ``` +``` ruby +# delete CentOS-Media repo +yum_repository 'CentOS-Media' do + action :delete +end +``` + +#### Actions +- `:create` - creates a repository file and builds the repository listing +- `:delete` - deletes the repository file + +#### Parameters +* `baseurl` - Must be a URL to the directory where the yum repository's + 'repodata' directory lives. Can be an http://, ftp:// or file:// + URL. You can specify multiple URLs in one baseurl statement. +* `cost` - relative cost of accessing this repository. Useful for + weighing one repo's packages as greater/less than any other. + defaults to 1000 +* `description` - Maps to the 'name' parameter in a repository .conf. + Descriptive name for the repository channel. This directive must be + specified. +* `enabled` - Either `true` or `false`. This tells yum whether or not use this repository. +* `enablegroups` - Either `true` or `false`. Determines whether yum + will allow the use of package groups for this repository. Default is + `true` (package groups are allowed). +* `exclude` - List of packages to exclude from updates or installs. This + should be a space separated list in a single string. Shell globs using wildcards (eg. * + and ?) are allowed. +* `failovermethod` - Either 'roundrobin' or 'priority'. +* `fastestmirror_enabled` - Either `true` or `false` +* `gpgcheck` - Either `true` or `false`. This tells yum whether or not + it should perform a GPG signature check on packages. When this is + set in the [main] section it sets the default for all repositories. + The default is `true`. +* `gpgkey` - A URL pointing to the ASCII-armored GPG key file for the + repository. This option is used if yum needs a public key to verify + a package and the required key hasn't been imported into the RPM + database. If this option is set, yum will automatically import the + key from the specified URL. +* `http_caching` - Either 'all', 'packages', or 'none'. Determines how + upstream HTTP caches are instructed to handle any HTTP downloads + that Yum does. Defaults to 'all' +* `includepkgs` - Inverse of exclude. This is a list of packages you + want to use from a repository. If this option lists only one package + then that is all yum will ever see from the repository. Defaults to + an empty list. +* `keepalive` - Either `true` or `false`. This tells yum whether or not + HTTP/1.1 keepalive should be used with this repository. +* `max_retries` - Set the number of times any attempt to retrieve a file + should retry before returning an error. Setting this to '0' makes + yum try forever. Default is '10'. +* `metadata_expire` - Time (in seconds) after which the metadata will + expire. So that if the current metadata downloaded is less than this + many seconds old then yum will not update the metadata against the + repository. If you find that yum is not downloading information on + updates as often as you would like lower the value of this option. + You can also change from the default of using seconds to using days, + hours or minutes by appending a d, h or m respectively. The default + is 6 hours, to compliment yum-updatesd running once an hour. It's + also possible to use the word "never", meaning that the metadata + will never expire. Note that when using a metalink file the metalink + must always be newer than the metadata for the repository, due to + the validation, so this timeout also applies to the metalink file. +* `mirrorlist` - Specifies a URL to a file containing a list of + baseurls. This can be used instead of or with the baseurl option. + Substitution variables, described below, can be used with this + option. As a special hack is the mirrorlist URL contains the word + "metalink" then the value of mirrorlist is copied to metalink (if + metalink is not set) +* `mirror_expire` - Time (in seconds) after which the mirrorlist locally + cached will expire. If the current mirrorlist is less than this many + seconds old then yum will not download another copy of the + mirrorlist, it has the same extra format as metadata_expire. If you + find that yum is not downloading the mirrorlists as often as you + would like lower the value of this option. +* `mirrorlist_expire` - alias for mirror_expire +* `priority` - When the yum-priorities plug-in is enabled, you set + priorities on repository entries, where N is an integer from 1 to 99. The + default priority for repositories is 99. +* `proxy` - URL to the proxy server that yum should use. +* `proxy_username` - username to use for proxy +* `proxy_password` - password for this proxy +* `report_instanceid` - Report instance ID when using Amazon Linux AMIs + and repositories +* `repositoryid` - Must be a unique name for each repository, one word. + Defaults to name attribute. +* `source` - Use a custom template source instead of the default one + in the yum cookbook +* `sslcacert` - Path to the directory containing the databases of the + certificate authorities yum should use to verify SSL certificates. + Defaults to none - uses system default +* `sslclientcert` - Path to the SSL client certificate yum should use to + connect to repos/remote sites Defaults to none. +* `sslclientkey` - Path to the SSL client key yum should use to connect + to repos/remote sites Defaults to none. +* `sslverify` - Either `true` or `false`. Determines if yum will verify SSL certificates/hosts. Defaults to `true` +* `timeout` - Number of seconds to wait for a connection before timing + out. Defaults to 30 seconds. This may be too short of a time for + extremely overloaded sites. + +### yum_globalconfig +This renders a template with global yum configuration parameters. The +default recipe uses it to render `/etc/yum.conf`. It is flexible +enough to be used in other scenarios, such as building RPMs in +isolation by modifying `installroot`. + +#### Example +``` ruby +yum_globalconfig '/my/chroot/etc/yum.conf' do + cachedir '/my/chroot/etc/yum.conf' + keepcache 'yes' + debuglevel '2' + installroot '/my/chroot' + action :create +end +``` + +#### Parameters +`yum_globalconfig` can take most of the same parameters as a +`yum_repository`, plus more, too numerous to describe here. Below are +a few of the more commonly used ones. For a complete list, please +consult the `yum.conf` man page, found here: +http://linux.die.net/man/5/yum.conf + +* `cachedir` - Directory where yum should store its cache and db + files. The default is '/var/cache/yum'. +* `keepcache` - Either `true` or `false`. Determines whether or not + yum keeps the cache of headers and packages after successful + installation. Default is `true` (keep files) +* `debuglevel` - Debug message output level. Practical range is 0-10. + Default is '2'. +* `exclude` - List of packages to exclude from updates or installs. + This should be a space separated list. Shell globs using wildcards + (eg. * and ?) are allowed. +* `installonlypkgs` = List of package provides that should only ever + be installed, never updated. Kernels in particular fall into this + category. Defaults to kernel, kernel-bigmem, kernel-enterprise, + kernel-smp, kernel-debug, kernel-unsupported, kernel-source, + kernel-devel, kernel-PAE, kernel-PAE-debug. +* `logfile` - Full directory and file name for where yum should write + its log file. +* `exactarch` - Either `true` or `false`. Set to `true` to make 'yum update' only + update the architectures of packages that you have installed. ie: + with this enabled yum will not install an i686 package to update an + x86_64 package. Default is `true` +* `gpgcheck` - Either `true` or `false`. This tells yum whether or not + it should perform a GPG signature check on the packages gotten from + this repository. + +Recipes +------- +* `default` - Configures `yum_globalconfig[/etc/yum.conf]` with values + found in node attributes at `node['yum']['main']` + +Attributes +---------- +The following attributes are set by default + +``` ruby +default['yum']['main']['cachedir'] = '/var/cache/yum/$basearch/$releasever' +default['yum']['main']['keepcache'] = false +default['yum']['main']['debuglevel'] = nil +default['yum']['main']['exclude'] = nil +default['yum']['main']['logfile'] = '/var/log/yum.log' +default['yum']['main']['exactarch'] = nil +default['yum']['main']['obsoletes'] = nil +default['yum']['main']['installonly_limit'] = nil +default['yum']['main']['installonlypkgs'] = nil +default['yum']['main']['installroot'] = nil +``` + +Related Cookbooks +----------------- +Recipes from older versions of this cookbook have been moved +individual cookbooks. Recipes for managing platform yum configurations +and installing specific repositories can be found in one (or more!) of +the following cookbook. + +* yum-centos +* yum-fedora +* yum-amazon +* yum-epel +* yum-elrepo +* yum-repoforge +* yum-ius +* yum-percona +* yum-pgdg Usage ----- -Put `recipe[yum::yum]` in the run list to ensure yum is configured correctly for your environment within your Chef run. - -Use the `yum::epel` recipe to enable EPEL, or the `yum::ius` recipe to enable IUS, or the `yum::repoforge` recipe to enable RepoForge, or the `yum::remi` recipe to enable remi per __Recipes__ section above. - -You can manage GPG keys either with cookbook_file in a recipe if you want to package it with a cookbook or use the `url` parameter of the `key` LWRP. - +Put `depends 'yum'` in your metadata.rb to gain access to the +yum_repository resource. License & Authors ----------------- - Author:: Eric G. Wolfe -- Author:: Matt Ray () -- Author:: Joshua Timberman () +- Author:: Matt Ray () +- Author:: Joshua Timberman () +- Author:: Sean OMeara () ```text -Copyright:: 2010 Tippr Inc. Copyright:: 2011 Eric G. Wolfe -Copyright:: 2011-2012 Opscode, Inc. +Copyright:: 2013 Chef Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chef/cookbooks/yum/attributes/default.rb b/chef/cookbooks/yum/attributes/default.rb deleted file mode 100644 index 085ac99..0000000 --- a/chef/cookbooks/yum/attributes/default.rb +++ /dev/null @@ -1,30 +0,0 @@ -# -# Cookbook Name:: yum -# Attributes:: default -# -# Copyright 2011, Eric G. Wolfe -# Copyright 2011, Opscode, Inc. -# -# 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. -# - -# Example: override.yum.exclude = "kernel* compat-glibc*" -default['yum']['exclude'] = Array.new -default['yum']['installonlypkgs'] = Array.new -default['yum']['ius_release'] = '1.0-11' -default['yum']['repoforge_release'] = '0.5.2-2' -default['yum']['proxy'] = '' -default['yum']['proxy_username'] = '' -default['yum']['proxy_password'] = '' -default['yum']['cachedir'] = '/var/cache/yum' -default['yum']['keepcache'] = 0 diff --git a/chef/cookbooks/yum/attributes/elrepo.rb b/chef/cookbooks/yum/attributes/elrepo.rb deleted file mode 100644 index 5c97402..0000000 --- a/chef/cookbooks/yum/attributes/elrepo.rb +++ /dev/null @@ -1,24 +0,0 @@ -# -# Cookbook Name:: yum -# Attributes:: elrepo -# -# Copyright 2013, Opscode, Inc. -# -# 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. -# - -default['yum']['elrepo']['url'] = "http://elrepo.org/mirrors-elrepo.el#{node['platform_version'].to_i}" -default['yum']['elrepo']['key'] = "RPM-GPG-KEY-elrepo.org" -default['yum']['elrepo']['key_url'] = "http://elrepo.org/#{node['yum']['elrepo']['key']}" -default['yum']['elrepo']['includepkgs'] = nil -default['yum']['elrepo']['exclude'] = nil diff --git a/chef/cookbooks/yum/attributes/epel.rb b/chef/cookbooks/yum/attributes/epel.rb deleted file mode 100644 index c2247f8..0000000 --- a/chef/cookbooks/yum/attributes/epel.rb +++ /dev/null @@ -1,39 +0,0 @@ -# -# Cookbook Name:: yum -# Attributes:: epel -# -# Copyright 2011, Eric G. Wolfe -# Copyright 2011, Opscode, Inc. -# -# 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. -# - -case node['platform'] -when "amazon" - default['yum']['epel']['url'] = "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch" - default['yum']['epel']['baseurl'] = "" - default['yum']['epel']['key'] = "RPM-GPG-KEY-EPEL-6" -else - default['yum']['epel']['url'] = "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-#{node['platform_version'].to_i}&arch=$basearch" - default['yum']['epel']['baseurl'] = "" - - if node['platform_version'].to_i >= 6 - default['yum']['epel']['key'] = "RPM-GPG-KEY-EPEL-6" - else - default['yum']['epel']['key'] = "RPM-GPG-KEY-EPEL" - end -end - -default['yum']['epel']['key_url'] = "http://dl.fedoraproject.org/pub/epel/#{node['yum']['epel']['key']}" -default['yum']['epel']['includepkgs'] = nil -default['yum']['epel']['exclude'] = nil diff --git a/chef/cookbooks/yum/attributes/remi.rb b/chef/cookbooks/yum/attributes/remi.rb deleted file mode 100644 index b80ee2c..0000000 --- a/chef/cookbooks/yum/attributes/remi.rb +++ /dev/null @@ -1,30 +0,0 @@ -# -# Cookbook Name:: yum -# Attributes:: remi -# -# Copyright 2011, Opscode, Inc. -# -# 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. -# - -case node['platform'] -when "fedora" - default['yum']['remi']['url'] = "http://rpms.famillecollet.com/fedora/#{node['platform_version'].to_i}/remi/mirror" -else - default['yum']['remi']['url'] = "http://rpms.famillecollet.com/enterprise/#{node['platform_version'].to_i}/remi/mirror" -end - -default['yum']['remi']['key'] = "RPM-GPG-KEY-remi" -default['yum']['remi']['key_url'] = "http://rpms.famillecollet.com/#{node['yum']['remi']['key']}" -default['yum']['remi']['includepkgs'] = nil -default['yum']['remi']['exclude'] = nil diff --git a/chef/cookbooks/yum/files/default/RPM-GPG-KEY-EPEL-6 b/chef/cookbooks/yum/files/default/RPM-GPG-KEY-EPEL-6 deleted file mode 100644 index 87a5035..0000000 --- a/chef/cookbooks/yum/files/default/RPM-GPG-KEY-EPEL-6 +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.4.5 (GNU/Linux) - -mQINBEvSKUIBEADLGnUj24ZVKW7liFN/JA5CgtzlNnKs7sBg7fVbNWryiE3URbn1 -JXvrdwHtkKyY96/ifZ1Ld3lE2gOF61bGZ2CWwJNee76Sp9Z+isP8RQXbG5jwj/4B -M9HK7phktqFVJ8VbY2jfTjcfxRvGM8YBwXF8hx0CDZURAjvf1xRSQJ7iAo58qcHn -XtxOAvQmAbR9z6Q/h/D+Y/PhoIJp1OV4VNHCbCs9M7HUVBpgC53PDcTUQuwcgeY6 -pQgo9eT1eLNSZVrJ5Bctivl1UcD6P6CIGkkeT2gNhqindRPngUXGXW7Qzoefe+fV -QqJSm7Tq2q9oqVZ46J964waCRItRySpuW5dxZO34WM6wsw2BP2MlACbH4l3luqtp -Xo3Bvfnk+HAFH3HcMuwdaulxv7zYKXCfNoSfgrpEfo2Ex4Im/I3WdtwME/Gbnwdq -3VJzgAxLVFhczDHwNkjmIdPAlNJ9/ixRjip4dgZtW8VcBCrNoL+LhDrIfjvnLdRu -vBHy9P3sCF7FZycaHlMWP6RiLtHnEMGcbZ8QpQHi2dReU1wyr9QgguGU+jqSXYar -1yEcsdRGasppNIZ8+Qawbm/a4doT10TEtPArhSoHlwbvqTDYjtfV92lC/2iwgO6g -YgG9XrO4V8dV39Ffm7oLFfvTbg5mv4Q/E6AWo/gkjmtxkculbyAvjFtYAQARAQAB -tCFFUEVMICg2KSA8ZXBlbEBmZWRvcmFwcm9qZWN0Lm9yZz6JAjYEEwECACAFAkvS -KUICGw8GCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRA7Sd8qBgi4lR/GD/wLGPv9 -qO39eyb9NlrwfKdUEo1tHxKdrhNz+XYrO4yVDTBZRPSuvL2yaoeSIhQOKhNPfEgT -9mdsbsgcfmoHxmGVcn+lbheWsSvcgrXuz0gLt8TGGKGGROAoLXpuUsb1HNtKEOwP -Q4z1uQ2nOz5hLRyDOV0I2LwYV8BjGIjBKUMFEUxFTsL7XOZkrAg/WbTH2PW3hrfS -WtcRA7EYonI3B80d39ffws7SmyKbS5PmZjqOPuTvV2F0tMhKIhncBwoojWZPExft -HpKhzKVh8fdDO/3P1y1Fk3Cin8UbCO9MWMFNR27fVzCANlEPljsHA+3Ez4F7uboF -p0OOEov4Yyi4BEbgqZnthTG4ub9nyiupIZ3ckPHr3nVcDUGcL6lQD/nkmNVIeLYP -x1uHPOSlWfuojAYgzRH6LL7Idg4FHHBA0to7FW8dQXFIOyNiJFAOT2j8P5+tVdq8 -wB0PDSH8yRpn4HdJ9RYquau4OkjluxOWf0uRaS//SUcCZh+1/KBEOmcvBHYRZA5J -l/nakCgxGb2paQOzqqpOcHKvlyLuzO5uybMXaipLExTGJXBlXrbbASfXa/yGYSAG -iVrGz9CE6676dMlm8F+s3XXE13QZrXmjloc6jwOljnfAkjTGXjiB7OULESed96MR -XtfLk0W5Ab9pd7tKDR6QHI7rgHXfCopRnZ2VVQ== -=V/6I ------END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/chef/cookbooks/yum/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/yum/files/default/tests/minitest/support/helpers.rb deleted file mode 100644 index cbc099e..0000000 --- a/chef/cookbooks/yum/files/default/tests/minitest/support/helpers.rb +++ /dev/null @@ -1,37 +0,0 @@ -# -# Cookbook Name:: yum_test -# Recipe:: default -# -# Copyright 2013, Opscode, Inc. -# -# 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. -# - -module Helpers - module YumTest - require 'chef/mixin/shell_out' - include Chef::Mixin::ShellOut - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - # This isn't the most efficient thing in the world, but it works - # reliably as yum will only return the repos that are actually - # enabled. It would probably be more efficient, since we're at the - # end of the successful run, to cache the output to a file and - # inspect its contents. - def repo_enabled(repo) - shell_out("yum repolist enabled --verbose | grep Repo-id").stdout.include?(repo) - end - end -end diff --git a/chef/cookbooks/yum/files/default/tests/minitest/test_test.rb b/chef/cookbooks/yum/files/default/tests/minitest/test_test.rb deleted file mode 100644 index 3db9fe2..0000000 --- a/chef/cookbooks/yum/files/default/tests/minitest/test_test.rb +++ /dev/null @@ -1,66 +0,0 @@ -# -# Cookbook Name:: yum -# -# Copyright 2013, Opscode, Inc. -# -# 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. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe "yum::test" do - # helpers includes the repo_enabled method used to test that repos - # are in fact enabled. - include Helpers::YumTest - - describe "elrepo" do - it "enables the elrepo repository" do - assert(repo_enabled("elrepo")) - end - end - - describe "epel" do - it "enables the epel repository" do - assert(repo_enabled("epel")) - end - end - - describe "ius" do - it "enables the ius repository" do - assert(repo_enabled("ius")) - end - end - - describe "remi" do - it "enables the remi repository" do - assert(repo_enabled("remi")) - end - end - - describe "repoforge" do - it "enables the repoforge repository" do - assert(repo_enabled("rpmforge")) - end - end - - describe "cook-2121" do - - it 'doesnt update the zenos-add.repo file if it exists' do - assert File.zero?('/etc/yum.repos.d/zenoss-add.repo') - end - - it 'updates the zenoss-create file' do - file('/etc/yum.repos.d/zenoss-create.repo').must_match %r[baseurl=http://dev.zenoss.com/yum/stable/] - end - end -end diff --git a/chef/cookbooks/yum/metadata.json b/chef/cookbooks/yum/metadata.json index 45467df..69234ba 100644 --- a/chef/cookbooks/yum/metadata.json +++ b/chef/cookbooks/yum/metadata.json @@ -1,8 +1,8 @@ { "name": "yum", - "version": "3.0.6", + "version": "3.1.4", "description": "Configures various yum components on Red Hat-like systems", - "long_description": "yum Cookbook\n============\n\nThe Yum cookbook exposes the `yum_globalconfig` and `yum_repository`\nresources that allows a user to both control global behavior and make\nindividual Yum repositories available for use. These resources aim to\nallow the user to configure all options listed in the `yum.conf` man\npage, found at http://linux.die.net/man/5/yum.conf\n\nNOTES\n-----\nWARNING: Yum cookbook version 3.0.0 and above contain non-backwards\ncompatible breaking changes and will not work with cookbooks written\nagainst the 2.x and 1.x series. Changes have been made to the\nyum_repository resource, and the yum_key resource has been eliminated\nentirely. Recipes have been eliminated and moved into their own\ncookbooks. Please lock yum to the 2.x series in your Chef environments\nuntil all dependent cookbooks have been ported.\n\nRequirements\n------------\n* Chef 11 or higher\n* Ruby 1.9 (preferably from the Chef full-stack installer)\n* RHEL5, RHEL6, or other platforms within the family\n\nResources/Providers\n-------------------\n### yum_repository\nThis resource manages a yum repository configuration file at\n/etc/yum.repos.d/`repositoryid`.repo. When the file needs to be\nrepaired, it calls yum-makecache so packages in the repo become\navailable to the next resource.\n\n#### Example\n``` ruby\n# add the Zenoss repository\nyum_repository 'zenoss' do\n description \"Zenoss Stable repo\"\n baseurl \"http://dev.zenoss.com/yum/stable/\"\n gpgkey 'http://dev.zenoss.com/yum/RPM-GPG-KEY-zenoss'\n action :create\nend\n\n# add the EPEL repo\nyum_repository 'epel' do\n description 'Extra Packages for Enterprise Linux'\n mirrorlist 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch'\n gpgkey 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'\n action :create\nend\n```\n\n``` ruby\n# delete CentOS-Media repo\nyum_repository 'CentOS-Media' do\n action :delete\nend\n```\n\n#### Actions\n- `:create` - creates a repository file and builds the repository listing\n- `:delete` - deletes the repository file\n\n#### Parameters\n* `baseurl` - Must be a URL to the directory where the yum repository's\n 'repodata' directory lives. Can be an http://, ftp:// or file://\n URL. You can specify multiple URLs in one baseurl statement.\n* `cost` - relative cost of accessing this repository. Useful for\n weighing one repo's packages as greater/less than any other.\n defaults to 1000\n* `description` - Maps to the 'name' parameter in a repository .conf.\n Descriptive name for the repository channel. This directive must be\n specified.\n* `enabled` - Either `true` or `false`. This tells yum whether or not use this repository.\n* `enablegroups` - Either `true` or `false`. Determines whether yum\n will allow the use of package groups for this repository. Default is\n `true` (package groups are allowed).\n* `exclude` - List of packages to exclude from updates or installs. This\n should be a space separated list in a single string. Shell globs using wildcards (eg. *\n and ?) are allowed.\n* `failovermethod` - Either 'roundrobin' or 'priority'.\n* `fastestmirror_enabled` - Either `true` or `false`\n* `gpgcheck` - Either `true` or `false`. This tells yum whether or not\n it should perform a GPG signature check on packages. When this is\n set in the [main] section it sets the default for all repositories.\n The default is `true`.\n* `gpgkey` - A URL pointing to the ASCII-armored GPG key file for the\n repository. This option is used if yum needs a public key to verify\n a package and the required key hasn't been imported into the RPM\n database. If this option is set, yum will automatically import the\n key from the specified URL.\n* `http_caching` - Either 'all', 'packages', or 'none'. Determines how\n upstream HTTP caches are instructed to handle any HTTP downloads\n that Yum does. Defaults to 'all'\n* `includepkgs` - Inverse of exclude. This is a list of packages you\n want to use from a repository. If this option lists only one package\n then that is all yum will ever see from the repository. Defaults to\n an empty list.\n* `keepalive` - Either `true` or `false`. This tells yum whether or not\n HTTP/1.1 keepalive should be used with this repository. \n* `max_retries` - Set the number of times any attempt to retrieve a file\n should retry before returning an error. Setting this to '0' makes\n yum try forever. Default is '10'.\n* `metadata_expire` - Time (in seconds) after which the metadata will\n expire. So that if the current metadata downloaded is less than this\n many seconds old then yum will not update the metadata against the\n repository. If you find that yum is not downloading information on\n updates as often as you would like lower the value of this option.\n You can also change from the default of using seconds to using days,\n hours or minutes by appending a d, h or m respectively. The default\n is 6 hours, to compliment yum-updatesd running once an hour. It's\n also possible to use the word \"never\", meaning that the metadata\n will never expire. Note that when using a metalink file the metalink\n must always be newer than the metadata for the repository, due to\n the validation, so this timeout also applies to the metalink file.\n* `mirrorlist` - Specifies a URL to a file containing a list of\n baseurls. This can be used instead of or with the baseurl option.\n Substitution variables, described below, can be used with this\n option. As a special hack is the mirrorlist URL contains the word\n \"metalink\" then the value of mirrorlist is copied to metalink (if\n metalink is not set)\n* `mirror_expire` - Time (in seconds) after which the mirrorlist locally\n cached will expire. If the current mirrorlist is less than this many\n seconds old then yum will not download another copy of the\n mirrorlist, it has the same extra format as metadata_expire. If you\n find that yum is not downloading the mirrorlists as often as you\n would like lower the value of this option.\n* `mirrorlist_expire` - alias for mirror_expire\n* `priority` - When the yum-priorities plug-in is enabled, you set\n priorities on repository entries, where N is an integer from 1 to 99. The\n default priority for repositories is 99.\n* `proxy` - URL to the proxy server that yum should use.\n* `proxy_username` - username to use for proxy\n* `proxy_password` - password for this proxy\n* `report_instanceid` - Report instance ID when using Amazon Linux AMIs\n and repositories\n* `repositoryid` - Must be a unique name for each repository, one word.\n Defaults to name attribute.\n* `sslcacert` - Path to the directory containing the databases of the\n certificate authorities yum should use to verify SSL certificates.\n Defaults to none - uses system default\n* `sslclientcert` - Path to the SSL client certificate yum should use to\n connect to repos/remote sites Defaults to none. \n* `sslclientkey` - Path to the SSL client key yum should use to connect\n to repos/remote sites Defaults to none.\n* `sslverify` - Either `true` or `false`. Determines if yum will verify SSL certificates/hosts. Defaults to `true`\n* `timeout` - Number of seconds to wait for a connection before timing\n out. Defaults to 30 seconds. This may be too short of a time for\n extremely overloaded sites.\n\n### yum_globalconfig\nThis renders a template with global yum configuration parameters. The\ndefault recipe uses it to render `/etc/yum.conf`. It is flexible\nenough to be used in other scenarios, such as building RPMs in\nisolation by modifying `installroot`. \n\n#### Example\n``` ruby\nyum_globalconfig '/my/chroot/etc/yum.conf' do\n cachedir '/my/chroot/etc/yum.conf'\n keepcache 'yes'\n debuglevel '2'\n installroot '/my/chroot'\n action :create\nend\n```\n\n#### Parameters\n`yum_globalconfig` can take most of the same parameters as a\n`yum_repository`, plus more, too numerous to describe here. Below are\na few of the more commonly used ones. For a complete list, please\nconsult the `yum.conf` man page, found here:\nhttp://linux.die.net/man/5/yum.conf\n\n* `cachedir` - Directory where yum should store its cache and db\n files. The default is '/var/cache/yum'. \n* `keepcache` - Either `true` or `false`. Determines whether or not\n yum keeps the cache of headers and packages after successful\n installation. Default is `true` (keep files)\n* `debuglevel` - Debug message output level. Practical range is 0-10.\n Default is '2'. \n* `exclude` - List of packages to exclude from updates or installs.\n This should be a space separated list. Shell globs using wildcards\n (eg. * and ?) are allowed. \n* `installonlypkgs` = List of package provides that should only ever\n be installed, never updated. Kernels in particular fall into this\n category. Defaults to kernel, kernel-bigmem, kernel-enterprise,\n kernel-smp, kernel-debug, kernel-unsupported, kernel-source,\n kernel-devel, kernel-PAE, kernel-PAE-debug.\n* `logfile` - Full directory and file name for where yum should write\n its log file.\n* `exactarch` - Either `true` or `false`. Set to `true` to make 'yum update' only\n update the architectures of packages that you have installed. ie:\n with this enabled yum will not install an i686 package to update an\n x86_64 package. Default is `true`\n* `gpgcheck` - Either `true` or `false`. This tells yum whether or not\n it should perform a GPG signature check on the packages gotten from\n this repository.\n \nRecipes\n-------\n* `default` - Configures `yum_globalconfig[/etc/yum.conf]` with values\n found in node attributes at `node['yum']['main']`\n\nAttributes\n----------\nThe following attributes are set by default\n\n``` ruby\ndefault['yum']['main']['cachedir'] = '/var/cache/yum/$basearch/$releasever'\ndefault['yum']['main']['keepcache'] = false\ndefault['yum']['main']['debuglevel'] = nil\ndefault['yum']['main']['exclude'] = nil\ndefault['yum']['main']['logfile'] = '/var/log/yum.log'\ndefault['yum']['main']['exactarch'] = nil\ndefault['yum']['main']['obsoletes'] = nil\ndefault['yum']['main']['installonly_limit'] = nil\ndefault['yum']['main']['installonlypkgs'] = nil\ndefault['yum']['main']['installroot'] = nil\n```\n\nRelated Cookbooks\n-----------------\nRecipes from older versions of this cookbook have been moved\nindividual cookbooks. Recipes for managing platform yum configurations\nand installing specific repositories can be found in one (or more!) of\nthe following cookbook.\n\n* yum-centos\n* yum-fedora\n* yum-amazon\n* yum-epel\n* yum-elrepo\n* yum-repoforge\n* yum-ius\n* yum-percona\n* yum-pgdg\n\nUsage\n-----\nPut `depends 'yum'` in your metadata.rb to gain access to the\nyum_repository resource.\n\nLicense & Authors\n-----------------\n- Author:: Eric G. Wolfe\n- Author:: Matt Ray ()\n- Author:: Joshua Timberman ()\n- Author:: Sean OMeara ()\n\n```text\nCopyright:: 2011 Eric G. Wolfe\nCopyright:: 2013 Chef\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", + "long_description": "yum Cookbook\n============\n\nThe Yum cookbook exposes the `yum_globalconfig` and `yum_repository`\nresources that allows a user to both control global behavior and make\nindividual Yum repositories available for use. These resources aim to\nallow the user to configure all options listed in the `yum.conf` man\npage, found at http://linux.die.net/man/5/yum.conf\n\nNOTES\n-----\nWARNING: Yum cookbook version 3.0.0 and above contain non-backwards\ncompatible breaking changes and will not work with cookbooks written\nagainst the 2.x and 1.x series. Changes have been made to the\nyum_repository resource, and the yum_key resource has been eliminated\nentirely. Recipes have been eliminated and moved into their own\ncookbooks. Please lock yum to the 2.x series in your Chef environments\nuntil all dependent cookbooks have been ported.\n\nRequirements\n------------\n* Chef 11 or higher\n* Ruby 1.9 (preferably from the Chef full-stack installer)\n* RHEL5, RHEL6, or other platforms within the family\n\nResources/Providers\n-------------------\n### yum_repository\nThis resource manages a yum repository configuration file at\n/etc/yum.repos.d/`repositoryid`.repo. When the file needs to be\nrepaired, it calls yum-makecache so packages in the repo become\navailable to the next resource.\n\n#### Example\n``` ruby\n# add the Zenoss repository\nyum_repository 'zenoss' do\n description \"Zenoss Stable repo\"\n baseurl \"http://dev.zenoss.com/yum/stable/\"\n gpgkey 'http://dev.zenoss.com/yum/RPM-GPG-KEY-zenoss'\n action :create\nend\n\n# add the EPEL repo\nyum_repository 'epel' do\n description 'Extra Packages for Enterprise Linux'\n mirrorlist 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch'\n gpgkey 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'\n action :create\nend\n```\n\n``` ruby\n# delete CentOS-Media repo\nyum_repository 'CentOS-Media' do\n action :delete\nend\n```\n\n#### Actions\n- `:create` - creates a repository file and builds the repository listing\n- `:delete` - deletes the repository file\n\n#### Parameters\n* `baseurl` - Must be a URL to the directory where the yum repository's\n 'repodata' directory lives. Can be an http://, ftp:// or file://\n URL. You can specify multiple URLs in one baseurl statement.\n* `cost` - relative cost of accessing this repository. Useful for\n weighing one repo's packages as greater/less than any other.\n defaults to 1000\n* `description` - Maps to the 'name' parameter in a repository .conf.\n Descriptive name for the repository channel. This directive must be\n specified.\n* `enabled` - Either `true` or `false`. This tells yum whether or not use this repository.\n* `enablegroups` - Either `true` or `false`. Determines whether yum\n will allow the use of package groups for this repository. Default is\n `true` (package groups are allowed).\n* `exclude` - List of packages to exclude from updates or installs. This\n should be a space separated list in a single string. Shell globs using wildcards (eg. *\n and ?) are allowed.\n* `failovermethod` - Either 'roundrobin' or 'priority'.\n* `fastestmirror_enabled` - Either `true` or `false`\n* `gpgcheck` - Either `true` or `false`. This tells yum whether or not\n it should perform a GPG signature check on packages. When this is\n set in the [main] section it sets the default for all repositories.\n The default is `true`.\n* `gpgkey` - A URL pointing to the ASCII-armored GPG key file for the\n repository. This option is used if yum needs a public key to verify\n a package and the required key hasn't been imported into the RPM\n database. If this option is set, yum will automatically import the\n key from the specified URL.\n* `http_caching` - Either 'all', 'packages', or 'none'. Determines how\n upstream HTTP caches are instructed to handle any HTTP downloads\n that Yum does. Defaults to 'all'\n* `includepkgs` - Inverse of exclude. This is a list of packages you\n want to use from a repository. If this option lists only one package\n then that is all yum will ever see from the repository. Defaults to\n an empty list.\n* `keepalive` - Either `true` or `false`. This tells yum whether or not\n HTTP/1.1 keepalive should be used with this repository. \n* `max_retries` - Set the number of times any attempt to retrieve a file\n should retry before returning an error. Setting this to '0' makes\n yum try forever. Default is '10'.\n* `metadata_expire` - Time (in seconds) after which the metadata will\n expire. So that if the current metadata downloaded is less than this\n many seconds old then yum will not update the metadata against the\n repository. If you find that yum is not downloading information on\n updates as often as you would like lower the value of this option.\n You can also change from the default of using seconds to using days,\n hours or minutes by appending a d, h or m respectively. The default\n is 6 hours, to compliment yum-updatesd running once an hour. It's\n also possible to use the word \"never\", meaning that the metadata\n will never expire. Note that when using a metalink file the metalink\n must always be newer than the metadata for the repository, due to\n the validation, so this timeout also applies to the metalink file.\n* `mirrorlist` - Specifies a URL to a file containing a list of\n baseurls. This can be used instead of or with the baseurl option.\n Substitution variables, described below, can be used with this\n option. As a special hack is the mirrorlist URL contains the word\n \"metalink\" then the value of mirrorlist is copied to metalink (if\n metalink is not set)\n* `mirror_expire` - Time (in seconds) after which the mirrorlist locally\n cached will expire. If the current mirrorlist is less than this many\n seconds old then yum will not download another copy of the\n mirrorlist, it has the same extra format as metadata_expire. If you\n find that yum is not downloading the mirrorlists as often as you\n would like lower the value of this option.\n* `mirrorlist_expire` - alias for mirror_expire\n* `priority` - When the yum-priorities plug-in is enabled, you set\n priorities on repository entries, where N is an integer from 1 to 99. The\n default priority for repositories is 99.\n* `proxy` - URL to the proxy server that yum should use.\n* `proxy_username` - username to use for proxy\n* `proxy_password` - password for this proxy\n* `report_instanceid` - Report instance ID when using Amazon Linux AMIs\n and repositories\n* `repositoryid` - Must be a unique name for each repository, one word.\n Defaults to name attribute.\n* `source` - Use a custom template source instead of the default one\n in the yum cookbook\n* `sslcacert` - Path to the directory containing the databases of the\n certificate authorities yum should use to verify SSL certificates.\n Defaults to none - uses system default\n* `sslclientcert` - Path to the SSL client certificate yum should use to\n connect to repos/remote sites Defaults to none. \n* `sslclientkey` - Path to the SSL client key yum should use to connect\n to repos/remote sites Defaults to none.\n* `sslverify` - Either `true` or `false`. Determines if yum will verify SSL certificates/hosts. Defaults to `true`\n* `timeout` - Number of seconds to wait for a connection before timing\n out. Defaults to 30 seconds. This may be too short of a time for\n extremely overloaded sites.\n\n### yum_globalconfig\nThis renders a template with global yum configuration parameters. The\ndefault recipe uses it to render `/etc/yum.conf`. It is flexible\nenough to be used in other scenarios, such as building RPMs in\nisolation by modifying `installroot`. \n\n#### Example\n``` ruby\nyum_globalconfig '/my/chroot/etc/yum.conf' do\n cachedir '/my/chroot/etc/yum.conf'\n keepcache 'yes'\n debuglevel '2'\n installroot '/my/chroot'\n action :create\nend\n```\n\n#### Parameters\n`yum_globalconfig` can take most of the same parameters as a\n`yum_repository`, plus more, too numerous to describe here. Below are\na few of the more commonly used ones. For a complete list, please\nconsult the `yum.conf` man page, found here:\nhttp://linux.die.net/man/5/yum.conf\n\n* `cachedir` - Directory where yum should store its cache and db\n files. The default is '/var/cache/yum'. \n* `keepcache` - Either `true` or `false`. Determines whether or not\n yum keeps the cache of headers and packages after successful\n installation. Default is `true` (keep files)\n* `debuglevel` - Debug message output level. Practical range is 0-10.\n Default is '2'. \n* `exclude` - List of packages to exclude from updates or installs.\n This should be a space separated list. Shell globs using wildcards\n (eg. * and ?) are allowed. \n* `installonlypkgs` = List of package provides that should only ever\n be installed, never updated. Kernels in particular fall into this\n category. Defaults to kernel, kernel-bigmem, kernel-enterprise,\n kernel-smp, kernel-debug, kernel-unsupported, kernel-source,\n kernel-devel, kernel-PAE, kernel-PAE-debug.\n* `logfile` - Full directory and file name for where yum should write\n its log file.\n* `exactarch` - Either `true` or `false`. Set to `true` to make 'yum update' only\n update the architectures of packages that you have installed. ie:\n with this enabled yum will not install an i686 package to update an\n x86_64 package. Default is `true`\n* `gpgcheck` - Either `true` or `false`. This tells yum whether or not\n it should perform a GPG signature check on the packages gotten from\n this repository.\n \nRecipes\n-------\n* `default` - Configures `yum_globalconfig[/etc/yum.conf]` with values\n found in node attributes at `node['yum']['main']`\n\nAttributes\n----------\nThe following attributes are set by default\n\n``` ruby\ndefault['yum']['main']['cachedir'] = '/var/cache/yum/$basearch/$releasever'\ndefault['yum']['main']['keepcache'] = false\ndefault['yum']['main']['debuglevel'] = nil\ndefault['yum']['main']['exclude'] = nil\ndefault['yum']['main']['logfile'] = '/var/log/yum.log'\ndefault['yum']['main']['exactarch'] = nil\ndefault['yum']['main']['obsoletes'] = nil\ndefault['yum']['main']['installonly_limit'] = nil\ndefault['yum']['main']['installonlypkgs'] = nil\ndefault['yum']['main']['installroot'] = nil\n```\n\nRelated Cookbooks\n-----------------\nRecipes from older versions of this cookbook have been moved\nindividual cookbooks. Recipes for managing platform yum configurations\nand installing specific repositories can be found in one (or more!) of\nthe following cookbook.\n\n* yum-centos\n* yum-fedora\n* yum-amazon\n* yum-epel\n* yum-elrepo\n* yum-repoforge\n* yum-ius\n* yum-percona\n* yum-pgdg\n\nUsage\n-----\nPut `depends 'yum'` in your metadata.rb to gain access to the\nyum_repository resource.\n\nLicense & Authors\n-----------------\n- Author:: Eric G. Wolfe\n- Author:: Matt Ray ()\n- Author:: Joshua Timberman ()\n- Author:: Sean OMeara ()\n\n```text\nCopyright:: 2011 Eric G. Wolfe\nCopyright:: 2013 Chef\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", "maintainer": "Chef", "maintainer_email": "cookbooks@getchef.com", "license": "Apache 2.0", diff --git a/chef/cookbooks/yum/metadata.rb b/chef/cookbooks/yum/metadata.rb index a676ef6..31b3039 100644 --- a/chef/cookbooks/yum/metadata.rb +++ b/chef/cookbooks/yum/metadata.rb @@ -1,37 +1,12 @@ -name "yum" -maintainer "Opscode, Inc." -maintainer_email "cookbooks@opscode.com" -license "Apache 2.0" +name 'yum' +maintainer 'Chef' +maintainer_email 'cookbooks@getchef.com' +license 'Apache 2.0' +description 'Configures various yum components on Red Hat-like systems' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "2.3.3" -recipe "yum", "Empty recipe." -recipe "yum::yum", "Manages yum configuration" +version '3.1.4' -%w{ redhat centos scientific amazon }.each do |os| - supports os, ">= 5.0" -end - -attribute "yum/exclude", - :display_name => "yum.conf exclude", - :description => "List of packages to exclude from updates or installs. This should be an array. Shell globs using wildcards (eg. * and ?) are allowed.", - :required => "optional" - -attribute "yum/installonlypkgs", - :display_name => "yum.conf installonlypkgs", - :description => "List of packages that should only ever be installed, never updated. Kernels in particular fall into this category. Defaults to kernel, kernel-smp, kernel-bigmem, kernel-enterprise, kernel-debug, kernel-unsupported.", - :required => "optional" - -attribute "yum/proxy", - :display_name => "yum.conf proxy", - :description => "Set the http URL for proxy to use in yum.conf", - :required => "optional" - -attribute "yum/proxy_username", - :display_name => "yum.conf proxy_username", - :description => "Set the proxy_username to use for yum.conf", - :required => "optional" - -attribute "yum/proxy_password", - :display_name => "yum.conf proxy_password", - :description => "Set the proxy_password to use for yum.conf", - :required => "optional" +supports 'redhat' +supports 'centos' +supports 'amazon' +supports 'fedora' diff --git a/chef/cookbooks/yum/providers/key.rb b/chef/cookbooks/yum/providers/key.rb deleted file mode 100644 index 7a08bcf..0000000 --- a/chef/cookbooks/yum/providers/key.rb +++ /dev/null @@ -1,83 +0,0 @@ -# -# Cookbook Name:: yum -# Provider:: key -# -# Copyright 2010, Tippr Inc. -# Copyright 2011, Opscode, Inc. -# -# 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. -# - -def whyrun_supported? - true -end - -action :add do - unless ::File.exists?("/etc/pki/rpm-gpg/#{new_resource.key}") - Chef::Log.info "Adding #{new_resource.key} GPG key to /etc/pki/rpm-gpg/" - - if node['platform_version'].to_i <= 5 - package "gnupg" - elsif node['platform_version'].to_i >= 6 - package "gnupg2" - end - - execute "import-rpm-gpg-key-#{new_resource.key}" do - command "rpm --import /etc/pki/rpm-gpg/#{new_resource.key}" - action :nothing - not_if <<-EOH - function packagenames_for_keyfile() { - local filename="$1" - gpg \ - --with-fingerprint \ - --with-colons \ - --fixed-list-mode \ - "$filename" \ - | gawk -F: '/^pub/ { print tolower(sprintf("gpg-pubkey-%s-%x\\n", substr($5, length($5)-8+1), $6)) }' - } - - for pkgname in $(packagenames_for_keyfile "/etc/pki/rpm-gpg/#{new_resource.key}"); do - if [[ $pkgname ]] && ! rpm -q $pkgname ; then - exit 1; - fi; - done - - exit 0 - EOH - end - - #download the file if necessary - unless new_resource.url.nil? - # remote_file "/etc/pki/rpm-gpg/#{new_resource.key}" do - # source new_resource.url - # mode "0644" - # notifies :run, "execute[import-rpm-gpg-key-#{new_resource.key}]", :immediately - # end - cookbook_file "/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6" do - source "RPM-GPG-KEY-EPEL-6" - mode 0644 - end - end - - end -end - -action :remove do - if ::File.exists?("/etc/pki/rpm-gpg/#{new_resource.key}") - Chef::Log.info "Removing #{new_resource.key} key from /etc/pki/rpm-gpg/" - file "/etc/pki/rpm-gpg/#{new_resource.key}" do - action :delete - end - new_resource.updated_by_last_action(true) - end -end diff --git a/chef/cookbooks/yum/providers/repository.rb b/chef/cookbooks/yum/providers/repository.rb index 87ee598..4fbc232 100644 --- a/chef/cookbooks/yum/providers/repository.rb +++ b/chef/cookbooks/yum/providers/repository.rb @@ -2,8 +2,8 @@ # Cookbook Name:: yum # Provider:: repository # -# Copyright 2010, Tippr Inc. -# Copyright 2011, Opscode, Inc.. +# Author:: Sean OMeara +# Copyright 2013, Chef # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,108 +18,70 @@ # limitations under the License. # -# note that deletion does not remove GPG keys, either from the repo or -# /etc/pki/rpm-gpg; this is a design decision. +# In Chef 11 and above, calling the use_inline_resources method will +# make Chef create a new "run_context". When an action is called, any +# nested resources are compiled and converged in isolation from the +# recipe that calls it. + +# Allow for Chef 10 support +use_inline_resources if defined?(use_inline_resources) def whyrun_supported? true end -action :add do - unless ::File.exists?("/etc/yum.repos.d/#{new_resource.repo_name}.repo") - Chef::Log.info "Adding #{new_resource.repo_name} repository to /etc/yum.repos.d/#{new_resource.repo_name}.repo" - repo_config - end -end +action :create do + if node['local_repo'].nil? or node['local_repo'].empty? + # Hack around the lack of "use_inline_resources" before Chef 11 by + # uniquely naming the execute[yum-makecache] resources. Set the + # notifies timing to :immediately for the same reasons. Remove both + # of these when dropping Chef 10 support. -action :create do - Chef::Log.info "Adding and updating #{new_resource.repo_name} repository in /etc/yum.repos.d/#{new_resource.repo_name}.repo" - repo_config -end - -action :remove do - if ::File.exists?("/etc/yum.repos.d/#{new_resource.repo_name}.repo") - Chef::Log.info "Removing #{new_resource.repo_name} repository from /etc/yum.repos.d/" - file "/etc/yum.repos.d/#{new_resource.repo_name}.repo" do - action :delete - end - new_resource.updated_by_last_action(true) - end -end - -action :update do - repos ||= {} - # If the repo is already enabled/disabled as per the resource, we don't want to converge the template resource. - if ::File.exists?("/etc/yum.repos.d/#{new_resource.repo_name}.repo") - ::File.open("/etc/yum.repos.d/#{new_resource.repo_name}.repo") do |file| - repo_name ||= nil - file.each_line do |line| - case line - when /^\[(\S+)\]/ - repo_name = $1 - repos[repo_name] ||= {} - when /^(\S+?)=(.*)$/ - param, value = $1, $2 - repos[repo_name][param] = value - else - end + template "/etc/yum.repos.d/#{new_resource.repositoryid}.repo" do + if new_resource.source.nil? + source 'repo.erb' + cookbook 'yum' + else + source new_resource.source end + mode '0644' + variables(:config => new_resource) + notifies :run, "execute[yum-makecache-#{new_resource.repositoryid}]", :immediately + notifies :create, "ruby_block[yum-cache-reload-#{new_resource.repositoryid}]", :immediately + end + + # get the metadata for this repo only + execute "yum-makecache-#{new_resource.repositoryid}" do + command "yum -q makecache --disablerepo=* --enablerepo=#{new_resource.repositoryid}" + action :nothing + end + + # reload internal Chef yum cache + ruby_block "yum-cache-reload-#{new_resource.repositoryid}" do + block { Chef::Provider::Package::Yum::YumCache.instance.reload } + action :nothing end - else - Chef::Log.error "Repo /etc/yum.repos.d/#{new_resource.repo_name}.repo does not exist, you must create it first" - end - if repos[new_resource.repo_name]['enabled'].to_i != new_resource.enabled - Chef::Log.info "Updating #{new_resource.repo_name} repository in /etc/yum.repos.d/#{new_resource.repo_name}.repo (setting enabled=#{new_resource.enabled})" - repo_config - else - Chef::Log.debug "Repository /etc/yum.repos.d/#{new_resource.repo_name}.repo is already set to enabled=#{new_resource.enabled}, skipping" end end -private +action :delete do + file "/etc/yum.repos.d/#{new_resource.repositoryid}.repo" do + action :delete + notifies :run, "execute[yum clean #{new_resource.repositoryid}]", :immediately + notifies :create, "ruby_block[yum-cache-reload-#{new_resource.repositoryid}]", :immediately + end -def repo_config - #import the gpg key. If it needs to be downloaded or imported from a cookbook - #that can be done in the calling recipe - if new_resource.key then - yum_key new_resource.key - end - #get the metadata - execute "yum-makecache" do - command "yum -q makecache" + execute "yum clean #{new_resource.repositoryid}" do + command "yum clean all --disablerepo=* --enablerepo=#{new_resource.repositoryid}" + only_if "yum repolist | grep -P '^#{new_resource.repositoryid}([ \t]|$)'" action :nothing end - #reload internal Chef yum cache - ruby_block "reload-internal-yum-cache" do - block do - Chef::Provider::Package::Yum::YumCache.instance.reload - end + + ruby_block "yum-cache-reload-#{new_resource.repositoryid}" do + block { Chef::Provider::Package::Yum::YumCache.instance.reload } action :nothing end - #write out the file - template "/etc/yum.repos.d/#{new_resource.repo_name}.repo" do - cookbook "yum" - source "repo.erb" - mode "0644" - variables({ - :repo_name => new_resource.repo_name, - :description => new_resource.description, - :url => new_resource.url, - :mirrorlist => new_resource.mirrorlist, - :key => new_resource.key, - :enabled => new_resource.enabled, - :type => new_resource.type, - :failovermethod => new_resource.failovermethod, - :bootstrapurl => new_resource.bootstrapurl, - :includepkgs => new_resource.includepkgs, - :exclude => new_resource.exclude, - :priority => new_resource.priority, - :metadata_expire => new_resource.metadata_expire, - :type => new_resource.type - }) - if new_resource.make_cache - notifies :run, "execute[yum-makecache]", :immediately - notifies :create, "ruby_block[reload-internal-yum-cache]", :immediately - end - end end + +alias_method :action_add, :action_create +alias_method :action_remove, :action_delete diff --git a/chef/cookbooks/yum/recipes/default.rb b/chef/cookbooks/yum/recipes/default.rb index 9bc90f2..37dd36d 100644 --- a/chef/cookbooks/yum/recipes/default.rb +++ b/chef/cookbooks/yum/recipes/default.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: yum -# Recipe:: default +# Author:: Sean OMeara () +# Recipe:: yum::default # -# Copyright 2011, Opscode, Inc. +# Copyright 2013, Chef # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,4 +15,19 @@ # 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. -# + +yum_globalconfig '/etc/yum.conf' do + cachedir node['yum']['main']['cachedir'] + keepcache node['yum']['main']['keepcache'] + debuglevel node['yum']['main']['debuglevel'] + exclude node['yum']['main']['exclude'] + logfile node['yum']['main']['logfile'] + exactarch node['yum']['main']['exactarch'] + obsoletes node['yum']['main']['obsoletes'] + proxy node['yum']['main']['proxy'] + installonly_limit node['yum']['main']['installonly_limit'] + installonlypkgs node['yum']['main']['installonlypkgs'] + installroot node['yum']['main']['installroot'] + distroverpkg node['yum']['main']['distroverpkg'] + action :create +end diff --git a/chef/cookbooks/yum/recipes/elrepo.rb b/chef/cookbooks/yum/recipes/elrepo.rb deleted file mode 100644 index a54981d..0000000 --- a/chef/cookbooks/yum/recipes/elrepo.rb +++ /dev/null @@ -1,31 +0,0 @@ -# -# Cookbook Name:: yum -# Recipe:: elrepo -# -# Copyright:: Copyright (c) 2013 Opscode, Inc. -# -# 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. - -yum_key node['yum']['elrepo']['key'] do - url node['yum']['elrepo']['key_url'] - action :add -end - -yum_repository "elrepo" do - description "ELRepo.org Community Enterprise Linux Extras Repository" - key node['yum']['elrepo']['key'] - mirrorlist node['yum']['elrepo']['url'] - includepkgs node['yum']['elrepo']['includepkgs'] - exclude node['yum']['elrepo']['exclude'] - action :create -end diff --git a/chef/cookbooks/yum/recipes/epel.rb b/chef/cookbooks/yum/recipes/epel.rb deleted file mode 100644 index be1e2aa..0000000 --- a/chef/cookbooks/yum/recipes/epel.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Author:: Joshua Timberman () -# Cookbook Name:: yum -# Recipe:: epel -# -# Copyright:: Copyright (c) 2011 Opscode, Inc. -# Copyright 2010, Eric G. Wolfe -# Copyright 2010, Tippr Inc. -# -# 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. - -yum_key node['yum']['epel']['key'] do - url node['yum']['epel']['key_url'] - action :add -end - -yum_repository "epel" do - description "Extra Packages for Enterprise Linux" - key node['yum']['epel']['key'] - url node['yum']['epel']['baseurl'] - mirrorlist node['yum']['epel']['url'] - includepkgs node['yum']['epel']['includepkgs'] - exclude node['yum']['epel']['exclude'] - action platform?('amazon') ? [:add, :update] : :add -end diff --git a/chef/cookbooks/yum/recipes/ius.rb b/chef/cookbooks/yum/recipes/ius.rb deleted file mode 100644 index 2d25290..0000000 --- a/chef/cookbooks/yum/recipes/ius.rb +++ /dev/null @@ -1,42 +0,0 @@ -# -# Author:: Joshua Timberman () -# Cookbook Name:: yum -# Recipe:: ius -# -# Copyright:: Copyright (c) 2011 Opscode, Inc. -# -# 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. - -include_recipe "yum::epel" - -package "epel-release" - -major = node['platform_version'].to_i -ius = node['yum']['ius_release'] - -remote_file "#{Chef::Config[:file_cache_path]}/ius-release-#{ius}.ius.el#{major}.noarch.rpm" do - source "http://dl.iuscommunity.org/pub/ius/stable/Redhat/#{major}/i386/ius-release-#{ius}.ius.el#{major}.noarch.rpm" - not_if "rpm -qa | grep -q '^ius-release-#{ius}'" - notifies :install, "rpm_package[ius-release]", :immediately -end - -rpm_package "ius-release" do - source "#{Chef::Config[:file_cache_path]}/ius-release-#{ius}.ius.el#{major}.noarch.rpm" - only_if { ::File.exists?("#{Chef::Config[:file_cache_path]}/ius-release-#{ius}.ius.el#{major}.noarch.rpm") } - action :nothing -end - -file "ius-release-cleanup" do - path "#{Chef::Config[:file_cache_path]}/ius-release-#{ius}.ius.el#{major}.noarch.rpm" - action :delete -end diff --git a/chef/cookbooks/yum/recipes/remi.rb b/chef/cookbooks/yum/recipes/remi.rb deleted file mode 100644 index 5ce5ff6..0000000 --- a/chef/cookbooks/yum/recipes/remi.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Author:: Takeshi KOMIYA () -# Cookbook Name:: yum -# Recipe:: remi -# -# Copyright:: Copyright (c) 2011 Opscode, Inc. -# -# 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. - -include_recipe "yum::epel" - -yum_key node['yum']['remi']['key'] do - url node['yum']['remi']['key_url'] - action :add -end - -yum_repository "remi" do - description "Les RPM de remi pour Enterprise Linux #{node['platform_version']} - $basearch" - key node['yum']['remi']['key'] - mirrorlist node['yum']['remi']['url'] - failovermethod "priority" - includepkgs node['yum']['remi']['includepkgs'] - exclude node['yum']['remi']['exclude'] - action :create -end diff --git a/chef/cookbooks/yum/recipes/repoforge.rb b/chef/cookbooks/yum/recipes/repoforge.rb deleted file mode 100644 index 14c1607..0000000 --- a/chef/cookbooks/yum/recipes/repoforge.rb +++ /dev/null @@ -1,41 +0,0 @@ -# -# Author:: Eric Edgar () -# Cookbook Name:: yum -# Recipe:: repoforge -# -# Copyright:: Copyright (c) 2012-2013 Opscode, Inc. -# -# 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. - -include_recipe "yum::epel" - -major = platform?("amazon") ? 6 : node['platform_version'].to_i -arch = (node['kernel']['machine'] == "i686" && major == 5) ? "i386" : node['kernel']['machine'] -repoforge = node['yum']['repoforge_release'] - -remote_file "#{Chef::Config[:file_cache_path]}/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm" do - source "http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm" - not_if "rpm -qa | grep -q '^rpmforge-release-#{repoforge}'" - notifies :install, "rpm_package[rpmforge-release]", :immediately -end - -rpm_package "rpmforge-release" do - source "#{Chef::Config[:file_cache_path]}/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm" - only_if { ::File.exists?("#{Chef::Config[:file_cache_path]}/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm") } - action :install -end - -file "repoforge-release-cleanup" do - path "#{Chef::Config[:file_cache_path]}/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm" - action :delete -end diff --git a/chef/cookbooks/yum/recipes/test.rb b/chef/cookbooks/yum/recipes/test.rb deleted file mode 100644 index 7720c3a..0000000 --- a/chef/cookbooks/yum/recipes/test.rb +++ /dev/null @@ -1,39 +0,0 @@ -# -# Cookbook:: yum -# Recipe:: test -# -# Author:: Joshua Timberman -# Copyright:: Copyright (c) 2013, Opscode, Inc -# -# 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. -# - -include_recipe "yum::epel" -include_recipe "yum::elrepo" -include_recipe "yum::ius" -include_recipe "yum::repoforge" -include_recipe "yum::yum" -include_recipe "yum::remi" - -%w{add create}.each do |act| - file "/etc/yum.repos.d/zenoss-#{act}.repo" do - action :create - end - - yum_repository "zenoss-#{act}" do - description "Zenoss Stable repo" - url "http://dev.zenoss.com/yum/stable/" - key "RPM-GPG-KEY-zenoss" - action act.to_sym - end -end diff --git a/chef/cookbooks/yum/resources/globalconfig.rb b/chef/cookbooks/yum/resources/globalconfig.rb index 6a909ef..b1df2e5 100644 --- a/chef/cookbooks/yum/resources/globalconfig.rb +++ b/chef/cookbooks/yum/resources/globalconfig.rb @@ -67,7 +67,7 @@ attribute :localpkg_gpgcheck, :kind_of => [TrueClass, FalseClass], :default => n attribute :logfile, :kind_of => String, :regex => /.*/, :default => '/var/log/yum.log' attribute :max_retries, :kind_of => String, :regex => /^\d+$/, :default => nil attribute :mdpolicy, :kind_of => String, :equal_to => %w{ instant group:primary group:small group:main group:all }, :default => nil -attribute :metadata_expire, :kind_of => String, :regex => /^\d+$/, :default => nil +attribute :metadata_expire, :kind_of => String, :regex => [/^\d+$/, /^\d+[mhd]$/, /never/], :default => nil attribute :mirrorlist_expire, :kind_of => String, :regex => /^\d+$/, :default => nil attribute :multilib_policy, :kind_of => String, :equal_to => %w{ all best }, :default => nil attribute :obsoletes, :kind_of => [TrueClass, FalseClass], :default => '1' diff --git a/chef/cookbooks/yum/resources/repository.rb b/chef/cookbooks/yum/resources/repository.rb index 1e274ca..8092f3a 100644 --- a/chef/cookbooks/yum/resources/repository.rb +++ b/chef/cookbooks/yum/resources/repository.rb @@ -2,7 +2,8 @@ # Cookbook Name:: yum # Resource:: repository # -# Copyright 2011, Opscode, Inc. +# Author:: Sean OMeara +# Copyright 2013, Chef # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,26 +18,44 @@ # limitations under the License. # -actions :add, :remove, :update, :create +actions :create, :delete, :add, :remove -#name of the repo, used for .repo filename -attribute :repo_name, :kind_of => String, :name_attribute => true -attribute :description, :kind_of => String #long description -attribute :url, :kind_of => String, :default => "" -attribute :mirrorlist, :default => false -attribute :key, :kind_of => String, :default => nil -attribute :enabled, :default => 1 -attribute :type, :kind_of => String, :default => nil -attribute :failovermethod, :kind_of => String, :default => nil -attribute :bootstrapurl, :kind_of => String, :default => nil -attribute :make_cache, :kind_of => [TrueClass, FalseClass], :default => true -attribute :includepkgs, :kind_of => String, :default => nil -attribute :exclude, :kind_of => String, :default => nil -attribute :priority, :kind_of => [Integer, String], :default => nil -attribute :metadata_expire, :kind_of => [Integer, String], :default => nil -attribute :type, :kind_of => String, :default => nil +default_action :create -def initialize(*args) - super - @action = :add -end +# http://linux.die.net/man/5/yum.conf +attribute :baseurl, :kind_of => String, :regex => /.*/ , :default => nil +attribute :cost, :kind_of => String, :regex => /^\d+$/, :default => nil +attribute :description, :kind_of => String, :regex => /.*/, :default => 'Ye Ole Rpm Repo' +attribute :enabled, :kind_of => [TrueClass, FalseClass], :default => true +attribute :enablegroups, :kind_of => [TrueClass, FalseClass], :default => nil +attribute :exclude, :kind_of => String, :regex => /.*/, :default => nil +attribute :failovermethod, :kind_of => String, :equal_to => %w{ priority roundrobin }, :default => nil +attribute :fastestmirror_enabled, :kind_of => [TrueClass, FalseClass], :default => nil +attribute :gpgcheck, :kind_of => [TrueClass, FalseClass], :default => true +attribute :gpgkey, :kind_of => String, :regex => /.*/, :default => nil +attribute :http_caching, :kind_of => String, :equal_to => %w{ packages all none }, :default => nil +attribute :include_config, :kind_of => String, :regex => /.*/, :default => nil +attribute :includepkgs, :kind_of => String, :regex => /.*/, :default => nil +attribute :keepalive, :kind_of => [TrueClass, FalseClass], :default => nil +attribute :max_retries, :kind_of => String, :regex => /.*/, :default => nil +attribute :metadata_expire, :kind_of => String, :regex => [/^\d+$/, /^\d+[mhd]$/, /never/], :default => nil +attribute :mirrorexpire, :kind_of => String, :regex => /.*/, :default => nil +attribute :mirrorlist, :kind_of => String, :regex => /.*/, :default => nil +attribute :mirror_expire, :kind_of => String, :regex => /^\d+$/, :default => nil +attribute :mirrorlist_expire, :kind_of => String, :regex => /^\d+$/, :default => nil +attribute :priority, :kind_of => String, :regex => /^(\d?[0-9]|[0-9][0-9])$/, :default => nil +attribute :proxy, :kind_of => String, :regex => /.*/, :default => nil +attribute :proxy_username, :kind_of => String, :regex => /.*/, :default => nil +attribute :proxy_password, :kind_of => String, :regex => /.*/, :default => nil +attribute :report_instanceid, :kind_of => [TrueClass, FalseClass], :default => nil +attribute :repositoryid, :kind_of => String, :regex => /.*/, :name_attribute => true +attribute :skip_if_unavailable, :kind_of => [TrueClass, FalseClass], :default => nil +attribute :source, :kind_of => String, :regex => /.*/, :default => nil +attribute :sslcacert, :kind_of => String, :regex => /.*/, :default => nil +attribute :sslclientcert, :kind_of => String, :regex => /.*/, :default => nil +attribute :sslclientkey, :kind_of => String, :regex => /.*/, :default => nil +attribute :sslverify, :kind_of => [TrueClass, FalseClass], :default => true +attribute :timeout, :kind_of => String, :regex => /^\d+$/, :default => nil + +alias_method :url, :baseurl +alias_method :keyurl, :gpgkey diff --git a/chef/cookbooks/yum/templates/default/CentOS-Base.repo.erb b/chef/cookbooks/yum/templates/default/CentOS-Base.repo.erb new file mode 100644 index 0000000..73d45ce --- /dev/null +++ b/chef/cookbooks/yum/templates/default/CentOS-Base.repo.erb @@ -0,0 +1,57 @@ +# CentOS-Base.repo +# # +# # The mirror system uses the connecting IP address of the client and the +# # update status of each mirror to pick mirrors that are updated to and +# # geographically close to the client. You should use this for CentOS updates +# # unless you are manually picking other mirrors. +# # +# # If the mirrorlist= does not work for you, as a fall back you can try the +# # remarked out baseurl= line instead. +# # +# # + +[base] +name=CentOS-$releasever - Base +#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os +baseurl=http://<%= node['yum']['centos']['repo_host'] %>/centos/$releasever/os/$basearch/ +gpgcheck=1 +keepcache=1 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 + +#released updates +[updates] +name=CentOS-$releasever - Updates +#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates +baseurl=http://<%= node['yum']['centos']['repo_host'] %>/centos/$releasever/updates/$basearch/ +gpgcheck=1 +keepcache=1 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 + +#additional packages that may be useful +[extras] +name=CentOS-$releasever - Extras +#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras +baseurl=http://<%= node['yum']['centos']['repo_host'] %>/centos/$releasever/extras/$basearch/ +gpgcheck=1 +keepcache=1 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 + +#additional packages that extend functionality of existing packages +[centosplus] +name=CentOS-$releasever - Plus +#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus +baseurl=http://<%= node['yum']['centos']['repo_host'] %>/centos/$releasever/centosplus/$basearch/ +gpgcheck=1 +keepcache=1 +enabled=0 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 + +#contrib - packages by Centos Users +[contrib] +name=CentOS-$releasever - Contrib +#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=contrib +baseurl=http://<%= node['yum']['centos']['repo_host'] %>/centos/$releasever/contrib/$basearch/ +gpgcheck=1 +keepcache=1 +enabled=0 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 diff --git a/chef/cookbooks/yum/templates/default/main.erb b/chef/cookbooks/yum/templates/default/main.erb index c948228..f41576c 100644 --- a/chef/cookbooks/yum/templates/default/main.erb +++ b/chef/cookbooks/yum/templates/default/main.erb @@ -1,4 +1,4 @@ -# This file was generated by Chef for <%= node.name %> +# This file was generated by Chef # Do NOT modify this file by hand. [main] diff --git a/chef/cookbooks/yum/templates/default/repo.erb b/chef/cookbooks/yum/templates/default/repo.erb index 02628a5..73fc116 100644 --- a/chef/cookbooks/yum/templates/default/repo.erb +++ b/chef/cookbooks/yum/templates/default/repo.erb @@ -1,41 +1,98 @@ -# Generated by Chef for <%= node['fqdn'] %> -# Local modifications will be overwritten. -[<%= @repo_name %>] -name=<%= @description %> -<% if @type %> -type=<%= @type %> +# This file was generated by Chef +# Do NOT modify this file by hand. + +[<%= @config.repositoryid %>] +name=<%= @config.description %> +<% if @config.baseurl %> +baseurl=<%= @config.baseurl %> <% end %> -<% unless @url.empty? -%> -baseurl=<%= @url %> -<% end -%> -<% if @mirrorlist %> -mirrorlist=<%= @mirrorlist %> +<% if @config.cost %> +cost=<%= @config.cost %> <% end %> -<% if @key %> +<% if @config.enabled %> +enabled=1 +<% else %> +enabled=0 +<% end %> +<% if @config.enablegroups %> +enablegroups=1 +<% end %> +<% if @config.exclude %> +exclude=<%= @config.exclude %> +<% end %> +<% if @config.failovermethod %> +failovermethod=<%= @config.failovermethod %> +<% end %> +<% if @config.fastestmirror_enabled %> +fastestmirror_enabled=<%= @config.fastestmirror_enabled %> +<% end %> +<% if @config.gpgcheck %> gpgcheck=1 -gpgkey=file:///etc/pki/rpm-gpg/<%= @key %> <% else %> gpgcheck=0 <% end %> -enabled=<%= @enabled %> -<% if @failovermethod %> -failovermethod=<%= @failovermethod %> +<% if @config.gpgkey %> +gpgkey=<%= @config.gpgkey %> <% end %> -<% if @bootstrapurl %> -bootstrapurl=<%= @bootstrapurl %> +<% if @config.http_caching %> +http_caching=<%= @config.http_caching %> <% end %> -<% if @includepkgs %> -includepkgs=<%= @includepkgs %> +<% if @config.include_config %> +include=<%= @config.include_config %> <% end %> -<% if @exclude %> -exclude=<%= @exclude %> +<% if @config.includepkgs %> +includepkgs=<%= @config.includepkgs %> <% end %> -<% if @priority %> -priority=<%= @priority %> +<% if @config.keepalive %> +keepalive=1 <% end %> -<% if @metadata_expire %> -metadata_expire=<%= @metadata_expire%> +<% if @config.metadata_expire %> +metadata_expire=<%= @config.metadata_expire %> <% end %> -<% if @type %> -type=<%= @type%> +<% if @config.mirrorlist %> +mirrorlist=<%= @config.mirrorlist %> <% end %> +<% if @config.mirror_expire %> +mirror_expire=<%= @config.mirror_expire %> +<% end %> +<% if @config.mirrorlist_expire %> +mirrorlist_expire=<%= @config.mirrorlist_expire %> +<% end %> +<% if @config.priority %> +priority=<%= @config.priority %> +<% end %> +<% if @config.proxy %> +proxy=<%= @config.proxy %> +<% end %> +<% if @config.proxy_username %> +proxy_username=<%= @config.proxy_username %> +<% end %> +<% if @config.proxy_password %> +proxy_password=<%= @config.proxy_password %> +<% end %> +<% if @config.max_retries %> +retries=<%= @config.max_retries %> +<% end %> +<% if @config.report_instanceid %> +report_instanceid=<%= @config.report_instanceid %> +<% end %> +<% if @config.skip_if_unavailable %> +skip_if_unavailable=1 +<% end %> +<% if @config.sslcacert %> +sslcacert=<%= @config.sslcacert %> +<% end %> +<% if @config.sslclientcert %> +sslclientcert=<%= @config.sslclientcert %> +<% end %> +<% if @config.sslclientkey %> +sslclientkey=<%= @config.sslclientkey %> +<% end %> +<% if @config.sslverify %> +sslverify=1 +<% else %> +sslverify=0 +<% end %> +<% if @config.timeout %> +timeout=<%= @config.timeout %> +<% end %> \ No newline at end of file diff --git a/chef/cookbooks/yum/templates/default/yum-rhel5.conf.erb b/chef/cookbooks/yum/templates/default/yum-rhel5.conf.erb deleted file mode 100644 index 6ed58dc..0000000 --- a/chef/cookbooks/yum/templates/default/yum-rhel5.conf.erb +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Chef for <%= node['fqdn'] %> -# Local modifications will be overwritten. -[main] -cachedir=<%= node['yum']['cachedir'] %> -keepcache=<%= node['yum']['keepcache'] %> -debuglevel=2 -logfile=/var/log/yum.log -distroverpkg=redhat-release -tolerant=1 -exactarch=1 -obsoletes=1 -gpgcheck=1 -plugins=1 -<%- unless node['yum']['exclude'].empty? %> -exclude=<%= node['yum']['exclude'].join(" ") %> -<%- end %> -<%- unless node['yum']['installonlypkgs'].empty? %> -installonlypkgs=<%= node['yum']['installonlypkgs'].join(" ") %> -<%- end %> -<%- unless node['yum']['proxy'].empty? %> -proxy=<%= node['yum']['proxy'] %> -proxy_username=<%= node['yum']['proxy_username'] %> -proxy_password=<%= node['yum']['proxy_password'] %> -<%- end %> - -# Note: yum-RHN-plugin doesn't honor this. -metadata_expire=1h - -# Default. -# installonly_limit = 3 - -# PUT YOUR REPOS HERE OR IN separate files named file.repo -# in /etc/yum.repos.d diff --git a/chef/cookbooks/yum/templates/default/yum-rhel6.conf.erb b/chef/cookbooks/yum/templates/default/yum-rhel6.conf.erb deleted file mode 100644 index 44d7979..0000000 --- a/chef/cookbooks/yum/templates/default/yum-rhel6.conf.erb +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Chef for <%= node['fqdn'] %> -# Local modifications will be overwritten. -[main] -cachedir=<%= node['yum']['cachedir'] %>/$basearch/$releasever -keepcache=<%= node['yum']['keepcache'] %> -debuglevel=2 -logfile=/var/log/yum.log -exactarch=1 -obsoletes=1 -gpgcheck=1 -plugins=1 -installonly_limit=3 -<%- unless node['yum']['exclude'].empty? %> -exclude=<%= node['yum']['exclude'].join(" ") %> -<%- end %> -<%- unless node['yum']['installonlypkgs'].empty? %> -installonlypkgs=<%= node['yum']['installonlypkgs'].join(" ") %> -<%- end %> -<%- unless node['yum']['proxy'].empty? %> -proxy=<%= node['yum']['proxy'] %> -proxy_username=<%= node['yum']['proxy_username'] %> -proxy_password=<%= node['yum']['proxy_password'] %> -<%- end %> - -# This is the default, if you make this bigger yum won't see if the metadata -# is newer on the remote and so you'll "gain" the bandwidth of not having to -# download the new metadata and "pay" for it by yum not having correct -# information. -# It is esp. important, to have correct metadata, for distributions like -# Fedora which don't keep old packages around. If you don't like this checking -# interupting your command line usage, it's much better to have something -# manually check the metadata once an hour (yum-updatesd will do this). -# metadata_expire=90m - -# PUT YOUR REPOS HERE OR IN separate files named file.repo -# in /etc/yum.repos.d diff --git a/chef/roles/allinone-compute.json b/chef/roles/allinone-compute.json new file mode 100644 index 0000000..27c9824 --- /dev/null +++ b/chef/roles/allinone-compute.json @@ -0,0 +1,17 @@ +{ + "name": "allinone-compute", + "description": "This will deploy all of the services for Openstack Compute to function on a single box.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-compute-single-controller]", + "role[os-compute-worker]", + "recipe[openstack-common::openrc]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/allinone-compute.rb b/chef/roles/allinone-compute.rb deleted file mode 100644 index daa008a..0000000 --- a/chef/roles/allinone-compute.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "allinone-compute" -description "This will deploy all of the services for Openstack Compute to function on a single box." -run_list( - "role[os-compute-single-controller]", - "role[os-compute-worker]" -) diff --git a/chef/roles/ceph-mds.json b/chef/roles/ceph-mds.json new file mode 100644 index 0000000..5383145 --- /dev/null +++ b/chef/roles/ceph-mds.json @@ -0,0 +1,8 @@ +{ + "name": "ceph-mds", + "description": "Ceph Metadata Server", + "run_list": [ + "recipe[ceph::repo]", + "recipe[ceph::mds]" + ] +} diff --git a/chef/roles/ceph-mon.json b/chef/roles/ceph-mon.json new file mode 100644 index 0000000..10162ff --- /dev/null +++ b/chef/roles/ceph-mon.json @@ -0,0 +1,8 @@ +{ + "name": "ceph-mon", + "description": "Ceph Monitor", + "run_list": [ + "recipe[ceph::repo]", + "recipe[ceph::mon]" + ] +} diff --git a/chef/roles/ceph-os-mon.json b/chef/roles/ceph-os-mon.json new file mode 100644 index 0000000..82c078f --- /dev/null +++ b/chef/roles/ceph-os-mon.json @@ -0,0 +1,9 @@ +{ + "name": "ceph-os-mon", + "description": "Ceph Monitor compatiable with OpenStack Icehouse", + "run_list": [ + "recipe[ceph::repo]", + "recipe[ceph::mon]", + "recipe[ceph::openstack_config_mon]" + ] +} diff --git a/chef/roles/ceph-os-radosgw.json b/chef/roles/ceph-os-radosgw.json new file mode 100644 index 0000000..550df0f --- /dev/null +++ b/chef/roles/ceph-os-radosgw.json @@ -0,0 +1,9 @@ +{ + "name": "ceph-os-radosgw", + "description": "Ceph RADOS Gateway compatiable with OpenStack Icehouse", + "run_list": [ + "recipe[ceph::repo]", + "recipe[ceph::radosgw]", + "recipe[ceph::openstack_config_radosgw]" + ] +} diff --git a/chef/roles/ceph-osd.json b/chef/roles/ceph-osd.json new file mode 100644 index 0000000..e749490 --- /dev/null +++ b/chef/roles/ceph-osd.json @@ -0,0 +1,8 @@ +{ + "name": "ceph-osd", + "description": "Ceph Object Storage Device", + "run_list": [ + "recipe[ceph::repo]", + "recipe[ceph::osd]" + ] +} diff --git a/chef/roles/ceph-radosgw.json b/chef/roles/ceph-radosgw.json new file mode 100644 index 0000000..925d017 --- /dev/null +++ b/chef/roles/ceph-radosgw.json @@ -0,0 +1,8 @@ +{ + "name": "ceph-radosgw", + "description": "Ceph RADOS Gateway", + "run_list": [ + "recipe[ceph::repo]", + "recipe[ceph::radosgw]" + ] +} diff --git a/chef/roles/ceph-tgt.json b/chef/roles/ceph-tgt.json new file mode 100644 index 0000000..f5dce87 --- /dev/null +++ b/chef/roles/ceph-tgt.json @@ -0,0 +1,8 @@ +{ + "name": "ceph-tgt", + "description": "Ceph iSCSI Target", + "run_list": [ + "recipe[ceph::repo]", + "recipe[ceph::tgt]" + ] +} diff --git a/chef/roles/compass-base.json b/chef/roles/compass-base.json new file mode 100644 index 0000000..0fc41b5 --- /dev/null +++ b/chef/roles/compass-base.json @@ -0,0 +1,15 @@ +{ + "name": "compass-base", + "description": "Base role to run before installing distributed system", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "recipe[collectd::client]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-base.json b/chef/roles/os-base.json new file mode 100644 index 0000000..8990f59 --- /dev/null +++ b/chef/roles/os-base.json @@ -0,0 +1,19 @@ +{ + "name": "os-base", + "description": "OpenStack Base role", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[compass-base]", + "recipe[openstack-common]", + "recipe[openstack-common::logging]", + "recipe[openstack-common::set_endpoints_by_interface]", + "recipe[openstack-common::sysctl]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-base.rb b/chef/roles/os-base.rb deleted file mode 100644 index c2783be..0000000 --- a/chef/roles/os-base.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-base" -description "OpenStack Base role" -run_list( - "recipe[openstack-common]", - "recipe[openstack-common::logging]" - ) diff --git a/chef/roles/os-block-storage-api.json b/chef/roles/os-block-storage-api.json new file mode 100644 index 0000000..e054288 --- /dev/null +++ b/chef/roles/os-block-storage-api.json @@ -0,0 +1,23 @@ +{ + "name": "os-block-storage-api", + "description": "OpenStack Block Storage API service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["cinder-api\" \"cinder-api"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-block-storage::api]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-block-storage-api.rb b/chef/roles/os-block-storage-api.rb deleted file mode 100644 index a0b5818..0000000 --- a/chef/roles/os-block-storage-api.rb +++ /dev/null @@ -1,23 +0,0 @@ -name "os-block-storage-api" -description "OpenStack Block Storage API service" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "cinder-api" => "/var/log/cinder/api.log" - }, - "debianloglist" => { - "cinder-api" => "/var/log/cinder/cinder-api.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["cinder-api\" \"cinder-api"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-block-storage::api]" - ) diff --git a/chef/roles/os-block-storage-controller.json b/chef/roles/os-block-storage-controller.json new file mode 100644 index 0000000..6d327cd --- /dev/null +++ b/chef/roles/os-block-storage-controller.json @@ -0,0 +1,18 @@ +{ + "name": "os-block-storage-controller", + "description": "Configures OpenStack block storage controller, configured by attributes.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-block-storage-api]", + "role[os-block-storage-scheduler]", + "recipe[openstack-block-storage::identity_registration]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-block-storage-scheduler.json b/chef/roles/os-block-storage-scheduler.json new file mode 100644 index 0000000..83cd8aa --- /dev/null +++ b/chef/roles/os-block-storage-scheduler.json @@ -0,0 +1,23 @@ +{ + "name": "os-block-storage-scheduler", + "description": "OpenStack Block Storage Scheduler service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["cinder-scheduler\" \"cinder-scheduler"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-block-storage::scheduler]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-block-storage-scheduler.rb b/chef/roles/os-block-storage-scheduler.rb deleted file mode 100644 index c254690..0000000 --- a/chef/roles/os-block-storage-scheduler.rb +++ /dev/null @@ -1,23 +0,0 @@ -name "os-block-storage-scheduler" -description "OpenStack Block Storage Scheduler service" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "cinder-scheduler" => "/var/log/cinder/scheduler.log" - }, - "debianloglist" => { - "cinder-scheduler" => "/var/log/cinder/cinder-scheduler.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["cinder-scheduler\" \"cinder-scheduler"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-block-storage::scheduler]" - ) diff --git a/chef/roles/os-block-storage-volume.json b/chef/roles/os-block-storage-volume.json new file mode 100644 index 0000000..932e8e4 --- /dev/null +++ b/chef/roles/os-block-storage-volume.json @@ -0,0 +1,24 @@ +{ + "name": "os-block-storage-volume", + "description": "OpenStack Block Storage volume service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["cinder-volume\" \"cinder-volume", "iscsid\" \"iscsid", + "multipathd\" \"multipathd"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-block-storage::volume]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-block-storage-worker.rb b/chef/roles/os-block-storage-worker.rb deleted file mode 100644 index 5d6e01d..0000000 --- a/chef/roles/os-block-storage-worker.rb +++ /dev/null @@ -1,23 +0,0 @@ -name "os-block-storage-worker" -description "OpenStack Block Storage worker" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "cinder-volume" => "/var/log/cinder/volume.log" - }, - "debianloglist" => { - "cinder-volume" => "/var/log/cinder/cinder-volume.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["cinder-volume\" \"cinder-volume", "iscsid\" \"iscsid", "multipathd\" \"multipathd"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-block-storage::volume]" - ) diff --git a/chef/roles/os-block-storage.json b/chef/roles/os-block-storage.json new file mode 100644 index 0000000..9b7e24b --- /dev/null +++ b/chef/roles/os-block-storage.json @@ -0,0 +1,19 @@ +{ + "name": "os-block-storage", + "description": "Configures OpenStack block storage, configured by attributes.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-block-storage-api]", + "role[os-block-storage-scheduler]", + "role[os-block-storage-volume]", + "recipe[openstack-block-storage::identity_registration]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-block-storage.rb b/chef/roles/os-block-storage.rb deleted file mode 100644 index f67aeed..0000000 --- a/chef/roles/os-block-storage.rb +++ /dev/null @@ -1,8 +0,0 @@ -name "os-block-storage" -description "Configures OpenStack block storage, configured by attributes." -run_list( - "role[os-base]", - "recipe[openstack-block-storage::api]", - "recipe[openstack-block-storage::scheduler]", - "recipe[openstack-block-storage::volume]" - ) diff --git a/chef/roles/os-ceph-block-storage-controller.json b/chef/roles/os-ceph-block-storage-controller.json new file mode 100644 index 0000000..c8b48fa --- /dev/null +++ b/chef/roles/os-ceph-block-storage-controller.json @@ -0,0 +1,19 @@ +{ + "name": "os-ceph-block-storage-controller", + "description": "Configures OpenStack block storage controller compatiable with Ceph, configured by attributes.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-block-storage-api]", + "role[os-block-storage-scheduler]", + "recipe[openstack-block-storage::identity_registration]", + "recipe[openstack-block-storage::cinder-config-ceph]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-ceph-compute-worker.json b/chef/roles/os-ceph-compute-worker.json new file mode 100644 index 0000000..a9ae170 --- /dev/null +++ b/chef/roles/os-ceph-compute-worker.json @@ -0,0 +1,25 @@ +{ + "name": "os-ceph-compute-worker", + "description": "The compute node compatiable with Ceph, most likely with a hypervisor.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["nova-compute\" \"nova-compute"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::compute]", + "recipe[openssh::passwordless]", + "recipe[openstack-compute::compute-config-ceph]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-ceph-controller.json b/chef/roles/os-ceph-controller.json new file mode 100644 index 0000000..7d5cb05 --- /dev/null +++ b/chef/roles/os-ceph-controller.json @@ -0,0 +1,23 @@ +{ + "name": "os-ceph-controller", + "description": "Roll-up role for all of the OpenStack services on a controller.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-ops-database]", + "role[os-ops-messaging]", + "role[os-identity]", + "role[os-ceph-image]", + "role[os-dashboard]", + "role[os-compute-controller]", + "role[os-ceph-block-storage-controller]", + "role[os-block-storage-volume]", + "role[os-network-server]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-ceph-image.json b/chef/roles/os-ceph-image.json new file mode 100644 index 0000000..540b3aa --- /dev/null +++ b/chef/roles/os-ceph-image.json @@ -0,0 +1,19 @@ +{ + "name": "os-ceph-image", + "description": "Roll-up role for Glance.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-image-api]", + "role[os-image-registry]", + "recipe[openstack-image::identity_registration]", + "recipe[openstack-image::glance-config-ceph]", + "role[os-image-upload]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-client.json b/chef/roles/os-client.json new file mode 100644 index 0000000..d08f24a --- /dev/null +++ b/chef/roles/os-client.json @@ -0,0 +1,24 @@ +{ + "name": "os-client", + "description": "Roll-up role for client interfaces.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-common::client]", + "recipe[openstack-block-storage::client]", + "recipe[openstack-compute::client]", + "recipe[openstack-identity::client]", + "recipe[openstack-image::client]", + "recipe[openstack-network::client]", + "recipe[openstack-object-storage::client]", + "recipe[openstack-orchestration::client]", + "recipe[openstack-telemetry::client]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-api-ec2.json b/chef/roles/os-compute-api-ec2.json new file mode 100644 index 0000000..27742e6 --- /dev/null +++ b/chef/roles/os-compute-api-ec2.json @@ -0,0 +1,16 @@ +{ + "name": "os-compute-api-ec2", + "description": "EC2 API for Compute", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::api-ec2]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-api-ec2.rb b/chef/roles/os-compute-api-ec2.rb deleted file mode 100644 index 10731a7..0000000 --- a/chef/roles/os-compute-api-ec2.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-compute-api-ec2" -description "EC2 API for Compute" -run_list( - "role[os-base]", - "recipe[openstack-compute::api-ec2]" - ) diff --git a/chef/roles/os-compute-api-metadata.json b/chef/roles/os-compute-api-metadata.json new file mode 100644 index 0000000..32d736d --- /dev/null +++ b/chef/roles/os-compute-api-metadata.json @@ -0,0 +1,23 @@ +{ + "name": "os-compute-api-metadata", + "description": "OpenStack compute metadata API service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["nova-metadata-api\" \"nova-metadata-api"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::api-metadata]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-api-metadata.rb b/chef/roles/os-compute-api-metadata.rb deleted file mode 100644 index 36ec59b..0000000 --- a/chef/roles/os-compute-api-metadata.rb +++ /dev/null @@ -1,15 +0,0 @@ -name "os-compute-api-metadata" -description "OpenStack compute metadata API service" -override_attributes( - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["nova-metadata-api\" \"nova-metadata-api"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-compute::api-metadata]" - ) diff --git a/chef/roles/os-compute-api-os-compute.json b/chef/roles/os-compute-api-os-compute.json new file mode 100644 index 0000000..6bdf108 --- /dev/null +++ b/chef/roles/os-compute-api-os-compute.json @@ -0,0 +1,16 @@ +{ + "name": "os-compute-api-os-compute", + "description": "OpenStack API for Compute", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::api-os-compute]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-api-os-compute.rb b/chef/roles/os-compute-api-os-compute.rb deleted file mode 100644 index 5c1bf31..0000000 --- a/chef/roles/os-compute-api-os-compute.rb +++ /dev/null @@ -1,23 +0,0 @@ -name "os-compute-api-os-compute" -description "OpenStack API for Compute" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "nova-api" => "/var/log/nova/api.log" - }, - "debianloglist" => { - "nova-api" => "/var/log/nova/nova-api.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["nova-api\" \"nova-api"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-compute::api-os-compute]" - ) diff --git a/chef/roles/os-compute-api.json b/chef/roles/os-compute-api.json new file mode 100644 index 0000000..569381b --- /dev/null +++ b/chef/roles/os-compute-api.json @@ -0,0 +1,25 @@ +{ + "name": "os-compute-api", + "description": "Roll-up role for all the Compute APIs", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["nova-api\" \"nova-api"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-compute-api-os-compute]", + "role[os-compute-api-metadata]", + "recipe[openstack-compute::identity_registration]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-api.rb b/chef/roles/os-compute-api.rb deleted file mode 100644 index 2db6422..0000000 --- a/chef/roles/os-compute-api.rb +++ /dev/null @@ -1,7 +0,0 @@ -name "os-compute-api" -description "Roll-up role for all the Compute APIs" -run_list( - "role[os-compute-api-ec2]", - "role[os-compute-api-os-compute]" -# "role[os-compute-api-metadata]" - ) diff --git a/chef/roles/os-compute-cert.json b/chef/roles/os-compute-cert.json new file mode 100644 index 0000000..4ef7c56 --- /dev/null +++ b/chef/roles/os-compute-cert.json @@ -0,0 +1,23 @@ +{ + "name": "os-compute-cert", + "description": "OpenStack Compute Cert service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["nova-cert\" \"nova-cert"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::nova-cert]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-cert.rb b/chef/roles/os-compute-cert.rb deleted file mode 100644 index d6ca8f2..0000000 --- a/chef/roles/os-compute-cert.rb +++ /dev/null @@ -1,15 +0,0 @@ -name "os-compute-cert" -description "OpenStack Compute Cert service" -override_attributes( - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["nova-cert\" \"nova-cert"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-compute::nova-cert]" - ) diff --git a/chef/roles/os-compute-conductor.json b/chef/roles/os-compute-conductor.json new file mode 100644 index 0000000..d6a3ffd --- /dev/null +++ b/chef/roles/os-compute-conductor.json @@ -0,0 +1,16 @@ +{ + "name": "os-compute-conductor", + "description": "Nova conductor", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::conductor]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-controller.json b/chef/roles/os-compute-controller.json new file mode 100644 index 0000000..dab6de3 --- /dev/null +++ b/chef/roles/os-compute-controller.json @@ -0,0 +1,22 @@ +{ + "name": "os-compute-controller", + "description": "Roll-up role for all of the OpenStack Compute services on a single roles", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-compute-setup]", + "role[os-compute-conductor]", + "role[os-compute-scheduler]", + "role[os-compute-api]", + "role[os-compute-cert]", + "role[os-compute-vncproxy]", + "role[os-compute-api-metadata]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-controller.rb b/chef/roles/os-compute-controller.rb deleted file mode 100644 index 4606fd2..0000000 --- a/chef/roles/os-compute-controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -name "os-compute-controller" -description "Roll-up role for all the Compute APIs" -run_list( - "role[os-base]", - "role[os-compute-api]", - "role[os-compute-scheduler]", - "role[os-compute-cert]", - "role[os-compute-vncproxy]", - "recipe[openstack-compute::conductor]", - "recipe[openstack-compute::nova-setup]" - ) diff --git a/chef/roles/os-compute-scheduler.json b/chef/roles/os-compute-scheduler.json new file mode 100644 index 0000000..24d3836 --- /dev/null +++ b/chef/roles/os-compute-scheduler.json @@ -0,0 +1,23 @@ +{ + "name": "os-compute-scheduler", + "description": "Nova scheduler", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["nova-scheduler\" \"nova-scheduler", "nova-conductor\" \"nova-conductor"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::scheduler]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-scheduler.rb b/chef/roles/os-compute-scheduler.rb deleted file mode 100644 index 3881389..0000000 --- a/chef/roles/os-compute-scheduler.rb +++ /dev/null @@ -1,25 +0,0 @@ -name "os-compute-scheduler" -description "Nova scheduler" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "nova-scheduler" => "/var/log/nova/scheduler.log", - "nova-conductor" => "/var/log/nova/conductor.log" - }, - "debianloglist" => { - "nova-scheduler" => "/var/log/nova/nova-scheduler.log", - "nova-conductor" => "/var/log/nova/nova-conductor.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["nova-scheduler\" \"nova-scheduler", "nova-conductor\" \"nova-conductor"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-compute::scheduler]" - ) diff --git a/chef/roles/os-compute-setup.json b/chef/roles/os-compute-setup.json new file mode 100644 index 0000000..f230885 --- /dev/null +++ b/chef/roles/os-compute-setup.json @@ -0,0 +1,17 @@ +{ + "name": "os-compute-setup", + "description": "Nova setup and identity registration", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::nova-setup]", + "recipe[openstack-compute::identity_registration]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-single-controller-no-network.json b/chef/roles/os-compute-single-controller-no-network.json new file mode 100644 index 0000000..ddb19c2 --- /dev/null +++ b/chef/roles/os-compute-single-controller-no-network.json @@ -0,0 +1,28 @@ +{ + "name": "os-compute-single-controller-no-network", + "description": "Roll-up role for all of the OpenStack Compute services on a single, non-HA controller, minus any network related roles", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-ops-database]", + "recipe[openstack-ops-database::openstack-db]", + "role[os-ops-messaging]", + "role[os-identity]", + "role[os-image]", + "role[os-compute-setup]", + "role[os-compute-conductor]", + "role[os-compute-scheduler]", + "role[os-compute-api]", + "role[os-block-storage]", + "role[os-compute-cert]", + "role[os-compute-vncproxy]", + "role[os-dashboard]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-single-controller.json b/chef/roles/os-compute-single-controller.json new file mode 100644 index 0000000..40d5bde --- /dev/null +++ b/chef/roles/os-compute-single-controller.json @@ -0,0 +1,29 @@ +{ + "name": "os-compute-single-controller", + "description": "Roll-up role for all of the OpenStack Compute services on a single, non-HA controller.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-ops-database]", + "recipe[openstack-ops-database::openstack-db]", + "role[os-ops-messaging]", + "role[os-identity]", + "role[os-image]", + "role[os-network]", + "role[os-compute-setup]", + "role[os-compute-conductor]", + "role[os-compute-scheduler]", + "role[os-compute-api]", + "role[os-block-storage]", + "role[os-compute-cert]", + "role[os-compute-vncproxy]", + "role[os-dashboard]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-single-controller.rb b/chef/roles/os-compute-single-controller.rb deleted file mode 100644 index c47366a..0000000 --- a/chef/roles/os-compute-single-controller.rb +++ /dev/null @@ -1,16 +0,0 @@ -name "os-compute-single-controller" -description "Roll-up role for all of the OpenStack Compute services on a single, non-HA controller." -run_list( - "role[os-base]", - "role[os-ops-database]", - "role[os-ops-messaging]", - "role[os-identity]", - "role[os-image]", - "role[os-network]", - "role[os-compute-scheduler]", - "role[os-compute-api]", - "role[os-block-storage]", - "role[os-compute-cert]", - "role[os-compute-vncproxy]", - "role[os-dashboard]" - ) diff --git a/chef/roles/os-compute-vncproxy.json b/chef/roles/os-compute-vncproxy.json new file mode 100644 index 0000000..05d30a3 --- /dev/null +++ b/chef/roles/os-compute-vncproxy.json @@ -0,0 +1,23 @@ +{ + "name": "os-compute-vncproxy", + "description": "Nova VNC Proxy", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["nova-xvpvncproxy\" \"nova-xvpvncproxy", "nova-novncproxy\" \"nova-novncproxy"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::vncproxy]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-vncproxy.rb b/chef/roles/os-compute-vncproxy.rb deleted file mode 100644 index 2b08850..0000000 --- a/chef/roles/os-compute-vncproxy.rb +++ /dev/null @@ -1,16 +0,0 @@ -name "os-compute-vncproxy" -description "Nova VNC Proxy" -override_attributes( - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["nova-xvpvncproxy\" \"nova-xvpvncproxy", "nova-novncproxy\" \"nova-novncproxy"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-compute::vncproxy]" - ) - diff --git a/chef/roles/os-compute-worker.json b/chef/roles/os-compute-worker.json new file mode 100644 index 0000000..77f6406 --- /dev/null +++ b/chef/roles/os-compute-worker.json @@ -0,0 +1,24 @@ +{ + "name": "os-compute-worker", + "description": "The compute node, most likely with a hypervisor.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["nova-compute\" \"nova-compute"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-compute::compute]", + "recipe[openssh::passwordless]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-compute-worker.rb b/chef/roles/os-compute-worker.rb deleted file mode 100644 index 7128ef1..0000000 --- a/chef/roles/os-compute-worker.rb +++ /dev/null @@ -1,24 +0,0 @@ -name "os-compute-worker" -description "The compute node, most likely with a hypervisor." -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "nova-compute" => "/var/log/nova/compute.log" - }, - "debianloglist" => { - "nova-compute" => "/var/log/nova/nova-compute.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["nova-compute\" \"nova-compute"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-compute::compute]" - ) - diff --git a/chef/roles/os-controller.json b/chef/roles/os-controller.json new file mode 100644 index 0000000..90ef17e --- /dev/null +++ b/chef/roles/os-controller.json @@ -0,0 +1,21 @@ +{ + "name": "os-controller", + "description": "Roll-up role for all of the OpenStack services on a controller.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-ops-database]", + "role[os-ops-messaging]", + "role[os-identity]", + "role[os-image]", + "role[os-dashboard]", + "role[os-compute-controller]", + "role[os-block-storage-controller]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-controller.rb b/chef/roles/os-controller.rb deleted file mode 100644 index 7338fcd..0000000 --- a/chef/roles/os-controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -name "os-controller" -description "Roll-up role for all of the OpenStack Compute services on a single, non-HA controller." -run_list( - 'role[os-dashboard]', - 'role[os-identity]', - 'role[os-identity-api]', - 'role[os-identity-api-admin]', - 'role[os-block-storage-api]', - 'role[os-block-storage-scheduler]', - 'role[os-compute-api]', - 'role[os-compute-api-os-compute]', - 'role[os-compute-cert]', - 'role[os-compute-controller]', - 'role[os-compute-scheduler]', -# 'role[os-compute-vncproxy]', - 'role[os-network-server]' - ) diff --git a/chef/roles/os-dashboard.json b/chef/roles/os-dashboard.json new file mode 100644 index 0000000..fc71eb3 --- /dev/null +++ b/chef/roles/os-dashboard.json @@ -0,0 +1,23 @@ +{ + "name": "os-dashboard", + "description": "Horizon server", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": {"ProcessMatch": ["httpd\" \"httpd"]} + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-dashboard::server]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-dashboard.rb b/chef/roles/os-dashboard.rb deleted file mode 100644 index 43a3062..0000000 --- a/chef/roles/os-dashboard.rb +++ /dev/null @@ -1,16 +0,0 @@ -name "os-dashboard" -description "Horizon server" -override_attributes( - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["httpd\" \"httpd"]} - } - } - } -) -run_list( - "role[os-base]", -# "recipe[openstack-dashboard::db]", - "recipe[openstack-dashboard::server]" - ) diff --git a/chef/roles/os-ha.json b/chef/roles/os-ha.json new file mode 100644 index 0000000..71bd549 --- /dev/null +++ b/chef/roles/os-ha.json @@ -0,0 +1,25 @@ +{ + "name": "os-ha", + "description": "Software load banance for all of the OpenStack services.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": { + "ProcessMatch": ["haproxy\" \"haproxy", "keepalived\" \"keepalived"] + } + } + } + } + }, + "chef_type": "role", + "run_list": [ + "recipe[keepalived]", + "recipe[haproxy::tcp_lb]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-ha.rb b/chef/roles/os-ha.rb deleted file mode 100644 index af1fc52..0000000 --- a/chef/roles/os-ha.rb +++ /dev/null @@ -1,15 +0,0 @@ -name "os-ha" -description "Software load banance" -override_attributes( - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["haproxy\" \"haproxy", "keepalived\" \"keepalived"]} - } - } - } -) -run_list( - "recipe[keepalived]", - "recipe[haproxy::tcp_lb]" - ) diff --git a/chef/roles/os-identity-api-admin.rb b/chef/roles/os-identity-api-admin.rb deleted file mode 100644 index 86e46cf..0000000 --- a/chef/roles/os-identity-api-admin.rb +++ /dev/null @@ -1,7 +0,0 @@ -name "os-identity-api-admin" -description "Keystone admin API service" -run_list( - "role[os-base]", - "recipe[openstack-identity::server]" - ) - diff --git a/chef/roles/os-identity-api.rb b/chef/roles/os-identity-api.rb deleted file mode 100644 index 96d7db9..0000000 --- a/chef/roles/os-identity-api.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-identity-api" -description "Keystone API service" -run_list( - "role[os-base]", - "recipe[openstack-identity::server]" - ) diff --git a/chef/roles/os-identity.json b/chef/roles/os-identity.json new file mode 100644 index 0000000..a829683 --- /dev/null +++ b/chef/roles/os-identity.json @@ -0,0 +1,27 @@ +{ + "name": "os-identity", + "description": "Roll-up role for Identity", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": { + "ProcessMatch": ["keystone\" \"keystone"] + } + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-ops-caching]", + "recipe[openstack-identity::server]", + "recipe[openstack-identity::registration]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-identity.rb b/chef/roles/os-identity.rb deleted file mode 100644 index ee374cc..0000000 --- a/chef/roles/os-identity.rb +++ /dev/null @@ -1,24 +0,0 @@ -name "os-identity" -description "Roll-up role for Identity" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "keystone" => "/var/log/keystone/keystone.log" - }, - "debianloglist" => { - "keystone" => "/var/log/keystone/keystone.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["keystone\" \"keystone"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-identity::server]", - "recipe[openstack-identity::registration]" - ) diff --git a/chef/roles/os-image-api.json b/chef/roles/os-image-api.json new file mode 100644 index 0000000..325f7d9 --- /dev/null +++ b/chef/roles/os-image-api.json @@ -0,0 +1,25 @@ +{ + "name": "os-image-api", + "description": "Glance API service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": { + "ProcessMatch": ["glance-api\" \"glance-api"] + } + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-image::api]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-image-api.rb b/chef/roles/os-image-api.rb deleted file mode 100644 index d533cff..0000000 --- a/chef/roles/os-image-api.rb +++ /dev/null @@ -1,25 +0,0 @@ -name "os-image-api" -description "Glance API service" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "glance-api" => "/var/log/glance/api.log" - }, - "debianloglist" => { - "glance-api" => "/var/log/glance/api.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["glance-api\" \"glance-api"] } - } - } - } -) -run_list( - "role[os-base]", - #"recipe[openstack-image::db]", - "recipe[openstack-image::api]" - ) - diff --git a/chef/roles/os-image-registry.json b/chef/roles/os-image-registry.json new file mode 100644 index 0000000..8c81498 --- /dev/null +++ b/chef/roles/os-image-registry.json @@ -0,0 +1,25 @@ +{ + "name": "os-image-registry", + "description": "Glance Registry service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": { + "ProcessMatch": ["glance-registry\" \"glance-registry"] + } + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-image::registry]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-image-registry.rb b/chef/roles/os-image-registry.rb deleted file mode 100644 index c4eebd0..0000000 --- a/chef/roles/os-image-registry.rb +++ /dev/null @@ -1,25 +0,0 @@ -name "os-image-registry" -description "Glance Registry service" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "glance-registry" => "/var/log/glance/registry.log" - }, - "debianloglist" => { - "glance-registry" => "/var/log/glance/registry.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["glance-registry\" \"glance-registry"] } - } - } - } -) -run_list( - "role[os-base]", - #"recipe[openstack-image::db]", - "recipe[openstack-image::registry]" - ) - diff --git a/chef/roles/os-image-upload.json b/chef/roles/os-image-upload.json new file mode 100644 index 0000000..163f239 --- /dev/null +++ b/chef/roles/os-image-upload.json @@ -0,0 +1,16 @@ +{ + "name": "os-image-upload", + "description": "Glance image upload", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-image::image_upload]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-image.json b/chef/roles/os-image.json new file mode 100644 index 0000000..1d5b4de --- /dev/null +++ b/chef/roles/os-image.json @@ -0,0 +1,18 @@ +{ + "name": "os-image", + "description": "Roll-up role for Glance.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-image-api]", + "role[os-image-registry]", + "recipe[openstack-image::identity_registration]", + "role[os-image-upload]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-image.rb b/chef/roles/os-image.rb deleted file mode 100644 index 8299fa8..0000000 --- a/chef/roles/os-image.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-image" -description "Roll-up role for Glance." -run_list( - "role[os-image-registry]", - "role[os-image-api]" - ) diff --git a/chef/roles/os-infra-caching.rb b/chef/roles/os-infra-caching.rb deleted file mode 100644 index 478227a..0000000 --- a/chef/roles/os-infra-caching.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-infra-caching" -description "Memcached role for Openstack" -run_list( - "role[os-base]", - "recipe[memcached::default]" - ) diff --git a/chef/roles/os-network-dhcp-agent.json b/chef/roles/os-network-dhcp-agent.json new file mode 100644 index 0000000..b0c19e1 --- /dev/null +++ b/chef/roles/os-network-dhcp-agent.json @@ -0,0 +1,16 @@ +{ + "name": "os-network-dhcp-agent", + "description": "OpenStack network dhcp-agent service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-network::dhcp_agent]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-network-l3-agent.json b/chef/roles/os-network-l3-agent.json new file mode 100644 index 0000000..5e1af4c --- /dev/null +++ b/chef/roles/os-network-l3-agent.json @@ -0,0 +1,16 @@ +{ + "name": "os-network-l3-agent", + "description": "OpenStack network l3-agent service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-network::l3_agent]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-network-metadata-agent.json b/chef/roles/os-network-metadata-agent.json new file mode 100644 index 0000000..ced91ad --- /dev/null +++ b/chef/roles/os-network-metadata-agent.json @@ -0,0 +1,16 @@ +{ + "name": "os-network-metadata-agent", + "description": "OpenStack network metadata-agent service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-network::metadata_agent]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-network-openvswitch.json b/chef/roles/os-network-openvswitch.json new file mode 100644 index 0000000..aa63b7b --- /dev/null +++ b/chef/roles/os-network-openvswitch.json @@ -0,0 +1,16 @@ +{ + "name": "os-network-openvswitch", + "description": "OpenStack network openvswitch service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-network::openvswitch]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-network-server.json b/chef/roles/os-network-server.json new file mode 100644 index 0000000..06f27e6 --- /dev/null +++ b/chef/roles/os-network-server.json @@ -0,0 +1,26 @@ +{ + "name": "os-network-server", + "description": "OpenStack network server service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": { + "ProcessMatch": ["neutron-server\" \"neutron-server"] + } + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-network::identity_registration]", + "recipe[openstack-network::server]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-network-server.rb b/chef/roles/os-network-server.rb deleted file mode 100644 index 0b64c6b..0000000 --- a/chef/roles/os-network-server.rb +++ /dev/null @@ -1,23 +0,0 @@ -name "os-network-server" -description "Configures OpenStack networking, managed by attribute for either nova-network or quantum" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "quantum-server" => "/var/log/quantum/server.log" - }, - "rhelloglist" => { - "quantum-server" => "/var/log/quantum/server.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["quantum-server\" \"quantum-server"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-network::server]" - ) diff --git a/chef/roles/os-network-worker.json b/chef/roles/os-network-worker.json new file mode 100644 index 0000000..127e29f --- /dev/null +++ b/chef/roles/os-network-worker.json @@ -0,0 +1,19 @@ +{ + "name": "os-network-worker", + "description": "Configures OpenStack networking node, managed by neutron.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-network-openvswitch]", + "role[os-network-l3-agent]", + "role[os-network-dhcp-agent]", + "role[os-network-metadata-agent]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-network.json b/chef/roles/os-network.json new file mode 100644 index 0000000..c1896ae --- /dev/null +++ b/chef/roles/os-network.json @@ -0,0 +1,33 @@ +{ + "name": "os-network", + "description": "Configures OpenStack networking, managed by attribute for either nova-network or neutron.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": { + "ProcessMatch": ["neutron-dhcp-agent\" \"neutron-dhcp-agent", + "neutron-l3-agent\" \"neutron-l3-agent", + "neutron-openvswitch-agent\" \"neutron-openvswitch-agent", + "neutron-metadata-agent\" \"neutron-metadata-agent"] + } + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-network::identity_registration]", + "role[os-network-openvswitch]", + "role[os-network-l3-agent]", + "role[os-network-dhcp-agent]", + "role[os-network-metadata-agent]", + "role[os-network-server]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-network.rb b/chef/roles/os-network.rb deleted file mode 100644 index 806f6f1..0000000 --- a/chef/roles/os-network.rb +++ /dev/null @@ -1,33 +0,0 @@ -name "os-network" -description "Configures OpenStack networking, managed by attribute for either nova-network or quantum" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "quantum-ovsagent" => "/var/log/quantum/openvswitch-agent.log", - "quantum-dhcp" => "/var/log/quantum/dhcp-agent.log", - "quantum-l3agent" => "/var/log/quantum/l3-agent.log" - }, - "debianloglist" => { - "quantum-ovsagent" => "/var/log/quantum/openvswitch-agent.log", - "quantum-dhcp" => "/var/log/quantum/dhcp-agent.log", - "quantum-l3agent" => "/var/log/quantum/l3-agent.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["quantum-dhcp-agent\" \"quantum-dhcp-agent", - "quantum-l3-agent\" \"quantum-l3-agent", - "quantum-openvswitch-agent\" \"quantum-openvswitch-agent", - "quantum-metadata-agent\" \"quantum-metadata-agent"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-network::openvswitch]", - "recipe[openstack-network::l3_agent]", - "recipe[openstack-network::dhcp_agent]", - "recipe[openstack-network::metadata_agent]" - ) diff --git a/chef/roles/os-object-storage-account.json b/chef/roles/os-object-storage-account.json new file mode 100644 index 0000000..6027507 --- /dev/null +++ b/chef/roles/os-object-storage-account.json @@ -0,0 +1,16 @@ +{ + "name": "os-object-storage-account", + "description": "OpenStack object storage account service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-object-storage::account]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-object-storage-account.rb b/chef/roles/os-object-storage-account.rb deleted file mode 100644 index 7e59eed..0000000 --- a/chef/roles/os-object-storage-account.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-object-storage-account" -description "OpenStack object storage account service" -run_list( - "role[os-base]", - "recipe[openstack-object-storage::account]" - ) diff --git a/chef/roles/os-object-storage-container.json b/chef/roles/os-object-storage-container.json new file mode 100644 index 0000000..494d4cb --- /dev/null +++ b/chef/roles/os-object-storage-container.json @@ -0,0 +1,16 @@ +{ + "name": "os-object-storage-container", + "description": "OpenStack object storage container service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-object-storage::container]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-object-storage-container.rb b/chef/roles/os-object-storage-container.rb deleted file mode 100644 index 5ee8098..0000000 --- a/chef/roles/os-object-storage-container.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-object-storage-container" -description "OpenStack object storage container service" -run_list( - "role[os-base]", - "recipe[openstack-object-storage::container]" - ) diff --git a/chef/roles/os-object-storage-management.json b/chef/roles/os-object-storage-management.json new file mode 100644 index 0000000..27aa232 --- /dev/null +++ b/chef/roles/os-object-storage-management.json @@ -0,0 +1,16 @@ +{ + "name": "os-object-storage-management", + "description": "OpenStack object storage management service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-object-storage::management]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-object-storage-management.rb b/chef/roles/os-object-storage-management.rb deleted file mode 100644 index 90399c3..0000000 --- a/chef/roles/os-object-storage-management.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-object-storage-management" -description "OpenStack object storage management service" -run_list( - "role[os-base]", - "recipe[openstack-object-storage::management]" - ) diff --git a/chef/roles/os-object-storage-object.json b/chef/roles/os-object-storage-object.json new file mode 100644 index 0000000..47806e4 --- /dev/null +++ b/chef/roles/os-object-storage-object.json @@ -0,0 +1,16 @@ +{ + "name": "os-object-storage-object", + "description": "OpenStack object storage object service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-object-storage::object]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-object-storage-object.rb b/chef/roles/os-object-storage-object.rb deleted file mode 100644 index 1d15727..0000000 --- a/chef/roles/os-object-storage-object.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-object-storage-object" -description "OpenStack object storage object service" -run_list( - "role[os-base]", - "recipe[openstack-object-storage::object]" - ) diff --git a/chef/roles/os-object-storage-proxy.json b/chef/roles/os-object-storage-proxy.json new file mode 100644 index 0000000..e5c8264 --- /dev/null +++ b/chef/roles/os-object-storage-proxy.json @@ -0,0 +1,16 @@ +{ + "name": "os-object-storage-proxy", + "description": "OpenStack object storage proxy service", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-object-storage::proxy-server]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-object-storage-proxy.rb b/chef/roles/os-object-storage-proxy.rb deleted file mode 100644 index da660a7..0000000 --- a/chef/roles/os-object-storage-proxy.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-object-storage-proxy" -description "OpenStack object storage proxy service" -run_list( - "role[os-base]", - "recipe[openstack-object-storage::proxy]" - ) diff --git a/chef/roles/os-object-storage.json b/chef/roles/os-object-storage.json new file mode 100644 index 0000000..82e1c44 --- /dev/null +++ b/chef/roles/os-object-storage.json @@ -0,0 +1,20 @@ +{ + "name": "os-object-storage", + "description": "OpenStack object storage roll-up role", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "role[os-object-storage-account]", + "role[os-object-storage-container]", + "role[os-object-storage-management]", + "role[os-object-storage-object]", + "role[os-object-storage-proxy]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-object-storage.rb b/chef/roles/os-object-storage.rb deleted file mode 100644 index 2a06fc2..0000000 --- a/chef/roles/os-object-storage.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "os-object-storage" -description "OpenStack object storage roll-up role" -run_list( - "role[os-base]", - "recipe[openstack-object-storage]" - ) diff --git a/chef/roles/os-ops-caching.json b/chef/roles/os-ops-caching.json new file mode 100644 index 0000000..86d2f95 --- /dev/null +++ b/chef/roles/os-ops-caching.json @@ -0,0 +1,16 @@ +{ + "name": "os-ops-caching", + "description": "Installs memcache server", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[memcached]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-ops-database.json b/chef/roles/os-ops-database.json new file mode 100644 index 0000000..329b155 --- /dev/null +++ b/chef/roles/os-ops-database.json @@ -0,0 +1,26 @@ +{ + "name": "os-ops-database", + "description": "Currently MySQL Server (non-ha)", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": { + "ProcessMatch": ["mysqld\" \"mysqld"] + } + } + } + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-ops-database::server]", + "recipe[openstack-ops-database::openstack-db]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-ops-database.rb b/chef/roles/os-ops-database.rb deleted file mode 100644 index ff243af..0000000 --- a/chef/roles/os-ops-database.rb +++ /dev/null @@ -1,24 +0,0 @@ -name "os-ops-database" -description "Currently MySQL Server (non-ha)" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "mysqld" => "/var/log/mysqld.log" - }, - "debianloglist" => { - "mysqld" => "/var/log/mysql.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["mysqld\" \"mysqld"] } - } - } - } -) -run_list( - "role[os-base]", - "recipe[openstack-ops-database::server]", - "recipe[openstack-ops-database::openstack-db]" - ) diff --git a/chef/roles/os-ops-messaging.json b/chef/roles/os-ops-messaging.json new file mode 100644 index 0000000..d48e756 --- /dev/null +++ b/chef/roles/os-ops-messaging.json @@ -0,0 +1,26 @@ +{ + "name": "os-ops-messaging", + "description": "Currently RabbitMQ Server (non-ha)", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "collectd": { + "rhel": { + "plugins": { + "processes": { + "ProcessMatch": ["rabbitmq-server\" \"rabbitmq-server"] + } + } + }, + "included_plugins": {"rabbitmq": {}} + } + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-ops-messaging::server]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-ops-messaging.rb b/chef/roles/os-ops-messaging.rb deleted file mode 100644 index d380c10..0000000 --- a/chef/roles/os-ops-messaging.rb +++ /dev/null @@ -1,24 +0,0 @@ -name "os-ops-messaging" -description "Currently RabbitMQ Server (non-ha)" -override_attributes( - "rsyslog" => { - "rhelloglist" => { - "rabbitmq" => "/var/log/rabbitmq/rabbit\@$hostname.log" - }, - "debianloglist" => { - "rabbitmq" => "/var/log/rabbitmq/rabbit\@$hostname.log" - } - }, - "collectd" => { - "rhel" => { - "plugins" => { - "processes" => { "ProcessMatch" => ["rabbitmq-server\" \"rabbitmq-server"] } - } - }, - "included_plugins" => {"rabbitmq"=>{}} - } -) -run_list( - "role[os-base]", - "recipe[openstack-ops-messaging::server]" - ) diff --git a/chef/roles/os-orchestration-api-cfn.json b/chef/roles/os-orchestration-api-cfn.json new file mode 100644 index 0000000..0b38c4e --- /dev/null +++ b/chef/roles/os-orchestration-api-cfn.json @@ -0,0 +1,16 @@ +{ + "name": "os-orchestration-api-cfn", + "description": "Role for Heat CloudFormation Api Service.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-orchestration::api-cfn]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-orchestration-api-cloudwatch.json b/chef/roles/os-orchestration-api-cloudwatch.json new file mode 100644 index 0000000..39f9d80 --- /dev/null +++ b/chef/roles/os-orchestration-api-cloudwatch.json @@ -0,0 +1,16 @@ +{ + "name": "os-orchestration-api-cloudwatch", + "description": "Role for Heat CloudWatch Api Service.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-orchestration::api-cloudwatch]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-orchestration-api.json b/chef/roles/os-orchestration-api.json new file mode 100644 index 0000000..33fad1d --- /dev/null +++ b/chef/roles/os-orchestration-api.json @@ -0,0 +1,16 @@ +{ + "name": "os-orchestration-api", + "description": "Role for Heat Api Service.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-orchestration::api]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-orchestration-engine.json b/chef/roles/os-orchestration-engine.json new file mode 100644 index 0000000..bebbf00 --- /dev/null +++ b/chef/roles/os-orchestration-engine.json @@ -0,0 +1,16 @@ +{ + "name": "os-orchestration-engine", + "description": "Role for Heat Engine Service.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-orchestration::engine]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-orchestration.json b/chef/roles/os-orchestration.json new file mode 100644 index 0000000..5d89edb --- /dev/null +++ b/chef/roles/os-orchestration.json @@ -0,0 +1,19 @@ +{ + "name": "os-orchestration", + "description": "Role for Heat.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-orchestration-engine]", + "role[os-orchestration-api]", + "role[os-orchestration-api-cfn]", + "role[os-orchestration-api-cloudwatch]", + "recipe[openstack-orchestration::identity_registration]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-single-controller.rb b/chef/roles/os-single-controller.rb deleted file mode 100644 index 88ad74f..0000000 --- a/chef/roles/os-single-controller.rb +++ /dev/null @@ -1,18 +0,0 @@ -name "os-single-controller" -description "Roll-up role for all of the OpenStack Compute services on a single, non-HA controller." -run_list( - "role[os-base]", - "role[os-ops-database]", - "role[os-ops-messaging]", - "role[os-identity]", - "role[os-image]", - "role[os-network-server]", - "role[os-block-storage]", - "role[os-compute-scheduler]", - "role[os-compute-api]", - "recipe[openstack-compute::conductor]", - "recipe[openstack-compute::nova-setup]", - "role[os-compute-cert]", - "role[os-compute-vncproxy]", - "role[os-dashboard]" - ) diff --git a/chef/roles/os-telemetry-agent-central.json b/chef/roles/os-telemetry-agent-central.json new file mode 100644 index 0000000..ba6d822 --- /dev/null +++ b/chef/roles/os-telemetry-agent-central.json @@ -0,0 +1,17 @@ +{ + "name": "os-telemetry-agent-central", + "description": "agent-central for telemetry", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-telemetry::identity_registration]", + "recipe[openstack-telemetry::agent-central]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-telemetry-agent-compute.json b/chef/roles/os-telemetry-agent-compute.json new file mode 100644 index 0000000..e8630f8 --- /dev/null +++ b/chef/roles/os-telemetry-agent-compute.json @@ -0,0 +1,17 @@ +{ + "name": "os-telemetry-agent-compute", + "description": "agent-compute for telemetry", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-telemetry::identity_registration]", + "recipe[openstack-telemetry::agent-compute]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-telemetry-agent-notification.json b/chef/roles/os-telemetry-agent-notification.json new file mode 100644 index 0000000..20857aa --- /dev/null +++ b/chef/roles/os-telemetry-agent-notification.json @@ -0,0 +1,17 @@ +{ + "name": "os-telemetry-agent-notification", + "description": "agent notification for telemetry", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-telemetry::identity_registration]", + "recipe[openstack-telemetry::agent-notification]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-telemetry-alarm-evaluator.json b/chef/roles/os-telemetry-alarm-evaluator.json new file mode 100644 index 0000000..8badff1 --- /dev/null +++ b/chef/roles/os-telemetry-alarm-evaluator.json @@ -0,0 +1,17 @@ +{ + "name": "os-telemetry-alarm-evaluator", + "description": "alarm evaluator for telemetry", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-telemetry::identity_registration]", + "recipe[openstack-telemetry::alarm-evaluator]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-telemetry-alarm-notifier.json b/chef/roles/os-telemetry-alarm-notifier.json new file mode 100644 index 0000000..39b6464 --- /dev/null +++ b/chef/roles/os-telemetry-alarm-notifier.json @@ -0,0 +1,17 @@ +{ + "name": "os-telemetry-alarm-notifier", + "description": "alarm notifier for telemetry", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-telemetry::identity_registration]", + "recipe[openstack-telemetry::alarm-notifier]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-telemetry-api.json b/chef/roles/os-telemetry-api.json new file mode 100644 index 0000000..549b933 --- /dev/null +++ b/chef/roles/os-telemetry-api.json @@ -0,0 +1,17 @@ +{ + "name": "os-telemetry-api", + "description": "api for telemetry", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-telemetry::identity_registration]", + "recipe[openstack-telemetry::api]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-telemetry-collector.json b/chef/roles/os-telemetry-collector.json new file mode 100644 index 0000000..dacfde5 --- /dev/null +++ b/chef/roles/os-telemetry-collector.json @@ -0,0 +1,17 @@ +{ + "name": "os-telemetry-collector", + "description": "collector for telemetry", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-base]", + "recipe[openstack-telemetry::identity_registration]", + "recipe[openstack-telemetry::collector]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/os-telemetry.json b/chef/roles/os-telemetry.json new file mode 100644 index 0000000..ecfc681 --- /dev/null +++ b/chef/roles/os-telemetry.json @@ -0,0 +1,21 @@ +{ + "name": "os-telemetry", + "description": "Role for Ceilometer.", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + }, + "chef_type": "role", + "run_list": [ + "role[os-telemetry-agent-central]", + "role[os-telemetry-agent-compute]", + "role[os-telemetry-agent-notification]", + "role[os-telemetry-alarm-evaluator]", + "role[os-telemetry-alarm-notifier]", + "role[os-telemetry-collector]", + "role[os-telemetry-api]" + ], + "env_run_lists": { + } +} diff --git a/chef/roles/test-synclog.rb b/chef/roles/test-synclog.rb deleted file mode 100644 index 45726c7..0000000 --- a/chef/roles/test-synclog.rb +++ /dev/null @@ -1,6 +0,0 @@ -name "test-synclog" -description "Sync application related logs for debugging" -run_list( - "recipe[rsyslog::client]", - "recipe[collectd::client]" - ) diff --git a/cobbler/conf/zone.template b/cobbler/conf/zone.template index db77d61..0f900d4 100644 --- a/cobbler/conf/zone.template +++ b/cobbler/conf/zone.template @@ -14,3 +14,4 @@ $cname_record $host_record $hostname IN A $ipaddr +metrics IN A $ipaddr diff --git a/cobbler/kickstarts/default.ks b/cobbler/kickstarts/default.ks index 7d9d096..89c0375 100644 --- a/cobbler/kickstarts/default.ks +++ b/cobbler/kickstarts/default.ks @@ -79,6 +79,7 @@ chef ntp openssh-clients wget +yum-plugin-priorities json-c libestr libgt @@ -94,6 +95,9 @@ chkconfig iptables off chkconfig ip6tables off $SNIPPET('kickstart_yum.conf') +#if $getVar('local_repo', '') != '' + $SNIPPET('kickstart_local_repo') +#end if $SNIPPET('kickstart_ssh') $SNIPPET('kickstart_ntp') $SNIPPET('kickstart_limits.conf') diff --git a/cobbler/snippets/kickstart_chef b/cobbler/snippets/kickstart_chef index 7dca2ad..0c1e267 100644 --- a/cobbler/snippets/kickstart_chef +++ b/cobbler/snippets/kickstart_chef @@ -1,12 +1,12 @@ mkdir -p /etc/chef - -## Generate chef rsyslog conf -$SNIPPET('kickstart_chef_rsyslog.conf') +mkdir -p /var/log/chef ## Generate validation.pem $SNIPPET('kickstart_chef-validator.pem') +## Generate admin.pem +$SNIPPET('kickstart_chef-admin.pem') ## Generate client.rb $SNIPPET('kickstart_client.rb') -$SNIPPET('kickstart_chef_firstrun.sh') -$SNIPPET('kickstart_chef_rerun.sh') +$SNIPPET('kickstart_knife.rb') +$SNIPPET('kickstart_chef_run.sh') $SNIPPET('kickstart_chef_init') diff --git a/cobbler/snippets/kickstart_chef-admin.pem b/cobbler/snippets/kickstart_chef-admin.pem new file mode 100644 index 0000000..36aad5d --- /dev/null +++ b/cobbler/snippets/kickstart_chef-admin.pem @@ -0,0 +1,8 @@ +#if $getVar('chef_admin_file', '') == "" + #set chef_admin_file = '/etc/chef-server/admin.pem' +#end if +#set f = $open($chef_admin_file) +cat << EOL > /etc/chef/admin.pem +#echo $f.read() +EOL +#silent $f.close() diff --git a/cobbler/snippets/kickstart_chef_firstrun.sh b/cobbler/snippets/kickstart_chef_firstrun.sh deleted file mode 100644 index aa1f5fe..0000000 --- a/cobbler/snippets/kickstart_chef_firstrun.sh +++ /dev/null @@ -1,25 +0,0 @@ -cat << EOF > /etc/chef/firstrun.sh -#raw -#!/bin/bash -touch /tmp/chef.log -while true; do - echo "firstrun chef-client on \`date\`" &>> /tmp/chef.log - clients=\$(pgrep chef-client) - if [ "\$?" == "0" ]; then - echo "there are chef-clients '\$clients' running" &>> /tmp/chef.log - sleep 1m - else - chef-client -L /var/log/chef-client.log &>> /tmp/chef.log - if [ "\$?" != "0" ]; then - echo "chef-client run failed" &>> /tmp/chef.log - sleep 1m - else - echo "chef-client run success" &>> /tmp/chef.log - break - fi - fi -done -#end raw -EOF -chmod +x /etc/chef/firstrun.sh - diff --git a/cobbler/snippets/kickstart_chef_init b/cobbler/snippets/kickstart_chef_init index 0c28cf8..95e98ef 100644 --- a/cobbler/snippets/kickstart_chef_init +++ b/cobbler/snippets/kickstart_chef_init @@ -15,12 +15,10 @@ ntpdate $ntp_server 2>&1 >> /tmp/ntp.log service ntpd start 2>&1 >> /tmp/ntp.log echo "new date is: `date`" 2>&1 >> /tmp/ntp.log -rm -rf /var/lib/rsyslog/firstboot_log -service rsyslog restart -/etc/chef/firstrun.sh +/etc/chef/run.sh crontab -l > /tmp/mycron -echo "*/30 * * * * /etc/chef/rerun.sh" >> /tmp/mycron +echo "*/30 * * * * /etc/chef/run.sh" >> /tmp/mycron crontab /tmp/mycron rm /tmp/mycron chkconfig chef off diff --git a/cobbler/snippets/kickstart_chef_rerun.sh b/cobbler/snippets/kickstart_chef_rerun.sh deleted file mode 100644 index 2ebd74e..0000000 --- a/cobbler/snippets/kickstart_chef_rerun.sh +++ /dev/null @@ -1,19 +0,0 @@ -cat << EOF > /etc/chef/rerun.sh -#raw -#!/bin/bash -echo "rerun chef-client on \`date\`" &>> /tmp/chef.log -clients=\$(pgrep chef-client) -if [ "\$?" == "0" ]; then - echo "there are chef-clients '\$clients' running" &>> /tmp/chef.log - exit 1 -fi -chef-client &>> /tmp/chef.log -if [ "\$?" != "0" ]; then - echo "chef-client run failed" &>> /tmp/chef.log -else - echo "chef-client run success" &>> /tmp/chef.log -fi -#end raw -EOF -chmod +x /etc/chef/rerun.sh - diff --git a/cobbler/snippets/kickstart_chef_rsyslog.conf b/cobbler/snippets/kickstart_chef_rsyslog.conf deleted file mode 100644 index ac42906..0000000 --- a/cobbler/snippets/kickstart_chef_rsyslog.conf +++ /dev/null @@ -1,12 +0,0 @@ -cat << EOL > /etc/rsyslog.d/chef.conf -\\$ModLoad imfile -\\$InputFileName /var/log/chef-client.log -\\$InputFileReadMode 0 -\\$InputFileTag -\\$InputFileStateFile firstboot_log -\\$InputFileSeverity notice -\\$InputFileFacility local3 -\\$InputRunFileMonitor -\\$InputFilePollInterval 1 -local3.info @$server:514 -EOL diff --git a/cobbler/snippets/kickstart_chef_run.sh b/cobbler/snippets/kickstart_chef_run.sh new file mode 100644 index 0000000..56628ea --- /dev/null +++ b/cobbler/snippets/kickstart_chef_run.sh @@ -0,0 +1,63 @@ +cat << EOF > /etc/chef/run.sh +#!/bin/bash +touch /tmp/chef.log +while true; do + echo "run chef-client on \`date\`" &>> /tmp/chef.log + clients=\\$(pgrep chef-client) + if [ "\\$?" == "0" ]; then + echo "there are chef-clients '\\$clients' running" &>> /tmp/chef.log + break + else + echo "knife search nodes" &>> /tmp/chef.log + USER=root HOME=/root knife search node "name:\\$HOSTNAME.*" -i -a name &>> /tmp/chef.log + nodes=\\$(USER=root HOME=/root knife search node "name:\\$HOSTNAME.*" -i -a name | grep 'name: ' | awk '{print \\$2}') + echo "found nodes \\$nodes" &>> /tmp/chef.log + let all_nodes_success=1 + for node in \\$nodes; do + mkdir -p /var/log/chef/\\$node + cat << EOL > /etc/chef/\\$node.json +#if $getVar("local_repo","") != "" +{"local_repo": "$local_repo"} +#else +{} +#end if +EOL + if [ ! -f "/etc/chef/\\$node.pem" ]; then + cat << EOL > /etc/rsyslog.d/\\$node.conf +\\\\$ModLoad imfile +\\\\$InputFileName /var/log/chef/\\$node/chef-client.log +\\\\$InputFileReadMode 0 +\\\\$InputFileTag \\$node +\\\\$InputFileStateFile chef_\\${node}_log +\\\\$InputFileSeverity notice +\\\\$InputFileFacility local3 +\\\\$InputRunFileMonitor +\\\\$InputFilePollInterval 1 +local3.info @$server:514 +EOL + rm -rf /var/lib/rsyslog/chef_\\$node_log + service rsyslog restart + fi + if [ -f "/etc/chef/\\$node.done" ]; then + chef-client --node-name \\$node -j /etc/chef/\\$node.json --client_key /etc/chef/\\$node.pem &>> /tmp/chef.log + else + chef-client --node-name \\$node -j /etc/chef/\\$node.json --client_key /etc/chef/\\$node.pem -L /var/log/chef/\\$node/chef-client.log &>> /tmp/chef.log + fi + if [ "\\$?" != "0" ]; then + echo "chef-client --node-name \\$node run failed" &>> /tmp/chef.log + let all_nodes_success=0 + else + echo "chef-client --node-name \\$node run success" &>> /tmp/chef.log + touch /etc/chef/\\$node.done + fi + done + if [ \\$all_nodes_success -eq 0 ]; then + sleep 1m + else + break + fi + fi +done +EOF +chmod +x /etc/chef/run.sh + diff --git a/cobbler/snippets/kickstart_client.rb b/cobbler/snippets/kickstart_client.rb index be3d106..314a47f 100644 --- a/cobbler/snippets/kickstart_client.rb +++ b/cobbler/snippets/kickstart_client.rb @@ -11,14 +11,12 @@ ENV['http_proxy'] = '$proxy' ENV['https_proxy'] = '$proxy' ENV['HTTP_PROXY'] = '$proxy' ENV['HTTPS_PROXY'] = '$proxy' -#end if -#if $getVar('ignore_proxy', '') != "" + #if $getVar('ignore_proxy', '') != "" + #set ignore_proxy = ','.join([proxy.strip() for proxy in $ignore_proxy.split(',') if proxy.strip()]) no_proxy '$ignore_proxy' ENV['no_proxy'] = '$ignore_proxy' ENV['NO_PROXY'] = '$ignore_proxy' -#end if -#if $getVar('chef_node_name', '') != "" -node_name '$chef_node_name' + #end if #end if validation_client_name 'chef-validator' json_attribs nil diff --git a/cobbler/snippets/kickstart_hosts b/cobbler/snippets/kickstart_hosts index 4281fa9..7f8426d 100644 --- a/cobbler/snippets/kickstart_hosts +++ b/cobbler/snippets/kickstart_hosts @@ -4,9 +4,12 @@ cat << EOL > /etc/hosts #for $iname, $idata in $interfaces.items() #if $hostname and $idata["management"] and $idata["static"] and $idata.get("ip_address", "") != "" $idata["ip_address"] $hostname - #end if + #end if #end for #import os #set $server_name = $os.uname[1] $server $server_name +#if $getVar("chef_server_ip", "") != "" and $getVar("chef_server_dns", "") != "" +$chef_server_ip $chef_server_dns +#end if EOL diff --git a/cobbler/snippets/kickstart_knife.rb b/cobbler/snippets/kickstart_knife.rb new file mode 100644 index 0000000..5801a72 --- /dev/null +++ b/cobbler/snippets/kickstart_knife.rb @@ -0,0 +1,13 @@ +mkdir -p /root/.chef +cat << EOL > /root/.chef/knife.rb +log_level :info +log_location '/dev/null' +#if $getVar('chef_url', '') != "" +chef_server_url '$chef_url' +#end if +node_name 'admin' +client_key '/etc/chef/admin.pem' +validation_client_name 'chef-validator' +validation_key '/etc/chef/validation.pem' +syntax_check_cache_path '/root/.chef/syntax_check_cache' +EOL diff --git a/cobbler/snippets/kickstart_local_repo b/cobbler/snippets/kickstart_local_repo new file mode 100644 index 0000000..2c862e8 --- /dev/null +++ b/cobbler/snippets/kickstart_local_repo @@ -0,0 +1,24 @@ +mkdir -p /tmp/repo_backup +mv /etc/yum.repos.d/* /tmp/repo_back/ + +cat << EOF > /etc/yum.repos.d/Compass.repo +[compass_repo] +name=Compass yum repo +baseurl=$local_repo/compass_repo/ +enabled=1 +gpgcheck=0 +priority=1 +proxy=_none_ +EOF + +cat << EOF > /etc/gemrc +gem: --no-ri --no-rdoc +:backtrace: false +:benchmark: false +:bulk_threshold: 1000 +:sources: +- http://gems.rubyforge.org/ +- $local_repo/gem_repo/ +:update_sources: true +:verbose: true +EOF diff --git a/cobbler/snippets/kickstart_post_install_network_config b/cobbler/snippets/kickstart_post_install_network_config index 69efd5e..174a375 100644 --- a/cobbler/snippets/kickstart_post_install_network_config +++ b/cobbler/snippets/kickstart_post_install_network_config @@ -4,334 +4,333 @@ #else #set promisc_interfaces = [] #end if -#if $getVar("system_name","") != "" - ## this is being provisioned by system records, not profile records - ## so we can do the more complex stuff - ## get the list of interface names - #set ikeys = $interfaces.keys() - #set osversion = $getVar("os_version","") - #import re - #set $vlanpattern = $re.compile("[a-zA-Z0-9]+[\.:][0-9]+") - ## Determine if we should use the MAC address to configure the interfaces first - ## Only physical interfaces are required to have a MAC address - ## Also determine the number of bonding devices we have, so we can set the - ## max-bonds option in modprobe.conf accordingly. -- jcapel - #set $configbymac = True - #set $numbondingdevs = 0 - #set $enableipv6 = False - ## ============================================================================= - #for $iname in $ikeys - ## look at the interface hash data for the specific interface - #set $idata = $interfaces[$iname] - ## do not configure by mac address if we don't have one AND it's not for bonding/vlans - ## as opposed to a "real" physical interface - #if $idata.get("mac_address", "") == "" and not $vlanpattern.match($iname) and not $idata.get("interface_type", "").lower() in ("master","bond","bridge"): - ## we have to globally turn off the config by mac feature as we can't - ## use it now - #set $configbymac = False - #end if - ## count the number of bonding devices we have. - #if $idata.get("interface_type", "").lower() in ("master","bond","bonded_bridge_slave") - #set $numbondingdevs += 1 - #end if - ## enable IPv6 networking if we set an ipv6 address or turn on autoconfiguration - #if $idata.get("ipv6_address", "") != "" or $ipv6_autoconfiguration == True - #set $enableipv6 = True - #end if - #end for - ## end looping through the interfaces to see which ones we need to configure. - ## ============================================================================= - #set $i = 0 - ## setup bonding if we have to - #if $numbondingdevs > 0 - -# we have bonded interfaces, so set max_bonds -if [ -f "/etc/modprobe.conf" ]; then - echo "options bonding max_bonds=$numbondingdevs" >> /etc/modprobe.conf -fi - #end if - ## ============================================================================= - ## create a staging directory to build out our network scripts into - ## make sure we preserve the loopback device - -# create a working directory for interface scripts -mkdir /etc/sysconfig/network-scripts/cobbler -cp /etc/sysconfig/network-scripts/ifcfg-lo /etc/sysconfig/network-scripts/cobbler/ - ## ============================================================================= - ## configure the gateway if set up (this is global, not a per-interface setting) - #if $gateway != "" +#if $gateway != "" # set the gateway in the network configuration file grep -v GATEWAY /etc/sysconfig/network > /etc/sysconfig/network.cobbler echo "GATEWAY=$gateway" >> /etc/sysconfig/network.cobbler rm -f /etc/sysconfig/network mv /etc/sysconfig/network.cobbler /etc/sysconfig/network - #end if - ## ============================================================================= - ## Configure the system's primary hostname. This is also passed to anaconda, but - ## anaconda doesn't seem to honour it in DHCP-setups. - #if $hostname != "" +#end if +#if $hostname != "" # set the hostname in the network configuration file grep -v HOSTNAME /etc/sysconfig/network > /etc/sysconfig/network.cobbler echo "HOSTNAME=$hostname" >> /etc/sysconfig/network.cobbler rm -f /etc/sysconfig/network mv /etc/sysconfig/network.cobbler /etc/sysconfig/network - -# Also set the hostname now, some applications require it -# (e.g.: if we're connecting to Puppet before a reboot). /bin/hostname $hostname - #end if +#end if $SNIPPET('kickstart_hosts') - #if $enableipv6 == True -grep -v NETWORKING_IPV6 /etc/sysconfig/network > /etc/sysconfig/network.cobbler -echo "NETWORKING_IPV6=yes" >> /etc/sysconfig/network.cobbler -rm -f /etc/sysconfig/network -mv /etc/sysconfig/network.cobbler /etc/sysconfig/network - #if $ipv6_autoconfiguration != "" -grep -v IPV6_AUTOCONF /etc/sysconfig/network > /etc/sysconfig/network.cobbler - #if $ipv6_autoconfiguration == True -echo "IPV6_AUTOCONF=yes" >> /etc/sysconfig/network.cobbler - #else -echo "IPV6_AUTOCONF=no" >> /etc/sysconfig/network.cobbler - #end if -rm -f /etc/sysconfig/network -mv /etc/sysconfig/network.cobbler /etc/sysconfig/network - #end if - #if $ipv6_default_device != "" -grep -v IPV6_DEFAULTDEV /etc/sysconfig/network > /etc/sysconfig/network.cobbler -echo "IPV6_DEFAULTDEV=$ipv6_default_device" >> /etc/sysconfig/network.cobbler -rm -f /etc/sysconfig/network -mv /etc/sysconfig/network.cobbler /etc/sysconfig/network - #end if - #end if - ## ============================================================================= - ## now create the config file for each interface - #for $iname in $ikeys - -# Start configuration for $iname - ## create lots of variables to use later - #set $idata = $interfaces[$iname] - #set $mac = $idata.get("mac_address", "").upper() - #set $mtu = $idata.get("mtu", "") - #set $static = $idata.get("static", "") - #set $ip = $idata.get("ip_address", "") - #set $netmask = $idata.get("netmask", "") - #set $if_gateway = $idata.get("if_gateway", "") - #set $static_routes = $idata.get("static_routes", "") - #set $iface_type = $idata.get("interface_type", "").lower() - #set $iface_master = $idata.get("interface_master", "") - #set $bonding_opts = $idata.get("bonding_opts", "") - #set $bridge_opts = $idata.get("bridge_opts", "").split(" ") - #set $ipv6_address = $idata.get("ipv6_address", "") - #set $ipv6_secondaries = $idata.get("ipv6_secondaries", "") - #set $ipv6_mtu = $idata.get("ipv6_mtu", "") - #set $ipv6_default_gateway = $idata.get("ipv6_default_gateway", "") - #set $ipv6_static_routes = $idata.get("ipv6_static_routes", "") - #set $devfile = "/etc/sysconfig/network-scripts/cobbler/ifcfg-" + $iname - #set $routesfile = "/etc/sysconfig/network-scripts/cobbler/route-" + $iname - #set $ipv6_routesfile = "/etc/sysconfig/network-scripts/cobbler/route6-" + $iname - ## determine if this interface is for a VLAN - #if $vlanpattern.match($iname) - #set $is_vlan = "true" - #else - #set $is_vlan = "false" - #end if - ## slave interfaces are assumed to be static - #if $iface_type in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") - #set $static = 1 - #end if - ## =================================================================== - ## Things every interface get, no matter what - ## =================================================================== -echo "DEVICE=$iname" > $devfile -echo "ONBOOT=yes" >> $devfile - #if $mac != "" and $iface_type not in ("master","bond","bridge","bonded_bridge_slave") - ## virtual interfaces don't get MACs -echo "HWADDR=$mac" >> $devfile -IFNAME=\$(ip -o link | grep -i '$mac' | sed -e 's/^[0-9]*: //' -e 's/:.*//') - ## Rename this interface in modprobe.conf - ## FIXME: if both interfaces startwith eth this is wrong -if [ -f "/etc/modprobe.conf" ] && [ \$IFNAME ]; then - grep \$IFNAME /etc/modprobe.conf | sed "s/\$IFNAME/$iname/" >> /etc/modprobe.conf.cobbler - grep -v \$IFNAME /etc/modprobe.conf >> /etc/modprobe.conf.new - rm -f /etc/modprobe.conf - mv /etc/modprobe.conf.new /etc/modprobe.conf -fi - #end if - ## =================================================================== - ## Actions based on interface_type - ## =================================================================== - #if $iface_type in ("master","bond","bonded_bridge_slave") - ## if this is a bonded interface, configure it in modprobe.conf - #if $osversion == "rhel4" -if [ -f "/etc/modprobe.conf" ]; then - echo "install $iname /sbin/modprobe bonding -o $iname $bonding_opts" >> /etc/modprobe.conf.cobbler -fi - #else - ## Add required entry to modprobe.conf -if [ -f "/etc/modprobe.conf" ]; then - echo "alias $iname bonding" >> /etc/modprobe.conf.cobbler -fi - #end if - #if $bonding_opts != "" -cat >> $devfile << EOF -BONDING_OPTS="$bonding_opts" -EOF - #end if - #elif $iface_type in ("slave","bond_slave") and $iface_master != "" -echo "SLAVE=yes" >> $devfile -echo "MASTER=$iface_master" >> $devfile -echo "HOTPLUG=no" >> $devfile - #end if - #if $iface_type == "bridge" -echo "TYPE=Bridge" >> $devfile - #for $bridge_opt in $bridge_opts - #if $bridge_opt.strip() != "" -echo "$bridge_opt" >> $devfile - #end if - #end for - #elif ($iface_type == "bridge_slave" or $iface_type == "bonded_bridge_slave") and $iface_master != "" -echo "BRIDGE=$iface_master" >> $devfile -echo "HOTPLUG=no" >> $devfile - #end if - #if $iface_type != "bridge" -echo "TYPE=Ethernet" >> $devfile - #end if - ## =================================================================== - ## Actions based on static/dynamic configuration - ## =================================================================== - #if $static - #if $mac == "" and $iface_type == "" -# WARNING! Configuring interfaces by their names only -# is error-prone, and can cause issues if and when -# the kernel gives an interface a different name -# following a reboot/hardware changes. - #end if -echo "BOOTPROTO=static" >> $devfile - #if $ip != "" and $iface_type not in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") - ## Only configure static networking if an IP-address is configured - ## and if the interface isn't slaved to another interface (bridging or bonding) - #if $iname in $promisc_interfaces -echo "PROMISC=yes" >> $devfile - #else - #if $ip != "" -echo "IPADDR=$ip" >> $devfile - #end if - #if $if_gateway != "" -echo "GATEWAY=$if_gateway" >> $devfile - #end if - #if $netmask == "" - ## Default to 255.255.255.0? - #set $netmask = "255.255.255.0" - #end if -echo "NETMASK=$netmask" >> $devfile - #end if - #end if - #if $enableipv6 == True and $ipv6_autoconfiguration == False - #if $ipv6_address != "" -echo "IPV6INIT=yes" >> $devfile -echo "IPV6ADDR=$ipv6_address" >> $devfile - #end if - #if $ipv6_secondaries != "" - #set ipv6_secondaries = ' '.join(ipv6_secondaries) - ## The quotes around the ipv6 ip's need to be here -echo "IPV6ADDR_SECONDARIES=\"$ipv6_secondaries\"" >> $devfile - #end if - #if $ipv6_mtu != "" -echo "IPV6MTU=$ipv6_mtu" >> $devfile - #end if - #if $ipv6_default_gateway != "" -echo "IPV6_DEFAULTGW=$ipv6_default_gateway" >> $devfile - #end if - #end if - #else - ## this is a DHCP interface, much less work to do -echo "BOOTPROTO=dhcp" >> $devfile - #if $len($name_servers) > 0 -echo "PEERDNS=no" >> $devfile - #end if - #end if - ## =================================================================== - ## VLAN configuration - ## =================================================================== - #if $is_vlan == "true" -echo "VLAN=yes" >> $devfile -echo "ONPARENT=yes" >> $devfile - #end if - ## =================================================================== - ## Optional configuration stuff - ## =================================================================== - #if $mtu != "" -echo "MTU=$mtu" >> $devfile - #end if - ## =================================================================== - ## Non-slave DNS configuration, when applicable - ## =================================================================== - ## If the interface is anything but a slave then add DNSn entry - #if $iface_type.lower() not in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") - #set $nct = 0 - #for $nameserver in $name_servers - #set $nct = $nct + 1 -echo "DNS$nct=$nameserver" >> $devfile - #end for - #end if - ## =================================================================== - ## Interface route configuration - ## =================================================================== - #for $route in $static_routes - #set routepattern = $re.compile("[0-9/.]+:[0-9.]+") - #if $routepattern.match($route) - #set $routebits = $route.split(":") - #set [$network, $router] = $route.split(":") -echo "$network via $router" >> $routesfile - #else -# Warning: invalid route "$route" - #end if - #end for - #if $enableipv6 == True - #for $route in $ipv6_static_routes - #set routepattern = $re.compile("[0-9a-fA-F:/]+,[0-9a-fA-F:]+") - #if $routepattern.match($route) - #set $routebits = $route.split(",") - #set [$network, $router] = $route.split(",") -echo "$network via $router dev $iname" >> $ipv6_routesfile - #else -# Warning: invalid ipv6 route "$route" - #end if - #end for - #end if - ## =================================================================== - ## Done with this interface - ## =================================================================== - #set $i = $i + 1 -# End configuration for $iname - #end for - ## ============================================================================= - ## Configure name server search path in /etc/resolv.conf - #set $num_ns = $len($name_servers) - #set $num_ns_search = $len($name_servers_search) - #if $num_ns_search > 0 - +#set $num_ns_search = $len($name_servers_search) +#if $num_ns_search > 0 sed -i -e "/^search /d" /etc/resolv.conf echo -n "search " >>/etc/resolv.conf #for $nameserversearch in $name_servers_search echo -n "$nameserversearch " >>/etc/resolv.conf #end for echo "" >>/etc/resolv.conf - #end if - ## ============================================================================= - ## Configure name servers in /etc/resolv.conf - #if $num_ns > 0 +#end if +#set $num_ns = $len($name_servers) +#if $num_ns > 0 sed -i -e "/^nameserver /d" /etc/resolv.conf - #for $nameserver in $name_servers + #for $nameserver in $name_servers echo "nameserver $nameserver" >>/etc/resolv.conf + #end for +#end if + +declare -A physical_interfaces +set \$(ip -o link | grep -v lo | awk '{print \$2}' | sed 's/://') +let physical_interface_num=\$#; +let physical_interface_offset=0 +echo "network interface numbers: \$physical_interface_num" > /tmp/network_log +while [ \$physical_interface_offset -lt \$physical_interface_num ]; +do + physical_interfaces[\$1]=\$1 + let physical_interface_offset=\$physical_interface_offset+1 + shift 1 +done + +echo "interfaces: \${physical_interfaces[@]}" >> /tmp/network_log +declare -A physical_interface_mac +declare -A physical_mac_interface +for physical_interface in \${physical_interfaces[@]}; do + mac=\$(cat /sys/class/net/\${physical_interface}/address) + mac=\${mac^^} + physical_interface_mac[\${physical_interface}]=\$mac + physical_mac_interface[\$mac]=\${physical_interface} +done +for key in \${!physical_interface_mac[@]}; do + echo "interface to mac: $key => \${physical_interface_mac[\$key]}" >> /tmp/network_log +done +for key in \${!physical_mac_interface[@]}; do + echo "mac to interface: $key => \${physical_mac_interface[\$key]}" >> /tmp/network_log +done + +declare -A logical_interface_mapping +declare -A unset_logical_interfaces +declare -A used_physical_interfaces + +#set ikeys = $interfaces.keys() +#set osversion = $getVar("os_version","") +#import re +#set $vlanpattern = $re.compile("([a-zA-Z0-9]+)[\.][0-9]+") +#set $subinterfacepattern = $re.compile("([a-zA-Z0-9]+)[:][0-9]+") +#set $numbondingdevs = 0 +#for $iname in $ikeys + #set $idata = $interfaces[$iname] + #set $mac = $idata.get("mac_address", "").upper() + #set $interface_type = $idata.get("interface_type", "").lower() + #if $interface_type in ("master","bond","bonded_bridge_slave") + #set $numbondingdevs += 1 + #end if + #if $mac != "" +physical_interface=\${physical_mac_interface[$mac]} +logical_interface_mapping[$iname]=\$physical_interface +if [ -n "\$physical_interface" ]; then + unset physical_interfaces[\$physical_interface] + if [ "\$physical_interface" != "$iname" ]; then + used_physical_interfaces[\$physical_interface]=\$physical_interface + fi +fi + #else +logical_interface_mapping[$iname]="" +unset_logical_interfaces[$iname]=$iname + #end if +#end for + +echo "unset logical interfaces: \${unset_logical_interfaces[@]}" >> /tmp/network_log + +for logical_interface in \${unset_logical_interfaces[@]}; do + if [ -n "\${physical_interfaces[\$logical_interface]}" ]; then + logical_interface_mapping[\$logical_interface]=\$logical_interface + unset unset_logical_interfaces[\$logical_interface] + unset physical_interfaces[\$logical_interface] + fi +done + +sorted_physical_interfaces=(\$(printf '%s\n' \${physical_interfaces[@]} | sort)) +echo "sorted physical interfaces: \${sorted_physical_interfaces[@]}" >> /tmp/network_log +sorted_unset_logical_interfaces=(\$(printf '%s\n' \${unset_logical_interfaces[@]} | sort)) +echo "sorted logical interfaces: \${sorted_unset_logical_interfaces[@]}" >> /tmp/network_log + +while [ \${#sorted_physical_interfaces[@]} -gt 0 -a \${#sorted_unset_logical_interfaces[@]} -gt 0 ]; do + physical_interface=\${sorted_physical_interfaces[0]} + logical_interface=\${sorted_unset_logical_interfaces[0]} + unset sorted_physical_interfaces[0] + unset sorted_unset_logical_interfaces[0] + unset physical_interfaces[\$physical_interface] + unset unset_logical_interfaces[\$logical_interface] + logical_interface_mapping[\$logical_interface]=\$physical_interface +done + +sorted_used_physical_interfaces=(\$(printf '%s\n' \${used_physical_interfaces[@]} | sort)) + +while [ \${#sorted_physical_interfaces[@]} -gt 0 -a \${#sorted_used_physical_interfaces[@]} -gt 0 ]; do + physical_interface=\${sorted_physical_interfaces[0]} + logical_interface=\${sorted_used_physical_interfaces[0]} + unset sorted_physical_interfaces[0] + unset sorted_used_physical_interfaces[0] + unset physical_interfaces[\$physical_interface] + unset used_physical_interfaces[\$logical_interface] + logical_interface_mapping[\$logical_interface]=\$physical_interface +done + +for key in \${!logical_interface_mapping[@]}; do + echo "map logical interface to physical interface: \$key => \${logical_interface_mapping[\$key]}" >> /tmp/network_log +done + +#if $numbondingdevs > 0 +# we have bonded interfaces, so set max_bonds +if [ -f "/etc/modprobe.conf" ]; then + echo "options bonding max_bonds=$numbondingdevs" >> /etc/modprobe.conf +fi +#end if + +# create a working directory for interface scripts +mkdir /etc/sysconfig/network-scripts/cobbler +cp /etc/sysconfig/network-scripts/ifcfg-lo /etc/sysconfig/network-scripts/cobbler/ + +#for $iname in $ikeys +# Start configuration for $iname + ## create lots of variables to use later + #set $idata = $interfaces[$iname] + #set $mac = $idata.get("mac_address", "").upper() + #set $mtu = $idata.get("mtu", "") + #set $static = $idata.get("static", "") + #set $ip = $idata.get("ip_address", "") + #set $netmask = $idata.get("netmask", "") + #set $if_gateway = $idata.get("if_gateway", "") + #set $static_routes = $idata.get("static_routes", "") + #set $iface_type = $idata.get("interface_type", "").lower() + #set $iface_master = $idata.get("interface_master", "") + #set $bonding_opts = $idata.get("bonding_opts", "") + #set $bridge_opts = $idata.get("bridge_opts", "").split(" ") + #set $devfile = "/etc/sysconfig/network-scripts/cobbler/ifcfg-" + $iname + #set $routesfile = "/etc/sysconfig/network-scripts/cobbler/route-" + $iname + + #if $iface_type in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") + #set $static = 1 + #end if +echo "DEVICE=$iname" > $devfile +echo "ONBOOT=yes" >> $devfile + + #if $iface_type not in ("master","bond","bridge","bonded_bridge_slave") + #if $vlanpattern.match(iname) + pass + #else + #set $interface_matched = $subinterfacepattern.match($iname) + #if $interface_matched + #set $interface_name = $interface_matched.group(1) +logical_interface=$interface_name + #else +logical_interface=$iname + #end if +physical_interface=\${logical_interface_mapping[\$logical_interface]} +if [ -n "\$physical_interface" ]; then + physical_mac=\${physical_interface_mac[\$physical_interface]} +fi +if [ -n "\$physical_mac" ]; then + echo "HWADDR=\$physical_mac" >> $devfile +fi + #if not $subinterfacepattern.match($iname) +if [ -f "/etc/modprobe.conf" ] && [ -n "\$physical_interface" ]; then + grep \$physical_interface /etc/modprobe.conf | sed "s/\$physical_interface/$iname/" >> /etc/modprobe.conf.cobbler + grep -v \$physical_interface /etc/modprobe.conf >> /etc/modprobe.conf.new + rm -f /etc/modprobe.conf + mv /etc/modprobe.conf.new /etc/modprobe.conf +fi + #end if + #end if + #end if + + #if $iface_type in ("master","bond","bonded_bridge_slave") + ## if this is a bonded interface, configure it in modprobe.conf +if [ -f "/etc/modprobe.conf" ]; then + #if $osversion == "rhel4" + echo "install $iname /sbin/modprobe bonding -o $iname $bonding_opts" >> /etc/modprobe.conf.cobbler + #else + echo "alias $iname bonding" >> /etc/modprobe.conf.cobbler + #end if +fi + #if $bonding_opts != "" +cat >> $devfile << EOF +BONDING_OPTS="$bonding_opts" +EOF + #end if + #elif $iface_type in ("slave","bond_slave") and $iface_master != "" +echo "SLAVE=yes" >> $devfile +echo "MASTER=$iface_master" >> $devfile +echo "HOTPLUG=no" >> $devfile + #end if + + #if $iface_type == "bridge" +echo "TYPE=Bridge" >> $devfile + #for $bridge_opt in $bridge_opts + #if $bridge_opt.strip() != "" +echo "$bridge_opt" >> $devfile + #end if + #end for + #elif $iface_type in ["bridge_slave", "bonded_bridge_slave"] and $iface_master != "" +echo "BRIDGE=$iface_master" >> $devfile +echo "HOTPLUG=no" >> $devfile + #end if + + #if $iface_type != "bridge" +echo "TYPE=Ethernet" >> $devfile + #end if + + #if $iname in $promisc_interfaces +echo "PROMISC=yes" >> $devfile + #end if + + #if $static +echo "BOOTPROTO=static" >> $devfile + #if $ip != "" and $iname not in $promisc_interfaces +echo "IPADDR=$ip" >> $devfile + #end if + + #if $if_gateway != "" +echo "GATEWAY=$if_gateway" >> $devfile + #end if + + #if $netmask == "" + #set $netmask = "255.255.255.0" + #end if +echo "NETMASK=$netmask" >> $devfile + #else +echo "BOOTPROTO=dhcp" >> $devfile + #if $len($name_servers) > 0 +echo "PEERDNS=no" >> $devfile + #end if + #end if + + #if $vlanpattern.match($iname) +echo "VLAN=yes" >> $devfile +echo "ONPARENT=yes" >> $devfile + #elif $subinterfacepattern.match($iname) +echo "ONPARENT=yes" >> $devfile + #end if + + #if $mtu != "" +echo "MTU=$mtu" >> $devfile + #end if + + #if $iface_type not in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") + #set $nct = 0 + #for $nameserver in $name_servers + #set $nct = $nct + 1 +echo "DNS$nct=$nameserver" >> $devfile #end for #end if + #for $route in $static_routes + #set routepattern = $re.compile("[0-9/.]+:[0-9.]+") + #if $routepattern.match($route) + #set $routebits = $route.split(":") + #set [$network, $router] = $route.split(":") +echo "$network via $router" >> $routesfile + #else +# Warning: invalid route "$route" + #end if + #end for +#end for + +#for $iname in $ikeys + #set $interface_matched = $subinterfacepattern.match($iname) + #if $interface_matched + #set $interface_name = $interface_matched.group(1) +logical_interface=$interface_name + #else +logical_interface=$iname + #end if +unset logical_interface_mapping[\$logical_interface] +#end for + +for logical_interface in \${!logical_interface_mapping[@]}; do + physical_interface=\${logical_interface_mapping[\$logical_interface]} + if [ -n "\$physical_interface" ]; then + devfile="/etc/sysconfig/network-scripts/cobbler/ifcfg-"\$logical_interface + mac=\${physical_interface_mac[\$physical_interface]} + echo "DEVICE=\$logical_interface" > \$devfile + echo "ONBOOT=yes" >> \$devfile + echo "BOOTPROTO=static" >> \$devfile + if [ -n "\$mac" ]; then + echo "HWADDR=\$mac" >> \$devfile + fi + echo "TYPE=Ethernet" >> \$devfile + if [ -f "/etc/modprobe.conf" ] && [ -n "\$physical_interface" ]; then + grep \$physical_interface /etc/modprobe.conf | sed "s/\$physical_interface/\$logical_interface/" >> /etc/modprobe.conf.cobbler + grep -v \$physical_interface /etc/modprobe.conf >> /etc/modprobe.conf.new + rm -f /etc/modprobe.conf + mv /etc/modprobe.conf.new /etc/modprobe.conf + fi + fi +done + ## Disable all eth interfaces by default before overwriting ## the old files with the new ones in the working directory ## This stops unneccesary (and time consuming) DHCP queries @@ -343,8 +342,11 @@ rm -f /etc/sysconfig/network-scripts/ifcfg-* mv /etc/sysconfig/network-scripts/cobbler/* /etc/sysconfig/network-scripts/ rm -r /etc/sysconfig/network-scripts/cobbler if [ -f "/etc/modprobe.conf" ]; then -cat /etc/modprobe.conf.cobbler >> /etc/modprobe.conf -rm -f /etc/modprobe.conf.cobbler + cat /etc/modprobe.conf.cobbler >> /etc/modprobe.conf + rm -f /etc/modprobe.conf.cobbler fi -#end if +if [ -f "/etc/udev/rules.d/70-persistent-net.rules" ]; then + rm -f /etc/udev/rules.d/70-persistent-net.rules +fi + # End post_install_network_config generated code diff --git a/cobbler/snippets/kickstart_pre_partition_disks b/cobbler/snippets/kickstart_pre_partition_disks index dd4fdd1..d48b1c9 100644 --- a/cobbler/snippets/kickstart_pre_partition_disks +++ b/cobbler/snippets/kickstart_pre_partition_disks @@ -1,5 +1,7 @@ #set hostname=$getVar('hostname',None) #set partition = $getVar('partition', None) +#set partition_size = $getVar('partition_size', None) +#set partition_maxsize = $getVar('partition_maxsize', None) #if $hostname == None #set $vgname = "VolGroup00" @@ -7,42 +9,220 @@ #set $vgname = $hostname.split('.')[0] #end if +declare -A disk_mapping +set \$(ls -l /dev/disk/by-path | awk '{print \$9; print \$11}') +let disk_mapping_nums=\$#/2 +let disk_mapping_offset=0 +echo "disk_mapping_nums: \$disk_mapping_nums" > /tmp/log +echo "disk_mapping_offset: \$disk_mapping_offset" >> /tmp/log +echo "disk_mapping: $*" >> /tmp/log +while [ \$disk_mapping_offset -lt \$disk_mapping_nums ]; +do + let found_disk_type=0 +#if $getVar('disk_type_only','') != "" + if expr match "\$1" ".*-${disk_type_only}-.*"; then + let found_disk_type=1 + fi +#else + let found_disk_type=1 +#end if + if [ \$found_disk_type -gt 0 ]; then + disk_name=\$(basename \$2) + disk_mapping[\$disk_name]=\$1 + fi + let disk_mapping_offset=\$disk_mapping_offset+1 + shift 2 +done +for key in \${!disk_mapping[@]}; do + echo "disk mapping \$key => \${disk_mapping[\$key]}" >> /tmp/log +done + +declare -A disks set \$(list-harddrives) let disk_nums=\$#/2 let disk_offset=0 let found_disk_offset=0 while [ \$disk_offset -lt \$disk_nums ]; do -#if $getVar('partitions_only','') != "" let found_disk=0 +if [[ x"\${disk_mapping[\$1]}" == x"" ]]; then + echo "ignore disk \$1 since it is not in disk_mapping" >> /tmp/log +else +#if $getVar('partitions_only', '') != "" #for $partition_only in $partitions_only.split(',') -if expr match "\$1" "$partition_only"; then - disks[found_disk_offset]=\$1 - let found_disk=1 -fi + if expr match "\$1" "$partition_only"; then + let found_disk=1 + else + echo "disk \$1 does not match $partition_only" >> /tmp/log + fi #end for +#else + let found_disk=1 +#end if +fi if [ \$found_disk -gt 0 ]; then + echo "add disk \$1 in partitioning list" >> /tmp/log +#if $getVar('partition_by_path', '0') != "0" + disks[\$found_disk_offset]=/dev/disk/by-path/\${disk_mapping[\$1]} +#else + disks[\$found_disk_offset]=\$1 +#end if let found_disk_offset=\$found_disk_offset+1 fi -#else - disks[found_disk_offset]=\$1 - let found_disk_offset=\$found_disk_offset+1 -#end if let disk_offset=\$disk_offset+1 shift 2 done -let disk_nums=\$found_disk_offset +echo "disks \${disks[@]}" >> /tmp/log +#if $getVar('sort_disks', '0') != "0" +sorted_disks=(\$(printf '%s\n' \${disks[@]} | sort)) +#else +sorted_disks=(\${disks[@]}) +#end if +echo "sorted disks \${sorted_disks[@]}" >> /tmp/log + +#if $getVar('start_from_disk', '') != "" +set \${sorted_disks[@]} +sorted_disks=(\${@:$start_from_disk}) +echo "sorted disks begin from the $start_from_disk: \${sorted_disks[@]}" >> /tmp/log +#end if + +#if $getVar('disk_num', '') != "" +set \${sorted_disks[@]} +sorted_disks=(\${@:1:$disk_num}) +echo "sorted disks for $disk_num disks: \${sorted_disks[@]}" >> /tmp/log +#end if +let disk_nums=\${#sorted_disks[@]} +sorted_disks_str="" +for disk in \${sorted_disks[@]}; do + sorted_disks_str="\${sorted_disks_str},\${disk}" +done echo "clearpart --all --initlabel" > /tmp/part-include #if $getVar('keep_old_partitions', '0') != "0" - #if $getVar('partitions_only','') != "" -echo "ignoredisk --only-use=$partitions_only" >> /tmp/part-include - #end if +echo "only partition \$sorted_disks_str" >> /tmp/log +echo "ignoredisk --only-use=\$sorted_disks_str" >> /tmp/part-include #end if -echo "part /boot --fstype ext3 --size=100 --ondisk=\${disks[0]} --asprimary" >> /tmp/part-include -echo "part swap --recommended --maxsize=128000 --ondisk=\${disks[0]}" >> /tmp/part-include +declare -A partitions_percentage +declare -A partitions_name +declare -A partitions_size +declare -A partitions_maxsize + +#if $partition != None + #set vol_sizes = [part.strip() for part in $partition.split(';') if part.strip()] + #for vol_and_size in $vol_sizes + #set vol, vol_size = $vol_and_size.split(' ', 1) + #set vol = $vol.strip() + #if $vol == '/' + #set volname = 'root' + #elif $vol == 'swap' + #set volname = 'swap' + #elif $vol.startswith('/') + #set volname = $vol[1:].replace('/', '_') + #else + #set volname = '' +# $vol is not starts with / + #end if +partitions_name[$vol]=$volname + #set vol_size = $vol_size.strip() + #if $vol_size.endswith('%'): + #set vol_percent = $vol_size[:-1] +partitions_percentage[$vol]=${vol_percent} + #else + #if $vol_size.endswith('K') + #set vol_min_size = $int($vol_size[:-1]) / 1000 + #elif $vol_size.endswith('M') + #set vol_min_size = $int($vol_size[:-1]) + #elif $vol_size.endswith('G') + #set vol_min_size = $int($vol_size[:-1]) * 1000 + #elif $vol_size.endswith('T') + #set vol_min_size = $int($vol_size[:-1]) * 1000000 + #else + #set vol_min_size = $int($vol_size) + #end if +partitions_size[$vol]=${vol_min_size} + #end if + #end for +#end if + +#if $partition_size != None + #set vol_sizes = [part.strip() for part in $partition_size.split(';') if part.strip()] + #for vol_and_size in $vol_sizes + #set vol, vol_size = $vol_and_size.split(' ', 1) + #set vol = $vol.strip() + #if $vol_size.endswith('K') + #set vol_min_size = $int($vol_size[:-1]) / 1000 + #elif $vol_size.endswith('M') + #set vol_min_size = $int($vol_size[:-1]) + #elif $vol_size.endswith('G') + #set vol_min_size = $int($vol_size[:-1]) * 1000 + #elif $vol_size.endswith('T') + #set vol_min_size = $int($vol_size[:-1]) * 1000000 + #else + #set vol_min_size = $int($vol_size) + #end if +partitions_size[$vol]=${vol_min_size} + #end for +#end if + +#if $partition_maxsize != None + #set vol_sizes = [part.strip() for part in $partition_maxsize.split(';') if part.strip()] + #for vol_and_size in $vol_sizes + #set vol, vol_size = $vol_and_size.split(' ', 1) + #set vol = $vol.strip() + #if $vol_size.endswith('K') + #set vol_max_size = $int($vol_size[:-1]) / 1000 + #elif $vol_size.endswith('M') + #set vol_max_size = $int($vol_size[:-1]) + #elif $vol_size.endswith('G') + #set vol_max_size = $int($vol_size[:-1]) * 1000 + #elif $vol_size.endswith('T') + #set vol_max_size = $int($vol_size[:-1]) * 1000000 + #else + #set vol_max_size = $int($vol_size) + #end if +partitions_maxsize[$vol]=${vol_max_size} + #end for +#end if + +default_partition=$getVar('default_partition', '/') +partition_fstype=$getVar('partition_fstype', 'ext3') + +for key in \${!partitions_name[@]}; do + echo "partition names \$key => \${partitions_name[\$key]}" >> /tmp/log +done + +for key in \${!partitions_percentage[@]}; do + echo "partition percentage \$key => \${partitions_percentage[\$key]}" >> /tmp/log +done + +for key in \${!partitions_size[@]}; do + echo "partition min size \$key => \${partitions_size[\$key]}" >> /tmp/log +done + +echo "default partition \${default_partition}" >> /tmp/log +echo "partition fstype \${partition_fstype}" >> /tmp/log + +for key in \${!partitions_maxsize[@]}; do + echo "partition max size \$key => \${partitions_maxsize[\$key]}" >> /tmp/log +done + +partition_size=\${partitions_size[/boot]:-500} +unset \${partitions_name[/boot]} +echo "part /boot --fstype=\${partition_fstype} --size=\${partition_size} --ondisk=\${sorted_disks[0]} --asprimary" >> /tmp/part-include +partition_size=\${partitions_size[swap]:-0} +if [[ "\$partition_size" == "0" ]]; then + partition_maxsize=\${partitions_maxsize[swap]:-128000} + echo "part swap --recommended --maxsize=\${partition_maxsize} --ondisk=\${sorted_disks[0]}" >> /tmp/part-include +else + echo "part swap --size=\${partition_size} --ondisk=\${sorted_disks[0]}" >> /tmp/part-include +fi +unset \${partitions_name[swap]} + +if [[ x"\${partitions_name[/]}" == x"" ]]; then + partitions_name[/]="root" +fi vggroup='' let disk_offset=0 @@ -50,31 +230,48 @@ while [ \$disk_offset -lt \$disk_nums ]; do let pv_id=\$disk_offset+1 partname="pv.0\$pv_id" -echo "part \$partname --size=1 --grow --ondisk=\${disks[\$disk_offset]}" >> /tmp/part-include +echo "part \$partname --size=1 --grow --ondisk=\${sorted_disks[\$disk_offset]}" >> /tmp/part-include vggroup="\$vggroup \$partname" let disk_offset=\$disk_offset+1; done echo "volgroup $vgname \$vggroup" >> /tmp/part-include -echo "logvol / --fstype ext3 --vgname=$vgname --size=1 --grow --name=rootvol" >> /tmp/part-include +declare -A sorted_partitions +sorted_partitions[0]=\${default_partition} +partition_offset=1 +for key in \${!partitions_name[@]}; do + if [[ "\$key" != "\${default_partition}" ]]; then + sorted_partitions[\${partition_offset}]=\$key + let partition_offset=\${partition_offset}+1 + fi +done -#if $partition != None - #set vol_sizes = [part.strip() for part in $partition.split(';') if part.strip()] - #for vol_and_size in vol_sizes - #set vol, vol_size = $vol_and_size.split(' ', 1) - #set vol = $vol.strip() - #set vol_size = $vol_size.strip() - #if $vol.startswith('/') - #set volname = $vol[1:] - #if $vol_size.endswith('%'): - #set vol_percent = vol_size[:-1] -echo "logvol $vol --fstype ext3 --vgname=$vgname --size=1 --grow --percent=$vol_percent --name=${volname}vol" >> /tmp/part-include - #else -echo "logvol $vol --vgname=$vgname --fstype ext3 --size=$vol_size --name=${volname}vol" >> /tmp/part-include - #end if - #else -# $vol is not starts with / - #end if - #end for -#end if +for key in \${sorted_partitions[@]}; do + partition_name=\${partitions_name[\$key]} + if [[ "\$key" == "\${default_partition}" ]]; then + grow_param="--grow" + else + grow_param="" + fi + partition_percentage=\${partitions_percentage[\$key]} + if [[ x"\${partition_percentage}" != x"" ]]; then + percentage_param="--percent=\${partition_percentage}" + grow_param="--grow" + else + percentage_param="" + fi + partition_size=\${partitions_size[\$key]} + if [[ x"\${partition_size}" != x"" ]]; then + size_param="--size=\${partition_size}" + else + size_param="--size=1" + fi + partition_maxsize=\${partitions_maxsize[\$key]} + if [[ x"\${partition_maxsize}" != x"" ]]; then + maxsize_param="--maxsize=\${partition_maxsize}" + else + maxsize_param="" + fi + echo "logvol \$key --fstype=\${partition_fstype} --vgname=$vgname \${percentage_param} \${size_param} \${maxsize_param} \${grow_param} --name=\${partition_name}vol" >> /tmp/part-include +done diff --git a/cobbler/snippets/kickstart_rsyslog.conf b/cobbler/snippets/kickstart_rsyslog.conf index 5b827a0..caef45b 100644 --- a/cobbler/snippets/kickstart_rsyslog.conf +++ b/cobbler/snippets/kickstart_rsyslog.conf @@ -1,5 +1,6 @@ cat << EOL > /etc/rsyslog.conf -#### MODULES #### +\#\#\#\# MODULES \#\#\#\## + \\$ModLoad imuxsock # provides support for local system logging (e.g. via logger command) \\$ModLoad imfile @@ -10,43 +11,51 @@ cat << EOL > /etc/rsyslog.conf \\$ActionQueueSaveOnShutDown on *.* @@$server:514 -# Provides UDP syslog reception -\\$ModLoad imudp -\\$UDPServerRun 514 +\# Provides UDP syslog reception +\#\\$ModLoad imudp +\#\\$UDPServerRun 514 -# Provides TCP syslog reception +\# Provides TCP syslog reception \\$ModLoad imtcp \\$InputTCPServerRun 514 #set system_name = $getVar('system_name','') \\$LocalHostName $system_name -#### GLOBAL DIRECTIVES #### +\#\#\#\# GLOBAL DIRECTIVES \#\#\#\## -# Use default timestamp format +\# Use default timestamp format \\$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat -# File syncing capability is disabled by default. This feature is usually not required, -# not useful and an extreme performance hit -#$ActionFileEnableSync on +\# File syncing capability is disabled by default. This feature is usually not required, +\# not useful and an extreme performance hit +\#\\$ActionFileEnableSync on -# Include all config files in /etc/rsyslog.d/ +\# Include all config files in /etc/rsyslog.d/ \\$IncludeConfig /etc/rsyslog.d/*.conf -#### RULES #### +\#\#\#\# RULES \#\#\#\## + +\# Log anything (except mail) of level info or higher. +\# Don't log private authentication messages! syslog.*,daemon.* /var/log/messages + +\# The authpriv file has restricted access. +authpriv.* /var/log/secure + +\# Log all the mail messages in one place. mail.* -/var/log/maillog -# Log cron stuff +\# Log cron stuff cron.* /var/log/cron -# Everybody gets emergency messages +\# Everybody gets emergency messages *.emerg * -# Save news errors of level crit and higher in a special file. +\# Save news errors of level crit and higher in a special file. uucp,news.crit /var/log/spooler -# Save boot messages also to boot.log +\# Save boot messages also to boot.log local7.* /var/log/boot.log EOL diff --git a/cobbler/snippets/preseed_client.rb b/cobbler/snippets/preseed_client.rb index 6afebec..a900ba4 100644 --- a/cobbler/snippets/preseed_client.rb +++ b/cobbler/snippets/preseed_client.rb @@ -10,11 +10,12 @@ echo "ENV['http_proxy'] = '$proxy'" >> /target/etc/chef/client.rb; \ echo "ENV['https_proxy'] = '$proxy'" >> /target/etc/chef/client.rb; \ echo "ENV['HTTP_PROXY'] = '$proxy'" >> /target/etc/chef/client.rb; \ echo "ENV['HTTPS_PROXY'] = '$proxy'" >> /target/etc/chef/client.rb; \ -#end if -#if $getVar('ignore_proxy', '') != "" + #if $getVar('ignore_proxy', '') != "" + #set ignore_proxy = ','.join([proxy.strip() for proxy in $ignore_proxy.split(',') if proxy.strip()]) echo "no_proxy '$ignore_proxy'" >> /target/etc/chef/client.rb; \ echo "ENV['no_proxy'] = '$ignore_proxy'" >> /target/etc/chef/client.rb; \ echo "ENV['NO_PROXY'] = '$ignore_proxy'" >> /target/etc/chef/client.rb; \ + #end if #end if #if $getVar('chef_node_name', '') != "" echo "node_name '$chef_node_name'" >> /target/etc/chef/client.rb; \ diff --git a/cobbler/snippets/preseed_hosts b/cobbler/snippets/preseed_hosts index 7188dbb..1bd0a8d 100644 --- a/cobbler/snippets/preseed_hosts +++ b/cobbler/snippets/preseed_hosts @@ -1,10 +1,14 @@ echo "127.0.0.1 $system_name localhost localhost.localdomain localhost4 localhost4.localdomain4" > /target/etc/hosts; \ echo "::1 $system_name localhost localhost.localdomain localhost6 localhost6.localdomain6" >> /target/etc/hosts; \ #for $iname, $idata in $interfaces.items() - #if $hostname and $idata["management"] and $idata["static"] and $idata.get("ip_address", "") != "" + #if $hostname and $idata["management"] and $idata["static"] and $idata.get("ip_address", "") != "" echo "$idata["ip_address"] $hostname" >> /target/etc/hosts; \ - #end if + #end if #end for #import os #set $server_name = $os.uname[1] echo "$server $server_name" >> /target/etc/hosts; \ + +#if $getVar("chef_server_ip", "") != "" and $getVar("chef_server_dns", "") != "" +echo "$chef_server_ip $chef_server_dns" >> /target/etc/hosts; \ +#end if diff --git a/cobbler/snippets/preseed_post_install_network_config b/cobbler/snippets/preseed_post_install_network_config index 90a54d6..539559b 100644 --- a/cobbler/snippets/preseed_post_install_network_config +++ b/cobbler/snippets/preseed_post_install_network_config @@ -187,6 +187,9 @@ echo " gateway $gateway" >> /target/etc/network/interfaces; \ ## ============================================================================= ## Configure name server search path in /target/etc/resolv.conf #set $nameservers = ' '.join($name_servers) + #if '8.8.8.8' not in $name_servers + #set $nameservers = ' '.join(($nameservers, '8.8.8.8')) + #end if #set $nameserver_search = ' '.join($name_servers_search) #if $nameservers != "" echo " dns-nameservers $nameservers" >> /target/etc/network/interfaces; \ diff --git a/cobbler/snippets/preseed_pre_partition_disks b/cobbler/snippets/preseed_pre_partition_disks index 2996d67..99f01a2 100644 --- a/cobbler/snippets/preseed_pre_partition_disks +++ b/cobbler/snippets/preseed_pre_partition_disks @@ -91,10 +91,11 @@ recipe="boot-root :: \ format{ } use_filesystem{ } filesystem{ ext3 } \ mountpoint{ /boot } device{ \$first_found_disk } \ . \ -200% 200% 200% linux-swap \ +256 256+10% 200% linux-swap \ \\$primary{ } method{ swap } format{ } \ device{ \$first_found_disk } \ . \ +#set default_percentage = 100 #if $partition != None #set vol_sizes = [part.strip() for part in $partition.split(';') if part.strip()] #for vol_and_size in vol_sizes @@ -103,18 +104,46 @@ device{ \$first_found_disk } \ #set vol_size = $vol_size.strip() #if $vol.startswith('/') #set volname = $vol[1:] -${vol_size} ${vol_size} ${vol_size} ext3 \ + #else + #continue + #end if + #set vol_min_size = 1 + #set vol_percent = 0 + #if $vol_size.endswith('%') + #set vol_percent = $int($vol_size[:-1]) + #elif $vol_size.endswith('K') + #set vol_min_size = $int($vol_size[:-1]) / 1000 + #elif $vol_size.endswith('M') + #set vol_min_size = $int($vol_size[:-1]) + #elif $vol_size.endswith('G') + #set vol_min_size = $int($vol_size[:-1]) * 1000 + #elif $vol_size.endswith('T') + #set vol_min_size = $int($vol_size[:-1]) * 1000000 + #end if + #if $vol_percent + #set factor = '%s+%s%%' % ($vol_min_size, $vol_percent) + #set default_percentage = $default_percentage - $vol_percent + #else + #set factor = $vol_min_size + #end if +${vol_min_size} ${factor} -1 ext3 \ \\$lvmok{ } method{ format } format{ } \ use_filesystem{ } filesystem{ ext3 } \ in_vg{ $vgname } lv_name{ ${volname}vol } \ mountpoint{ $vol } \ . \ - #else - #continue - #end if #end for #end if -1 2 100% ext3 \ +#set vol_min_size = 1 +#if $default_percentage <= 0 + #set default_percentage = 0 +#end if +#if $default_percentage + #set factor = '%s+%s%%' % ($vol_min_size, $default_percentage) +#else + #set factor = $vol_min_size +#end if +${vol_min_size} ${factor} -1 ext3 \ \\$lvmok{ } method{ format } \ format{ } use_filesystem{ } filesystem{ ext3 } \ in_vg{ $vgname } lv_name{ rootvol } \ @@ -124,7 +153,7 @@ mountpoint{ / } \ pv_offset=1; \ for found_disk in \${found_disks_str}; do \ recipe="\$recipe \ -1 1 -1 ext3 \ +512 512+100% -1 ext3 \ \\$defaultignore{ } \\$primary{ } device{ \${found_disk} } \ method{ lvm } vg_name{ $vgname } \ . \ From 353ee09bdec50f43c3f71a2ce24967ff2b211490 Mon Sep 17 00:00:00 2001 From: xiaodongwang Date: Thu, 23 Oct 2014 08:51:30 -0700 Subject: [PATCH 7/9] second merge round Change-Id: I49ca6463cd744866bcfe8a0defaa2402b49bb446 --- cobbler/snippets/kickstart_local_repo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cobbler/snippets/kickstart_local_repo b/cobbler/snippets/kickstart_local_repo index 2c862e8..cf93086 100644 --- a/cobbler/snippets/kickstart_local_repo +++ b/cobbler/snippets/kickstart_local_repo @@ -1,5 +1,5 @@ mkdir -p /tmp/repo_backup -mv /etc/yum.repos.d/* /tmp/repo_back/ +mv /etc/yum.repos.d/* /tmp/repo_backup/ cat << EOF > /etc/yum.repos.d/Compass.repo [compass_repo] @@ -11,7 +11,7 @@ priority=1 proxy=_none_ EOF -cat << EOF > /etc/gemrc +cat << EOF > /root/.gemrc gem: --no-ri --no-rdoc :backtrace: false :benchmark: false From 2e40ce9f7207a762b8511434ccaab62c4d544bdd Mon Sep 17 00:00:00 2001 From: Weidong Shao Date: Mon, 27 Oct 2014 20:29:58 +0000 Subject: [PATCH 8/9] Use cluster_id from the chef attribute Change-Id: Icbdfc6b7185725efbf8e57d5005395912ba6b413 --- chef/cookbooks/collectd/recipes/kairosdb.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/chef/cookbooks/collectd/recipes/kairosdb.rb b/chef/cookbooks/collectd/recipes/kairosdb.rb index c2b9fcd..75a47da 100644 --- a/chef/cookbooks/collectd/recipes/kairosdb.rb +++ b/chef/cookbooks/collectd/recipes/kairosdb.rb @@ -25,16 +25,17 @@ cookbook_file "#{node['collectd']['plugin_dir']}/kairosdb_writer.py" do notifies :restart, resources(:service => "collectd") end -if ! node['cluster'] - node.set['cluster'] = "no_cluster_defined" +cluster_id = 'no_cluster_defined' +if node['compass'] and node['compass']['cluster_id'] + cluster_id = node['compass']['cluster_id'] end + collectd_python_plugin "kairosdb_writer" do opts = {"KairosDBHost"=>node['collectd']['server']['host'], "KairosDBPort"=>node['collectd']['server']['port'], "KairosDBProtocol"=>node['collectd']['server']['protocol'], - "Tags" => "host=#{node['fqdn']}\" \"role=OSROLE\" \"location=China.Beijing.TsingHua\" \"cluster=#{node['cluster']}", + "Tags" => "host=#{node['fqdn']}\" \"role=OSROLE\" \"location=China.Beijing.TsingHua\" \"cluster=#{cluster_id}", "TypesDB" => node['collectd']['types_db'], - "LowercaseMetricNames"=>"true" } options(opts) end From 4ca08e44e703774fb8eda68bb2ffaad7a6123a93 Mon Sep 17 00:00:00 2001 From: xiaodongwang Date: Mon, 3 Nov 2014 17:23:52 -0800 Subject: [PATCH 9/9] merge new change to master Change-Id: I566f0183532979e53af75b56ac89a9f25ff6c1ad --- .../collectd/files/default/rabbitmq_info.py | 28 +++++++++++++++++-- cobbler/kickstarts/default.ks | 9 ++---- cobbler/snippets/kickstart_chef_run.sh | 25 +++++++++++++++-- cobbler/snippets/kickstart_post_anamon | 2 +- .../snippets/kickstart_pre_partition_disks | 3 ++ 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/chef/cookbooks/collectd/files/default/rabbitmq_info.py b/chef/cookbooks/collectd/files/default/rabbitmq_info.py index fd9137d..61fdf7d 100644 --- a/chef/cookbooks/collectd/files/default/rabbitmq_info.py +++ b/chef/cookbooks/collectd/files/default/rabbitmq_info.py @@ -84,15 +84,37 @@ def get_stats(): return None for i in resp: if i['vhost'] == VHOST: + # AN issue here is that the name has a GUID embedded and this is not a correct + # metric name for a time series database. The next lines attempt to remeady this + # by removing anything after the first or second character sequence followed by "_". + + iname = str(i['name']).split("_") + #logger('warn', 'iname = %s simplified.' % iname) + #logger('warn', 'Simplified from i[name] = %s.' % str(i['name'])) + iname = str(i['name']).split("_") + if(len(iname) > 1): + if(iname[0] == 'reply'): + mname = iname[0] + else: + mname = iname[0] + "_" + iname[1] + else: + mname = iname[0] + if "messages" in i: stats['ctl_messages'] += i['messages'] - stats['ctl_messages_%s' % i['name']] = i['messages'] + #stats['ctl_messages_%s' % i['name']] = i['messages'] + stats['ctl_messages_%s' % mname ] = i['messages'] + #logger('warn', 'ctl_message = %s.' % str(i['messages'])) if "memory" in i: stats['ctl_memory'] += i['memory'] - stats['ctl_memory_%s' % i['name']] = i['memory'] + #stats['ctl_memory_%s' % i['name']] = i['memory'] + stats['ctl_memory_%s' % mname ] = i['memory'] + #logger('warn', 'ctl_memory = %s.' % str(i['memory'])) if "consumers" in i: stats['ctl_consumers'] += i['consumers'] - stats['ctl_consumers_%s' % i['name']] = i['consumers'] + #stats['ctl_consumers_%s' % i['name']] = i['consumers'] + stats['ctl_consumers_%s' % mname ] = i['consumers'] + #logger('warn', 'ctl_consumers = %s.' % str(i['consumers'])) if not stats['ctl_memory'] > 0: logger('warn', '%s reports 0 memory usage. This is probably incorrect.' % RABBITMQ_API) diff --git a/cobbler/kickstarts/default.ks b/cobbler/kickstarts/default.ks index 89c0375..5452c1a 100644 --- a/cobbler/kickstarts/default.ks +++ b/cobbler/kickstarts/default.ks @@ -4,12 +4,6 @@ # System Authorization auth --useshadow --enablemd5 -# System Bootloader -bootloader --location=mbr - -# Clear MBR -zerombr - # Use Text Mode text @@ -70,6 +64,7 @@ $SNIPPET('kickstart_pre_partition_disks') # Enable installation monitoring $SNIPPET('kickstart_pre_anamon') +%end # Packages %packages --nobase @@ -85,6 +80,7 @@ libestr libgt liblogging rsyslog +%end %post --log=/var/log/post_install.log $SNIPPET('log_ks_post') @@ -109,3 +105,4 @@ $SNIPPET($kickstart_tool) #end if $SNIPPET('kickstart_post_anamon') $SNIPPET('kickstart_done') +%end diff --git a/cobbler/snippets/kickstart_chef_run.sh b/cobbler/snippets/kickstart_chef_run.sh index 56628ea..dcef27f 100644 --- a/cobbler/snippets/kickstart_chef_run.sh +++ b/cobbler/snippets/kickstart_chef_run.sh @@ -1,3 +1,15 @@ +#set ip_address = "" +#set ikeys = $interfaces.keys() +#for $iname in $ikeys + #set $idata = $interfaces[$iname] + #set $static = $idata["static"] + #set $management = $idata["management"] + #set $ip = $idata["ip_address"] + #if $management and $ip + #set $ip_address = $ip + #end if +#end for + cat << EOF > /etc/chef/run.sh #!/bin/bash touch /tmp/chef.log @@ -15,13 +27,20 @@ while true; do let all_nodes_success=1 for node in \\$nodes; do mkdir -p /var/log/chef/\\$node - cat << EOL > /etc/chef/\\$node.json + if [ ! -f /etc/chef/\\$node.json ]; then + cat << EOL > /etc/chef/\\$node.json #if $getVar("local_repo","") != "" -{"local_repo": "$local_repo"} +{ + "local_repo": "$local_repo", + "ip_address": "$ip_address" +} #else -{} +{ + "ip_address": "$ip_address" +} #end if EOL + fi if [ ! -f "/etc/chef/\\$node.pem" ]; then cat << EOL > /etc/rsyslog.d/\\$node.conf \\\\$ModLoad imfile diff --git a/cobbler/snippets/kickstart_post_anamon b/cobbler/snippets/kickstart_post_anamon index 8d5a47f..debfecc 100644 --- a/cobbler/snippets/kickstart_post_anamon +++ b/cobbler/snippets/kickstart_post_anamon @@ -26,7 +26,7 @@ cat << EOF > /etc/init.d/anamon.init # installation. # #end raw -/usr/local/sbin/anamon --watchfile "/var/log/boot.log /var/log/messages /var/log/dmesg" --name $name --server $server --port $http_port --exit +/usr/local/sbin/anamon --watchfile "/var/log/boot.log /var/log/messages /var/log/dmesg /var/log/anaconda.log" --name $name --server $server --port $http_port --exit chkconfig anamon.init off mv /etc/init.d/anamon.init /tmp/anamon.init EOF diff --git a/cobbler/snippets/kickstart_pre_partition_disks b/cobbler/snippets/kickstart_pre_partition_disks index d48b1c9..15bfb6d 100644 --- a/cobbler/snippets/kickstart_pre_partition_disks +++ b/cobbler/snippets/kickstart_pre_partition_disks @@ -210,6 +210,9 @@ done partition_size=\${partitions_size[/boot]:-500} unset \${partitions_name[/boot]} +echo "bootloader --location=mbr --driveorder=\${sorted_disks[0]}" >> /tmp/part-include +echo "zerombr" >> /tmp/part-include +echo "part biosboot --fstype=biosboot --size=1 --ondisk=\${sorted_disks[0]}" >> /tmp/part-include echo "part /boot --fstype=\${partition_fstype} --size=\${partition_size} --ondisk=\${sorted_disks[0]} --asprimary" >> /tmp/part-include partition_size=\${partitions_size[swap]:-0} if [[ "\$partition_size" == "0" ]]; then

    wU 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 Zpmz3h{%lVEF5@q@=%H%0)B8;S{09nOXovs+ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121229.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121229.log.gz deleted file mode 100644 index 59d87244d8abf0277108f0ad507ebc0d33fdf07a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119375 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