chef-provisioning-crowbar 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 749999debb44d64aaa65360b79ba838692756f80
4
+ data.tar.gz: 48a387ef467e1786ecf054e0b458923004f80216
5
+ SHA512:
6
+ metadata.gz: e24a4d37cef67b30a675bbeb9f32e4a2d4336de5d561ab9c6eb348e56c3cb8c8484241da35199a2ea7074433c8fdf99935f6f6207ddfdd39e25b7cf52a2c2e12
7
+ data.tar.gz: 440d09ad30356e528eee54d1cf70659f03276d8fdca9e47c9de2e7160b2945339860a64e1208bc14306a90624f43c96c5c93b3959b2de11dc8637af605fa2d77
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ # Apache 2
2
+
3
+ This submodule of OpenCrowbar is Apache 2 licensed.
4
+
5
+ ## Copyright 2014, Rob Hirschfeld
6
+ ## Copyright 2015, Judd Maltin
7
+
8
+ Licensed under the Apache License, Version 2.0 (the "License");
9
+ you may not use this file except in compliance with the License.
10
+ You may obtain a copy of the License at
11
+
12
+ http://www.apache.org/licenses/LICENSE-2.0
13
+
14
+ Unless required by applicable law or agreed to in writing, software
15
+ distributed under the License is distributed on an "AS IS" BASIS,
16
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ See the License for the specific language governing permissions and
18
+ limitations under the License.
19
+
20
+
21
+ ## For details
22
+
23
+ See [[doc/licenses/README.md]]
data/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # OpenCrowbar for Chef-Provisioning
2
+
3
+ **Chef Provisioning with Crowbar to treat your metal like a cloud**
4
+
5
+ This repo contains the interface between Chef Provisioning (https://github.com/opscode/chef-provisioning/) and OpenCrowbar.
6
+
7
+ > make sure you are running Ruby 1.9 and related gems for this tool. check `ruby -v` to verify.
8
+
9
+ > Better yet: *use chef-dk* - it now has chef-provisioning gems included.
10
+
11
+ ## Background
12
+
13
+ Crowbar discovers and manages your gear - preferably hardware nodes. The typical model would be to get Crowbar running on your admin network, and start booting up your gear. Crowbar will discover and inventory your gear automatically. You can then use Chef Provisioning to tell Crowbar to do all those things it's good at: install the OS you want, configure the BIOS, RAID, networking, and manage the power states of the gear. You can keep using Chef-Provisioning and Crowbar when you want to power down or re-image those nodes.
14
+
15
+ Crowbar manages nodes in groups of "deployments." All nodes start and spend their lives in the foundational "system" deployment. They're then added to deployments you create to effect change on them. By creating a new deployment and adding nodes to it, all the roles you defined as belonging to that deployment get run on the gear.
16
+
17
+ When you write a recipe with Chef Provisioning and Crowbar, Chef Provisioning requests from Crowbar to "allocate_machine." Crowbar then looks in its inventory for a node that is "alive" according to Crowbar (Crowbar can power it on and ssh into it,) but not used in any other deployments than "system." Once Crowbar finds an appropriate node, it adds the node to the "ready" deployment (default name, and will create it if necessary.) This will start Crowbar configuring the hardware, OS, network, etc. Chef Provisioning waits patiently for all this to get finished, so it can consider the node "ready."
18
+
19
+ A Chef Provisioning "ready" machine is a Crowbar node which has completed the tasks in the proper deployment ("ready"), and Crowbar has given up managing it until further notice. In Crowbar terms, that means that the milestone noderole for the deployment is 'active', and the node is marked in Crowbar as node["available"]:false so the annealer will not manage the node's noderoles.
20
+
21
+ ## Example
22
+
23
+ Chef Provisioning with Crowbar will treat your gear like a cloud!
24
+
25
+ Example Session:
26
+
27
+ See the nodes available from Crowbar:
28
+
29
+ ```bash
30
+ bash-4.1# crowbar deployments nodes "system" | grep '"alias"'
31
+ "alias": "d52-54-32-f9-00-00",
32
+ "alias": "d52-54-32-f8-00-00",
33
+ "alias": "d52-54-32-f5-00-00",
34
+ "alias": "d52-54-32-f6-00-00",
35
+ "alias": "d52-54-32-f7-00-00",
36
+ "alias": "be727e682d0d",
37
+ bash-4.1#
38
+ ```
39
+
40
+ And you can see them with `knife` against the chef server in Crowbar:
41
+
42
+ ```bash
43
+ $ knife node list -s http://192.168.124.10
44
+ be727e682d0d.crowbar.org
45
+ d52-54-32-f5-00-00.crowbar.org
46
+ d52-54-32-f6-00-00.crowbar.org
47
+ d52-54-32-f7-00-00.crowbar.org
48
+ d52-54-32-f8-00-00.crowbar.org
49
+ d52-54-32-f9-00-00.crowbar.org
50
+ ```
51
+
52
+ So, to provision one of these, run the example recipe:
53
+
54
+ Example Recipe:
55
+
56
+ ```ruby
57
+ require 'chef/provisioning'
58
+ with_driver 'crowbar'
59
+
60
+ # Here I indicate the chef server running inside Crowbar. If you like, use your own Chef Server, or just
61
+ # use Chef Zero by calling chef-client -z <recipe_name>.
62
+
63
+ with_chef_server 'https://192.168.124.10',
64
+ :client_name => 'metal',
65
+ :signing_key_filename => '/home/metal/.chef/metal.pem'
66
+
67
+ machine "chef-provisioning-#{rand(1000)}" do
68
+ machine_options :crowbar_options => { 'provisioner-target_os' => 'centos-7.0' }
69
+ end
70
+ ```
71
+
72
+ Example chef-client Invocation:
73
+
74
+ ```bash
75
+ $ chef-client ./crowbar_test.rb
76
+ ```
77
+
78
+ and you will see the new node being allocated and made ready. Here's a few nodes in the "ready"
79
+ deployment.
80
+
81
+ ```bash
82
+ # crowbar deployments nodes "ready" | grep '"alias"'
83
+ "alias": "chef-provisioning-another-brother-42",
84
+ "alias": "chef-provisioning-another-brother-97",
85
+ "alias": "chef-provisioning-example-56",
86
+ "alias": "chef-provisioning-another-brother-80",
87
+ ```
88
+
89
+
90
+ Real world use would have you put a run-list in the `machine` resource, so chef can use the nodes to actually do things.
91
+
92
+ ## Setup
93
+
94
+ ### Networking
95
+
96
+ Before you start, your chef-provisioning box must be on the crowbar administration network, so it can ssh into the slave nodes and do its cheffy thing. In our typical development environments, that's simply a matter of setting your host OSs network as follows:
97
+
98
+ ```bash
99
+ $ sudo ip a add 192.168.124.2/24 dev docker0
100
+ ```
101
+
102
+ ### Chef
103
+
104
+ #### Chef-DK
105
+
106
+ I use the chef-dk, which now has Chef Provisioning built in. Get your `knife` and `chef-client` all setup for development work.
107
+
108
+ #### Gems, no Chef-DK
109
+
110
+ Or you can use Chef gems.
111
+
112
+ *Gem dependencies* You also need the HTTParty, Chef and Chef-Metal gems: `sudo gem install httparty chef chef-metal`
113
+
114
+ ## Example gem build script and test run
115
+
116
+ This is an example file to run and build chef provisioning crowbar.
117
+
118
+ All the exciting stuff is happening in chef-provisioning-crowbar/cookbooks/app/recipes/crowbar-test.rb
119
+
120
+ /$HOME/build_and_test_chef-provisioning-crowbar.sh
121
+
122
+ ```bash
123
+ # source the chef-dk env
124
+ . $HOME/.bash_profile
125
+ CPC_REPO_PATH=<path to chef-provisioning-crowbar git repo>
126
+ cd ${CPC_REPO_PATH}
127
+ gem build chef-provisioning-crowbar.gemspec
128
+ gem install --ignore-dependencies --no-ri --no-rdoc chef-provisioning-crowbar-0.0.2.gem
129
+ cd ${CPC_REPO_PATH}/cookbooks/app/recipes/
130
+ # run with debugging..
131
+ #chef-client -l debug -z crowbar_test.rb
132
+ # or not
133
+ #chef-client -z crowbar_test.rb
134
+ # or omit -z to use a chef server indicated elsewhere
135
+ chef-client ./crowbar_test.rb
136
+ ```
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+ # Copyright 2014, Rob Hirschfeld
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'bundler'
17
+ require 'bundler'
18
+ require 'bundler/gem_tasks'
19
+
20
+ task :spec do
21
+ require File.expand_path('spec/run')
22
+ end
@@ -0,0 +1,280 @@
1
+ # Copyright 2014, Rob Hirschfeld
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ #require 'chef/mixin/shell_out'
16
+ require 'chef/provisioning/driver'
17
+ require 'chef/provisioning/machine/unix_machine'
18
+ require 'chef/provisioning/machine_spec'
19
+ require 'chef/provisioning/convergence_strategy/install_cached'
20
+ require 'chef/provisioning/transport/ssh'
21
+ require 'chef/provisioning/crowbar_driver/version'
22
+ require 'etc'
23
+ require 'time'
24
+ #require 'cheffish/merged_config'
25
+ require 'crowbar/core'
26
+
27
+ class Chef
28
+ module Provisioning
29
+ module CrowbarDriver
30
+
31
+ class Driver < Chef::Provisioning::Driver
32
+
33
+
34
+ ALLOCATE_DEPLOYMENT = 'system'
35
+ READY_DEPLOYMENT = 'ready'
36
+ TARGET_NODE_ROLE = "crowbar-installed-node"
37
+ API_BASE = "/api/v2"
38
+
39
+ def initialize(driver_url, config)
40
+ super(driver_url, config)
41
+ @crowbar = Crowbar.new
42
+ #config[:private_key_paths] = [ "$HOME/.ssh/id_rsa" ]
43
+ #config[:log_level] = :debug
44
+ end
45
+
46
+ # Passed in a driver_url, and a config in the format of Driver.config.
47
+ def self.from_url(driver_url, config)
48
+ Driver.new(driver_url, config)
49
+ end
50
+
51
+ def self.canonicalize_url(driver_url, config)
52
+ [ driver_url, config ]
53
+ end
54
+
55
+ # Acquire a machine, generally by provisioning it. Returns a Machine
56
+ # object pointing at the machine, allowing useful actions like setup,
57
+ # converge, execute, file and directory.
58
+
59
+ def allocate_machine(action_handler, machine_spec, machine_options)
60
+
61
+ @crowbar.log_level(config[:log_level])
62
+
63
+ if machine_spec.location
64
+ if !@crowbar.node_exists?(machine_spec.location['server_id'])
65
+ # It doesn't really exist
66
+ action_handler.perform_action "Machine #{machine_spec.location['server_id']} does not really exist. Recreating ..." do
67
+ machine_spec.location = nil
68
+ end
69
+ end
70
+ end
71
+
72
+ if !machine_spec.location
73
+ action_handler.perform_action "Crowbar: #{@crowbar} Creating server #{machine_spec.name} with options #{machine_options}" do
74
+ # TODO: Make sure SSH keys are found locally here? Or in allocate_node?
75
+ # get ssh pubkey from current user
76
+ #result = shell_out("cat ~/.ssh/id_rsa.pub", :cwd => '$HOME')
77
+ #sshkey = result.stdout
78
+ #action_handler.report_progress "sshpubkey on admin server #{sshkey}\n"
79
+ # put it on the crowbar server provisioner
80
+ #@crowbar.add_sshkey(result.stdout)
81
+
82
+ server = allocate_node(machine_spec.name, machine_options, action_handler)
83
+ server_id = server["id"]
84
+ debug "allocate server_id = #{server_id}"
85
+ machine_spec.location = {
86
+ 'driver_url' => driver_url,
87
+ 'driver_version' => Chef::Provisioning::CrowbarDriver::VERSION,
88
+ 'server_id' => server_id,
89
+ 'node_role_id' => server["node_role_id"]
90
+ # 'bootstrap_key' => sshkey
91
+ }
92
+ end
93
+ end
94
+ end
95
+
96
+ # Ready a machine moves the machine from the System deployment
97
+ # to the Ready deployment. Default Ready deployment is named 'ready'
98
+ # but will pick up machine_configs that match
99
+ def ready_machine(action_handler, machine_spec, machine_options)
100
+ debug machine_spec.location
101
+
102
+ server_id = machine_spec.location['server_id']
103
+ unless @crowbar.node_alive?(server_id)
104
+ action_handler.perform_action "Powering up machine #{server_id}" do
105
+ @crowbar.power(server_id, "on")
106
+ end
107
+ end
108
+
109
+ nr_id = machine_spec.location['node_role_id']
110
+
111
+ action_handler.report_progress "Awaiting ready machine..."
112
+ action_handler.perform_action "done waiting for machine id: #{server_id}" do
113
+ loop do
114
+ break if @crowbar.node_ready(server_id,nr_id)
115
+ sleep 5
116
+ end
117
+ action_handler.report_progress "waited for machine - machine is ready. machine id: #{server_id}"
118
+ end
119
+
120
+ # set the machine to "reserved" to take control away from Crowbar
121
+ node = @crowbar.node(server_id)
122
+ node['available'] = false
123
+ @crowbar.set_node(server_id, node)
124
+
125
+ # Return the Machine object
126
+ machine_for(machine_spec, machine_options)
127
+ end
128
+
129
+ def machine_for(machine_spec, machine_options)
130
+ ssh_options = {
131
+ :auth_methods => ['publickey'],
132
+ }
133
+ server_id = machine_spec.location['server_id']
134
+ node_admin_addresses = @crowbar.node_attrib(server_id, 'network-admin_addresses')
135
+ node_ipv4_admin_net_ip = node_admin_addresses['value'][0].split('/')[0]
136
+ node_ipv4_admin_net_ip = node_admin_addresses['value'][0].split('/')[0]
137
+ #node_ipv6_admin_net_ip = node_admin_addresses['value'][1]
138
+
139
+ transport = Chef::Provisioning::Transport::SSH.new(node_ipv4_admin_net_ip, 'root', ssh_options, {}, config)
140
+ convergence_strategy = Chef::Provisioning::ConvergenceStrategy::InstallCached.new(machine_options[:convergence_options], config)
141
+ Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport, convergence_strategy)
142
+ end
143
+
144
+ def create_ssh_transport(machine_spec)
145
+ crowbar_ssh_config = crowbar_ssh_config_for(machine_spec)
146
+ hostname = crowbar_ssh_config['HostName']
147
+ username = crowbar_ssh_config['User']
148
+ ssh_options = {
149
+ :port => '22',
150
+ :auth_methods => ['publickey'],
151
+ #:user_known_hosts_file => crowbar_ssh_config['UserKnownHostsFile'],
152
+ :paranoid => false, #yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
153
+ :keys => [ '$HOME/.ssh/id_rsa' ],
154
+ :keys_only => true
155
+ }
156
+ Chef::Provisioning::Transport::SSH.new(hostname, username, ssh_options, options, config) end
157
+
158
+ def ensure_deployment(to_deployment)
159
+ unless @crowbar.deployment_exists?(to_deployment)
160
+ @crowbar.deployment_create(to_deployment)
161
+ debug("Crowbar deployment '#{to_deployment}' does not exist... creating...")
162
+ end
163
+ end
164
+
165
+ def get_non_admin_nodes(names=[])
166
+ # get available nodes
167
+ from_deployment = ALLOCATE_DEPLOYMENT
168
+ pool = @crowbar.non_admin_nodes_in_deployment(from_deployment)
169
+ raise "No available non-admin nodes in pool '#{from_deployment}'" if !pool || pool.size == 0
170
+ #action_handler.report_progress "Pool size: #{pool.size}"
171
+ # make sure node name isn't taken
172
+ good_nodes = []
173
+ names.each do |name|
174
+ pool.each do |node|
175
+ if node['alias'] == name
176
+ debug "Node #{name} already exists, skipping."
177
+ break
178
+ end
179
+ good_nodes << node
180
+ end
181
+ end
182
+ return good_nodes
183
+ end
184
+
185
+
186
+ def set_node_and_bind_noderole(node,name,role,to_deployment,crowbar_options={})
187
+ debug("set_node_foundling #{node}")
188
+ node["alias"] = name
189
+ node["deployment"] = to_deployment
190
+ raise "Setting node #{node["alias"]} to deployment \"#{to_deployment}\" failed." unless @crowbar.set_node(node["id"], node)
191
+ #action_handler.report_progress "Crowbar node\'s deployment attirbute set to \"#{to_deployment}\"\n"
192
+
193
+
194
+ # bind the NodeRole if missing (eventually set the OS property)
195
+ bind = {:node=>node["id"], :role=>role, :deployment=>to_deployment}
196
+ # blindly add node role > we need to make this smarter and skip if unneeded
197
+ # query node for all its noderoles and skip if noderole is in finished state
198
+ node["node_role_id"] = @crowbar.bind_node_role(bind)
199
+ #action_handler.report_progress "Crowbar node #{node["id"]} noderole bound to #{TARGET_NODE_ROLE} deployment #{to_deployment} as noderole #{node["node_role_id"]}\n"
200
+
201
+ # set crowbar_options. they're attribs in crowbar
202
+ crowbar_options.each do |attrib, value|
203
+ #attribs = { "provisioner-target_os" => machine_options[:crowbar_options]['provisioner-target_os'] }
204
+ @crowbar.set_node_attrib( node["id"], attrib, value )
205
+ end
206
+ node
207
+ end
208
+
209
+ # debug messages
210
+ def debug(msg)
211
+ Chef::Log.debug msg
212
+ end
213
+
214
+ # Allocate many machines simultaneously
215
+ def allocate_machines(action_handler, specs_and_options, parallelizer)
216
+ to_deployment = READY_DEPLOYMENT
217
+
218
+ # check for ready deployment
219
+ ensure_deployment(to_deployment)
220
+ # can the deployment be set to proposed?
221
+ @crowbar.propose_deployment(to_deployment)
222
+
223
+ #private_key = get_private_key('bootstrapkey')
224
+ servers = []
225
+ server_names = []
226
+ specs_and_options.each do |machine_spec, machine_options|
227
+ if !machine_spec.location
228
+ servers << [ machine_spec.name, machine_options ]
229
+ server_names << machine_spec.name
230
+ # skip name collisions
231
+ # add nodes to ready deployment
232
+ # Tell the cloud API to spin them all up at once
233
+ action_handler.perform_action "Allocating servers #{server_names.join(',')} from the cloud" do
234
+ role = TARGET_NODE_ROLE
235
+ node = get_non_admin_nodes([machine_spec.name])[0]
236
+ server = set_node_and_bind_noderole(node,machine_spec.name,role,to_deployment,machine_options[:crowbar_options])
237
+ server_id = server["id"]
238
+ debug "allocate server_id = #{server_id}"
239
+ machine_spec.location = {
240
+ 'driver_url' => driver_url,
241
+ 'driver_version' => Chef::Provisioning::CrowbarDriver::VERSION,
242
+ 'server_id' => server_id,
243
+ 'node_role_id' => server["node_role_id"]
244
+ }
245
+ end
246
+ end
247
+ end
248
+
249
+
250
+ # commit deployment
251
+ @crowbar.commit_deployment(to_deployment)
252
+ end
253
+
254
+ # follow getready process to allocate nodes
255
+ def allocate_node(name, machine_options, action_handler)
256
+
257
+ role = TARGET_NODE_ROLE
258
+ to_deployment = READY_DEPLOYMENT
259
+
260
+ ensure_deployment(to_deployment)
261
+
262
+ @crowbar.propose_deployment(to_deployment)
263
+
264
+ my_node = get_non_admin_nodes([name])[0]
265
+ #puts(my_node)
266
+ set_node_and_bind_noderole(my_node,name,role,to_deployment,machine_options[:crowbar_options])
267
+
268
+ @crowbar.commit_deployment(to_deployment)
269
+
270
+ # at this point Crowbar will bring up the node in the background
271
+ # we can return the node handle to the user
272
+ my_node
273
+
274
+ end
275
+ private
276
+
277
+ end # Class
278
+ end
279
+ end # Module
280
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright 2014, Rob Hirschfeld
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ class Chef
16
+ module Provisioning
17
+ module CrowbarDriver
18
+ VERSION = '0.1.0'
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright 2014, Rob Hirschfeld
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'chef/provisioning/crowbar_driver/driver'
16
+
17
+ Chef::Provisioning.register_driver_class("crowbar", Chef::Provisioning::CrowbarDriver::Driver)
@@ -0,0 +1,280 @@
1
+ # Copyright 2014, Rob Hirschfeld
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'rubygems'
16
+ require 'httparty'
17
+ require 'json'
18
+
19
+
20
+ class Crowbar
21
+ include HTTParty
22
+
23
+ API_BASE = '/api/v2'
24
+
25
+
26
+
27
+ def initialize(url = "http://127.0.0.1:3000", u = "crowbar", p = "crowbar")
28
+ @url = url + API_BASE
29
+ self.class.digest_auth u, p
30
+ self.class.base_uri @url
31
+ debug "initialize #{@url}"
32
+ end
33
+
34
+ @llevel = :info
35
+ def log_level(level)
36
+ @llevel = level
37
+ debug("incoming #{level} instance #{@llevel}")
38
+ end
39
+
40
+ def debug(msg)
41
+ puts "\nCROWBAR #{@llevel}: #{msg}" if @llevel == :debug
42
+ end
43
+
44
+ # debug_output $stderr
45
+ format :json
46
+
47
+ #http://192.168.222.6:3000/api/v2/nodes/1/attribs/provisioner-access_keys
48
+ def add_sshkey(sshkey)
49
+ res = self.class.get("/nodes/1/attribs/provisioner-access_keys")
50
+ if res.code != 200
51
+ raise("Could not get sshkey on admin node #{res.code} #{res.message}")
52
+ end
53
+ debug res
54
+ res = self.class.put("/nodes/1/attribs/provisioner-access_keys", :body => sshkey)
55
+ if res.code != 200
56
+ raise("Could not put sshkey on admin node #{res.code} #{res.message}")
57
+ end
58
+ return res
59
+ end
60
+
61
+ def deployment_create(name, parent_id=1)
62
+ data = { :name => name, :parent_id => parent_id }
63
+ res = self.class.post("/deployments", :body => data)
64
+ if res.code != 200
65
+ raise("Could not create deployment #{name}. #{res.code} #{res.message}")
66
+ end
67
+ res
68
+ end
69
+
70
+ def commit_deployment(name)
71
+ deployment_set(name,"commit")
72
+ end
73
+
74
+ def propose_deployment(name)
75
+ deployment_set(name,"propose")
76
+ end
77
+
78
+ def deployment_set(name,state)
79
+ res = self.class.put("/deployments/#{name}/#{state}")
80
+ if res.code != 200
81
+ raise("Could not set deployment #{name} to #{state}. #{res.code} #{res.message}")
82
+ end
83
+ return res
84
+ end
85
+
86
+ def deployment_exists?(name)
87
+ res = self.class.get("/deployments/#{name}")
88
+ debug("res code deployment exists #{res.code}")
89
+ return false unless res.code == 200
90
+ true
91
+ end
92
+
93
+ def node_exists?(name)
94
+ res = self.class.get("/nodes/#{name}")
95
+ debug("res code node exists #{res.code}")
96
+ return false unless res.code == 200
97
+ true
98
+ end
99
+
100
+ def non_admin_nodes_in_deployment(name, attrs={})
101
+ #attrs = {'x-return-attributes' => '["admin"]' }
102
+ n = nodes_in_deployment(name,attrs)
103
+ n.reject{ |e| e["admin"] == true } || []
104
+ end
105
+
106
+
107
+ def find_node_in_deployment(node,deployment,attrs=[])
108
+ res = self.class.get("/deployments/#{deployment}/nodes", :headers => {'x-return-attributes' => "#{attrs.to_json}" } )
109
+ res.index{|e|e["name"] == node || e["id"] == node}
110
+ end
111
+
112
+ def nodes_in_deployment(name,attrs={})
113
+ self.class.get("/deployments/#{name}/nodes", :headers => attrs )
114
+ end
115
+
116
+ def set_deployment_to_proposed(name)
117
+ self.class.put("/deployments/#{name}/propose")
118
+ end
119
+
120
+ def ssh_private_key(name)
121
+ res = self.class.get("nodes/#{name}/attribs/#{attrib}")
122
+ if res.code != 200
123
+ raise("Could not get node \"#{name}\" ssh keys")
124
+ end
125
+ res
126
+ end
127
+
128
+ def node_status(id)
129
+ res = self.class.get("http://127.0.0.1:3000/api/status/nodes/#{id}" )
130
+ if res.code != 200
131
+ raise("Could not get node status #{res.code} #{res.message}")
132
+ end
133
+ res
134
+ end
135
+
136
+ def node_alive?(node_id)
137
+ n = node(node_id, ['alive'])
138
+ n["alive"]
139
+ end
140
+
141
+ def node_ready(node_id,node_role_id)
142
+ # get noderole state == 0 and runcount >= 1
143
+ # get node alive = true
144
+ nr = self.class.get("/node_roles/#{node_role_id}")
145
+ if nr["state"] == 0 && nr["run_count"] >= 1 && node_alive?(node_id)
146
+ return true
147
+ else
148
+ return false
149
+ end
150
+ end
151
+
152
+ def set_node(id, data)
153
+ res = self.class.put("/nodes/#{id}", :body => data)
154
+ if res.code != 200
155
+ raise("Could not update node #{res.code} #{res.message}")
156
+ end
157
+ return res
158
+ end
159
+
160
+ def node(id,attrs=[])
161
+ my_head = {}
162
+ attrs.size > 0 && my_head = { :headers => {'x-return-attributes' => attrs.to_json } }
163
+ res = self.class.get("/nodes/#{id}", my_head )
164
+ if res.code != 200
165
+ raise("Could not get node \"#{id}\" #{res.code} #{res.message}")
166
+ end
167
+ res
168
+ end
169
+
170
+ def node_attrib(id,attrib)
171
+ res = self.class.get("/nodes/#{id}/attribs/#{attrib}")
172
+ if res.code != 200
173
+ raise("Could not get node \"#{id}\" attrib #{attrib} - #{res.code} #{res.message}")
174
+ end
175
+ return res
176
+ end
177
+
178
+ def deployment(id,attrs={})
179
+ res = self.class.get("/deployments/#{id}", :headers => attrs )
180
+ if res.code != 200
181
+ raise("Could not get deployment \"#{id}\" #{res.code} #{res.message}")
182
+ end
183
+ return res
184
+ end
185
+
186
+ def power(name,action)
187
+ res = self.class.put("/nodes/#{name}/power?poweraction=#{action}")
188
+ if res.code != 200
189
+ raise("Could not power #{action} node #{name}")
190
+ end
191
+ end
192
+
193
+ def set_node_role_attrib(nr_id, attrib, value)
194
+ res = self.class.put("/node_roles/#{nr_id}/attribs/#{attrib}", :body => { :value => "#{value}" } )
195
+ if res.code != 200
196
+ raise("Could not set node_role #{nr_id} attrib #{attrib} to value #{value}")
197
+ end
198
+ res
199
+ end
200
+
201
+ def set_node_attrib(n_id, attrib, value)
202
+ res = self.class.put("/nodes/#{n_id}/attribs/#{attrib}", :body => { :value => "#{value}" } )
203
+ if res.code != 200
204
+ raise("Could not set node #{n_id} attrib #{attrib} to value #{value}: code #{res.code} #{res.message}")
205
+ end
206
+ res
207
+ end
208
+
209
+ def bind_node_role(data)
210
+ raise("Count not bind role to node. #{data}") unless
211
+ res = self.class.post("/node_roles", :body => data)
212
+ res['id']
213
+ end
214
+
215
+
216
+ private
217
+
218
+
219
+ # # connect to the Crowbar API
220
+ # # this currently AUTHS every call, we need to optimize that so that we can reuse the auth tokens
221
+ # def authenticate(req,uri,data=nil)
222
+ #
223
+ # # build request
224
+ # request_headers={
225
+ # "Accept" => "application/json",
226
+ # "Content-Type" => "application/json"}
227
+ # #request_headers['x-return-attributes']=$attributes if $attributes
228
+ # # build URL
229
+ # uri = URI.parse(@url)
230
+ # uri.user= @username
231
+ # uri.password= @password
232
+ # # starting HTTP session
233
+ # res=nil
234
+ # Net::HTTP.start(uri.host, uri.port) {|http|
235
+ # http.read_timeout = 500
236
+ # r = http.new(uri.request_uri,request_headers)
237
+ # r.body = data if data
238
+ # res = http.request r
239
+ # debug "(a) return code: #{res.code}"
240
+ # debug "(a) return body: #{res.body}"
241
+ # debug "(a) return headers:"
242
+ # res.each_header do |h, v|
243
+ # debug "#{h}: #{v}"
244
+ # end
245
+ #
246
+ # if res['www-authenticate']
247
+ # debug "(a) uri: #{uri}"
248
+ # debug "(a) www-authenticate: #{res['www-authenticate']}"
249
+ # debug "(a) req-method: #{req::METHOD}"
250
+ # auth=Net::HTTP::DigestAuth.new.auth_header(uri,
251
+ # res['www-authenticate'],
252
+ # req::METHOD)
253
+ # r.add_field 'Authorization', auth
254
+ # res = http.request r
255
+ # end
256
+ # }
257
+ # res
258
+ # end
259
+ #
260
+ # # Common data and debug handling.
261
+ # def go(verb,path,data=nil)
262
+ # uri = URI.parse(@url + API_BASE + path)
263
+ # # We want to give valid JSON to the API, so if we were
264
+ # # handed an array or a hash as the data to be messed with,
265
+ # # turn it into a blob of JSON.
266
+ # data = data.to_json if data.is_a?(Array) || data.is_a?(Hash)
267
+ # res = authenticate(verb,uri,data)
268
+ # debug "(#{verb}) hostname: #{uri.host}:#{uri.port}"
269
+ # debug "(#{verb}) request: #{uri.path}"
270
+ # debug "(#{verb}) data: #{data}"
271
+ # debug "(#{verb}) return code: #{res.code}"
272
+ # debug "(#{verb}) return body: #{res.body}"
273
+ # [ JSON.parse(res.body), res.code.to_i ]
274
+ # end
275
+ #
276
+ #
277
+
278
+
279
+ end
280
+
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-provisioning-crowbar
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Judd Maltin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '11.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '11.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: chef-provisioning
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.15'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.15'
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Crowbar is an open-source, multi-purpose node deployment tool.
70
+ email: judd@newgoliath.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files:
74
+ - README.md
75
+ - LICENSE
76
+ files:
77
+ - LICENSE
78
+ - README.md
79
+ - Rakefile
80
+ - lib/chef/provisioning/crowbar_driver/driver.rb
81
+ - lib/chef/provisioning/crowbar_driver/version.rb
82
+ - lib/chef/provisioning/driver_init/crowbar.rb
83
+ - lib/crowbar/core.rb
84
+ homepage: https://github.com/newgoliath/chef-provisioning-crowbar
85
+ licenses:
86
+ - Apache 2
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.4.1
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Driver for creating Crowbar servers in Chef Provisioning.
108
+ test_files: []
109
+ has_rdoc: