abiquo-chef-agent 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011 Abiquo Inc.
2
+ Copyright (c) 2011 Sergio Rubio <rubiojr@frameos.org>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ = abiquo-chef-agent
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to abiquo-chef-agent
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Sergio Rubio. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
7
+ gem.version = File.exist?('VERSION') ? File.read('VERSION') : ""
8
+
9
+ gem.name = "abiquo-chef-agent"
10
+ gem.homepage = "http://github.com/abiquo/abiquo-chef-agent"
11
+ gem.license = "MIT"
12
+ gem.summary = %Q{Abiquo Chef Agent}
13
+ gem.description = %Q{Abiquo Chef Agent}
14
+ gem.email = "srubio@abiquo.com"
15
+ gem.authors = ["Sergio Rubio"]
16
+ gem.add_runtime_dependency 'run-as-root'
17
+ gem.add_runtime_dependency 'chef'
18
+ gem.add_runtime_dependency 'daemons'
19
+ gem.add_runtime_dependency 'rest-client'
20
+ gem.add_runtime_dependency 'xml-simple'
21
+ gem.files.exclude 'rubygem-abiquo-chef-agent.spec'
22
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
23
+ end
24
+ Jeweler::RubygemsDotOrgTasks.new
25
+
26
+ task :default => :build
27
+
28
+ require 'rdoc/task'
29
+ Rake::RDocTask.new do |rdoc|
30
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
31
+
32
+ rdoc.rdoc_dir = 'rdoc'
33
+ rdoc.title = "abiquo-chef-agent #{version}"
34
+ rdoc.rdoc_files.include('README*')
35
+ rdoc.rdoc_files.include('lib/**/*.rb')
36
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.4
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'fileutils'
4
+ require 'rest-client'
5
+ require 'xmlsimple'
6
+ require 'abiquo-chef-agent'
7
+ require 'logger'
8
+ include Abiquo::Chef
9
+
10
+ at_exit do
11
+ if $!
12
+ log "BACKTRACE", :error
13
+ log $!.to_s, :error
14
+ log $@.to_s, :error
15
+ end
16
+ end
17
+
18
+ Log = Logger.new Abiquo::Chef::Config.log_file
19
+
20
+ def log(msg, level = :info)
21
+ if level == :debug and not $DEBUG
22
+ return
23
+ end
24
+ if level == :info
25
+ Log.info msg.to_s
26
+ elsif level == :warning
27
+ Log.warn msg.to_s
28
+ elsif level == :error
29
+ Log.error msg.to_s
30
+ else
31
+ Log.debug msg.to_s
32
+ end
33
+ end
34
+
35
+ CHEF_CONFIG_DIR = Abiquo::Chef::Config.chef_config_dir
36
+ BOOTSTRAP_XML = Abiquo::Chef::Config.bootstrap_backup_file
37
+ CLIENT_CERT = Abiquo::Chef::Config.client_cert_file
38
+
39
+ if File.exist? CLIENT_CERT
40
+ log "#{CLIENT_CERT} file found. If you want to re-run the Abiquo Chef Agent,\n" +
41
+ "delete the #{Abiquo::Chef::Config.chef_config_dir} directory first.\n" +
42
+ "Aborting.", :warning
43
+ exit 1
44
+ end
45
+
46
+ if not File.directory? CHEF_CONFIG_DIR
47
+ log "Creating #{CHEF_CONFIG_DIR} directory."
48
+ FileUtils.mkdir_p '/etc/chef'
49
+ end
50
+
51
+ #
52
+ # Try to fix system clock with ntpdate
53
+ #
54
+ log "Trying to synchronize system clock"
55
+ `/usr/sbin/ntpdate #{Abiquo::Chef::Config.ntp_server} > /dev/null 2>&1`
56
+ if $? != 0
57
+ log "Could not update the system clock using ntpdate and #{Abiquo::Chef::Config.ntp_server} ntp server", :warning
58
+ end
59
+
60
+ #
61
+ # Parse info from DCHP client leases file
62
+ #
63
+ log "Parsing leases file"
64
+ leases = Util.parse_leases_file
65
+ if not leases
66
+ log "Leases file not found. Aborting.", :error
67
+ exit 1
68
+ else
69
+ log "Leases found #{leases.inspect}"
70
+
71
+ #
72
+ # Request node info from Abiquo API
73
+ #
74
+ log "Requesting Chef config from API #{leases[:abiquo_api_url]} OneTime #{leases[:abiquo_api_token]}"
75
+ begin
76
+ xml = RestClient.get leases[:abiquo_api_url], :authorization => "OneTime #{leases[:abiquo_api_token]}"
77
+ rescue Exception => e
78
+ log "Error requesting node info from API", :error
79
+ log e.message, :error
80
+ log e.backtrace.join("\n "), :debug
81
+ exit 1
82
+ end
83
+ #
84
+ # Write the bootstrap XML
85
+ # Daemon will not run if this XML is found
86
+ #
87
+ File.open(BOOTSTRAP_XML, 'w') do |f|
88
+ f.puts xml
89
+ end
90
+
91
+ #
92
+ # Parse the XML returned by API
93
+ #
94
+ log "Parsing Bootstrap XML from API"
95
+ begin
96
+ bootstrap_config = BootstrapConfigParser.new(xml)
97
+ rescue Exception => e
98
+ log "Error parsing XML bootstrap file", :error
99
+ log e.message, :error
100
+ log e.backtrace.join("\n "), :debug
101
+ exit 1
102
+ end
103
+
104
+ #
105
+ # Write Chef validation pem
106
+ #
107
+ File.open(Abiquo::Chef::Config.validation_cert, 'w') do |f|
108
+ f.puts bootstrap_config.validation_cert
109
+ end
110
+
111
+ #
112
+ # Get required node info and write chef-client config file
113
+ #
114
+ validation_client_name = bootstrap_config.validation_client_name
115
+ chef_server_url = bootstrap_config.chef_server_url
116
+ recipes = bootstrap_config.run_list
117
+ if recipes.empty?
118
+ log "No recipes selected", :warning
119
+ recipes = []
120
+ else
121
+ log "Recipes found #{recipes.inspect}"
122
+ end
123
+ File.open('/etc/chef/client.rb', 'w') do |f|
124
+ f.puts "log_level :info"
125
+ f.puts "log_location STDOUT"
126
+ f.puts "chef_server_url '#{chef_server_url}'"
127
+ f.puts "validation_client_name '#{validation_client_name}'"
128
+ end
129
+
130
+ #
131
+ # Write first-boot.json attributes
132
+ #
133
+ if not recipes.empty?
134
+ recipes_json = '"run_list": ["'
135
+ recipes_json << recipes.join(',')
136
+ recipes_json << '"]'
137
+ File.open('/etc/chef/first-boot.json', 'w') do |f|
138
+ f.puts "{"
139
+ f.puts recipes_json
140
+ f.puts "}"
141
+ end
142
+ else
143
+ File.open('/etc/chef/first-boot.json', 'w') do |f|
144
+ f.puts "{}"
145
+ end
146
+ end
147
+
148
+ #
149
+ # Everything in place, no run the client
150
+ #
151
+ # first, we run it once to register and apply recipes
152
+ node_name = bootstrap_config.node_name
153
+ cmd = "chef-client -N #{node_name} --once -j /etc/chef/first-boot.json -L /var/log/chef-client.log"
154
+ log "Running chef-client first time"
155
+ log cmd
156
+ output = `#{cmd}`
157
+ if $? != 0
158
+ log "chef-client run failed", :error
159
+ log output, :debug
160
+ else
161
+ #
162
+ # Remove validation certs and bootstrap XML
163
+ #
164
+ FileUtils.rm(Abiquo::Chef::Config.validation_cert)
165
+ FileUtils.rm(Abiquo::Chef::Config.bootstrap_backup_file)
166
+ log "Setting hostname"
167
+ output = `hostname #{node_name} 2>&1`
168
+ log output, :debug
169
+ File.open('/etc/hostname', 'w') do |f|
170
+ f.puts node_name
171
+ end
172
+ log "chef-client run OK"
173
+ log "Running chef-client in daemon mode"
174
+ # Now we run it in daemon mode
175
+ cmd = "chef-client -N #{node_name} -i 1800 -d -L /var/log/chef-client.log"
176
+ output = `#{cmd}`
177
+ if $? != 0
178
+ log "Running chef-client in daemon mode failed.", :error
179
+ log output, :debug
180
+ else
181
+ log "Success."
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,145 @@
1
+ require 'xmlsimple'
2
+
3
+ module Abiquo
4
+ module Chef
5
+
6
+ VERSION="1.0.4"
7
+
8
+ class Config
9
+ def self.chef_config_dir
10
+ '/etc/chef'
11
+ end
12
+
13
+ def self.ntp_server
14
+ 'pool.ntp.org'
15
+ end
16
+
17
+ def self.log_file
18
+ '/var/log/abiquo-chef-agent.log'
19
+ end
20
+
21
+ def self.bootstrap_backup_file
22
+ '/etc/chef/abiquo_bootstrap.xml'
23
+ end
24
+
25
+ def self.client_cert_file
26
+ '/etc/chef/client.pem'
27
+ end
28
+
29
+ def self.validation_cert
30
+ '/etc/chef/validation.pem'
31
+ end
32
+
33
+ end
34
+
35
+ class BootstrapConfigParser
36
+
37
+ attr_reader :run_list, :node_name, :validation_client_name
38
+ attr_reader :chef_server_url, :validation_cert
39
+
40
+ def initialize(xml)
41
+ @raw_xml = xml
42
+ #
43
+ # HACK, FIXME
44
+ #
45
+ @hash = XmlSimple.xml_in xml.gsub("&#xD;","\n")
46
+ parse
47
+ end
48
+
49
+ def parse
50
+ @node_name = @hash['node'].first
51
+ if not @node_name or @node_name.strip.chomp.empty?
52
+ raise Exception.new("Invalid bootstrap XML. Missing <node> info.")
53
+ end
54
+
55
+ @node_info = @hash['chef'].first
56
+ if not @node_info
57
+ raise Exception.new("Invalid bootstrap XML. Missing <chef> info.")
58
+ end
59
+
60
+ @validation_client_name = @node_info['validation-client-name'].first
61
+ if not @validation_client_name or @validation_client_name.strip.chomp.empty?
62
+ raise Exception.new("Invalid bootstrap XML. Missing <validation-client-name> info.")
63
+ end
64
+
65
+ @validation_cert = @node_info['validation-cert'].first
66
+ if not @validation_cert or @validation_cert.strip.chomp.empty?
67
+ raise Exception.new("Invalid bootstrap XML. Missing <validation-cert> info.")
68
+ end
69
+ @chef_server_url = @node_info['chef-server-url'].first
70
+ if not @chef_server_url or @chef_server_url.strip.chomp.empty?
71
+ raise Exception.new("Invalid bootstrap XML. Missing <chef-server-url> info.")
72
+ end
73
+ @run_list = @node_info['runlist'].first['element']
74
+ if not @run_list
75
+ raise Exception.new("Invalid bootstrap XML. Missing <runlist>> info.")
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ class Util
82
+
83
+ #
84
+ # Tries to find the right leases file
85
+ #
86
+ def self.find_leases_file(search_dirs = ['/var/lib/dhcp3', '/var/lib/dhcp', '/var/lib/dhclient'])
87
+ leases = []
88
+ search_dirs.each do |d|
89
+ Dir["#{d}/*"].each do |f|
90
+ mtime = File.mtime f
91
+ if (Time.now - mtime) <= 600
92
+ leases << f
93
+ end
94
+ end
95
+ end
96
+ return leases
97
+ end
98
+
99
+ #
100
+ # Returns a Hash representing the leases file
101
+ # {
102
+ # :ip => ipaddr,
103
+ # :interface => iface,
104
+ # :mac => hwmac,
105
+ # :routers => router_addr,
106
+ # :domain_name => domain_name,
107
+ # :abiquo_api_url => url,
108
+ # :abiquo_api_token => token
109
+ # }
110
+ #
111
+ def self.parse_leases_file
112
+ files = find_leases_file
113
+ files.each do |file|
114
+ f = File.open(file)
115
+ l = {}
116
+ f.each do |line|
117
+ case line
118
+ when /fixed-address (.*);/
119
+ l[:ip] = $1
120
+ when /interface (.*);/
121
+ l[:interface] = $1
122
+ when /hardware ethernet (.*);/
123
+ l[:mac] = $1
124
+ when /option dhcp-server-identifier (.*);/
125
+ l[:dhcp_server] = $1
126
+ when /option routers (.*);/
127
+ l[:routers] = $1
128
+ when /option domain-name (.*);/
129
+ l[:domain_name] = $1
130
+ when /option vendor-encapsulated-options\s*"(.*)"\s*;/
131
+ tokens = $1.split('@@')
132
+ l[:abiquo_api_url] = tokens[0].gsub("\\", "")
133
+ l[:abiquo_api_token] = tokens[1]
134
+ end
135
+ end
136
+ if l[:abiquo_api_token]
137
+ log "Default leases file '#{file}' found"
138
+ return l
139
+ end
140
+ end
141
+ return nil
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,103 @@
1
+ #!/bin/sh
2
+ #
3
+ # abiquo-chef-agent Abiquo Agent to Manage Chef Node initial registration
4
+ #
5
+ # chkconfig: - 80 01
6
+ # description: Abiquo Chef Agent
7
+
8
+ ### BEGIN INIT INFO
9
+ # Provides: abiquo-chef-agent
10
+ # Required-Start: $local_fs $network $remote_fs
11
+ # Required-Stop: $local_fs $network $remote_fs
12
+ # Should-Start: $named $time
13
+ # Should-Stop: $named $time
14
+ # Short-Description: Startup script for the Abiquo Chef Agent
15
+ # Description: Abiquo Agent to Manage Chef Node initial registration
16
+ ### END INIT INFO
17
+
18
+ # Source function library.
19
+ . /etc/rc.d/init.d/functions
20
+
21
+ exec="/usr/bin/abiquo-chef-agent"
22
+ prog="abiquo-chef-agent"
23
+ config="/etc/sysconfig/abiquo-chef-agent"
24
+
25
+ [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
26
+
27
+ lockfile=/var/lock/subsys/$prog
28
+
29
+ start() {
30
+ [ -x $exec ] || exit 5
31
+ #[ -f $config ] || exit 6
32
+ echo -n $"Starting $prog: "
33
+ $exec start
34
+ retval=$?
35
+ echo
36
+ [ $retval -eq 0 ] && touch $lockfile
37
+ return $retval
38
+ }
39
+
40
+ stop() {
41
+ echo -n $"Stopping $prog: "
42
+ # stop it here, often "killproc $prog"
43
+ $exec stop
44
+ retval=$?
45
+ echo
46
+ [ $retval -eq 0 ] && rm -f $lockfile
47
+ return $retval
48
+ }
49
+
50
+ restart() {
51
+ stop
52
+ start
53
+ }
54
+
55
+ reload() {
56
+ restart
57
+ }
58
+
59
+ force_reload() {
60
+ restart
61
+ }
62
+
63
+ rh_status() {
64
+ # run checks to determine if the service is running or use generic status
65
+ status $prog
66
+ }
67
+
68
+ rh_status_q() {
69
+ rh_status >/dev/null 2>&1
70
+ }
71
+
72
+
73
+ case "$1" in
74
+ start)
75
+ rh_status_q && exit 0
76
+ $1
77
+ ;;
78
+ stop)
79
+ rh_status_q || exit 0
80
+ $1
81
+ ;;
82
+ restart)
83
+ $1
84
+ ;;
85
+ reload)
86
+ rh_status_q || exit 7
87
+ $1
88
+ ;;
89
+ force-reload)
90
+ force_reload
91
+ ;;
92
+ status)
93
+ rh_status
94
+ ;;
95
+ condrestart|try-restart)
96
+ rh_status_q || exit 0
97
+ restart
98
+ ;;
99
+ *)
100
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
101
+ exit 2
102
+ esac
103
+ exit $?
@@ -0,0 +1,16 @@
1
+ # abiquo-chef-agent - Abiquo Chef Client Wrapper
2
+ #
3
+ # Chef Client provides the Chef configuration management daemon
4
+
5
+ description "Abiquo Chef Agent"
6
+
7
+ start on (local-filesystems and net-device-up IFACE=eth0)
8
+ stop on runlevel [!2345]
9
+
10
+ pre-start script
11
+ test -x /usr/bin/abiquo-chef-run || { stop; exit 0; }
12
+ export RUBYOPT="-d"
13
+ end script
14
+
15
+ exec /usr/bin/abiquo-chef-run
16
+
@@ -0,0 +1,4 @@
1
+ LOGFILE=/var/log/chef/client.log
2
+ CONFIG=/etc/chef/client.rb
3
+ INTERVAL=1800
4
+ SPLAY=20
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: abiquo-chef-agent
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sergio Rubio
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: run-as-root
16
+ requirement: &11309100 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *11309100
25
+ - !ruby/object:Gem::Dependency
26
+ name: chef
27
+ requirement: &11308500 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *11308500
36
+ - !ruby/object:Gem::Dependency
37
+ name: daemons
38
+ requirement: &11307760 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *11307760
47
+ - !ruby/object:Gem::Dependency
48
+ name: rest-client
49
+ requirement: &11336000 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *11336000
58
+ - !ruby/object:Gem::Dependency
59
+ name: xml-simple
60
+ requirement: &11335420 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *11335420
69
+ description: Abiquo Chef Agent
70
+ email: srubio@abiquo.com
71
+ executables:
72
+ - abiquo-chef-run
73
+ extensions: []
74
+ extra_rdoc_files:
75
+ - LICENSE.txt
76
+ - README.md
77
+ files:
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - VERSION
82
+ - bin/abiquo-chef-run
83
+ - lib/abiquo-chef-agent.rb
84
+ - scripts/abiquo-chef-agent.init
85
+ - scripts/debian/abiquo-chef-agent.conf.upstart
86
+ - scripts/debian/chef-client.defaults
87
+ homepage: http://github.com/abiquo/abiquo-chef-agent
88
+ licenses:
89
+ - MIT
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.10
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Abiquo Chef Agent
112
+ test_files: []