knife-rackspace-cluster 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/chef/knife/rax_cluster_base.rb +152 -0
- data/lib/chef/knife/rax_cluster_build.rb +220 -0
- data/lib/chef/knife/rax_cluster_change.rb +95 -0
- data/lib/chef/knife/rax_cluster_create.rb +212 -0
- data/lib/chef/knife/rax_cluster_delete.rb +76 -0
- data/lib/chef/knife/rax_cluster_expand.rb +104 -0
- data/lib/chef/knife/rax_cluster_list.rb +56 -0
- data/lib/chef/knife/super_rax.rb +126 -0
- metadata +82 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
#require 'chef/knife/rackspace/rackspace_base'
|
3
|
+
#require 'chef/knife/rackspafce/rackspace_server_create'
|
4
|
+
|
5
|
+
|
6
|
+
# Make sure you subclass from Chef::Knife
|
7
|
+
|
8
|
+
class Chef
|
9
|
+
class Knife
|
10
|
+
|
11
|
+
|
12
|
+
module RaxClusterBase
|
13
|
+
|
14
|
+
def self.included(includer)
|
15
|
+
includer.class_eval do
|
16
|
+
|
17
|
+
deps do
|
18
|
+
|
19
|
+
require "net/https"
|
20
|
+
require 'net/http'
|
21
|
+
require "uri"
|
22
|
+
requie 'json'
|
23
|
+
#require 'chef/shef/ext'
|
24
|
+
#require 'rubygems'
|
25
|
+
#require 'chef/knife/rackspace_server_create'
|
26
|
+
#require 'json'
|
27
|
+
require 'fog'
|
28
|
+
#require "thread"
|
29
|
+
#require 'net/ssh/multi'
|
30
|
+
require 'readline'
|
31
|
+
require 'chef/knife/bootstrap'
|
32
|
+
require 'chef/json_compat'
|
33
|
+
|
34
|
+
Chef::Knife::Bootstrap.load_deps
|
35
|
+
#include Knife::RackspaceBase
|
36
|
+
end
|
37
|
+
#option :rax_cluster_auth,
|
38
|
+
# :short => "-auth auth_url_for_cluster",
|
39
|
+
# :long => "--rackspace-api-url url",
|
40
|
+
# :description => "Specify the URL to auth for creation of LB's, i.e. (https:////identity.api.rackspacecloud.com/v1.1)",
|
41
|
+
# :proc => Proc.new { |key| Chef::Config[:knife][:rax_cluster_auth] = key }
|
42
|
+
|
43
|
+
#option :rackspace_api_key,
|
44
|
+
# :short => "-K KEY",
|
45
|
+
# :long => "--rackspace-api-key KEY",
|
46
|
+
# :description => "Your rackspace API key",
|
47
|
+
# :proc => Proc.new { |key| Chef::Config[:knife][:rackspace_api_key] = key }
|
48
|
+
#
|
49
|
+
#option :rackspace_username,
|
50
|
+
# :short => "-A USERNAME",
|
51
|
+
# :long => "--rackspace-username USERNAME",
|
52
|
+
# :description => "Your rackspace API username",
|
53
|
+
# :proc => Proc.new { |username| Chef::Config[:knife][:rackspace_username] = username }
|
54
|
+
#
|
55
|
+
#option :rackspace_version,
|
56
|
+
# :long => '--rackspace-version VERSION',
|
57
|
+
# :description => 'Rackspace Cloud Servers API version',
|
58
|
+
# :default => "v1",
|
59
|
+
# :proc => Proc.new { |version| Chef::Config[:knife][:rackspace_version] = version }
|
60
|
+
#
|
61
|
+
#option :rackspace_api_auth_url,
|
62
|
+
# :long => "--rackspace-api-auth-url URL",
|
63
|
+
# :description => "Your rackspace API auth url",
|
64
|
+
# :default => "auth.api.rackspacecloud.com",
|
65
|
+
# :proc => Proc.new { |url| Chef::Config[:knife][:rackspace_api_auth_url] = url },
|
66
|
+
# :default => "https://identity.api.rackspacecloud.com/v1.1/auth"
|
67
|
+
#
|
68
|
+
#option :rackspace_endpoint,
|
69
|
+
# :long => "--rackspace-endpoint URL",
|
70
|
+
# :description => "Your rackspace API endpoint",
|
71
|
+
# :default => "https://dfw.servers.api.rackspacecloud.com/v2",
|
72
|
+
# :proc => Proc.new { |url| Chef::Config[:knife][:rackspace_endpoint] = url }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
def populate_environment
|
76
|
+
self.setup_environment_vars{
|
77
|
+
rackspace_username = Chef::Config[:knife][:rackspace_username]
|
78
|
+
rackspace_password = Chef::Config[:knife][:rackspace_password]
|
79
|
+
rackspace_endpoint = Chef::Config[:knife][:rackspace_auth_url]
|
80
|
+
@headers = {"x-auth-user" => rackspace_username, "x-auth-key" => rackspace_password,
|
81
|
+
"auth_url" => rackspace_endpoint,
|
82
|
+
"content-type" => "application/json", "Accept" => "application/json"}
|
83
|
+
#@rax_endpoint = Chef::Config[:knife][:narciss_url] + "/" + Chef::Config[:knife][:narciss_version] + "/"
|
84
|
+
#if rackspace_username_set
|
85
|
+
# @rackspace_username = rackspace_username
|
86
|
+
#end
|
87
|
+
#if rackspace_password_set
|
88
|
+
# @rackspace_password = rackspace_password
|
89
|
+
#end
|
90
|
+
#if rackspace_tenant_set
|
91
|
+
# @rackspace_tenant = rackspace_tenant
|
92
|
+
#end
|
93
|
+
#if rackspace_endpoint_set
|
94
|
+
# @rackspace_endpoint = rackspace_endpoint
|
95
|
+
#end
|
96
|
+
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def make_web_call(httpVerb,uri,headers=nil, request_content=nil)
|
101
|
+
verbs =
|
102
|
+
{"get" => "Net::HTTP::Get.new(uri.request_uri, headers)",
|
103
|
+
"head" => "Net::HTTP::Head.new(uri.request_uri, headers)",
|
104
|
+
"put" => "Net::HTTP::Put.new(uri.request_uri, headers)",
|
105
|
+
"delete" => "Net::HTTP::Delete.new(uri.request_uri, headers)",
|
106
|
+
"post" => "Net::HTTP::Post.new(uri.request_uri, headers)"
|
107
|
+
}
|
108
|
+
#Get to work boy! This is Ruby!
|
109
|
+
uri = URI.parse(uri)
|
110
|
+
|
111
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
112
|
+
#if uri.host =~ /https/
|
113
|
+
http.use_ssl = true
|
114
|
+
#end
|
115
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
116
|
+
request = eval verbs[httpVerb]
|
117
|
+
if httpVerb == 'post' or httpVerb == 'put'
|
118
|
+
request.body = request_content
|
119
|
+
end
|
120
|
+
response = http.request(request)
|
121
|
+
if not ('200'..'204').include? response.code
|
122
|
+
puts "Error making web call"
|
123
|
+
puts "Response code : #{response.code}"
|
124
|
+
puts "Response body : #{response.body}"
|
125
|
+
#puts "Response Headers : #{response.headers}"
|
126
|
+
end
|
127
|
+
return response
|
128
|
+
|
129
|
+
end
|
130
|
+
#Just used for lbaas since fog doesn't allow meta data on LB's
|
131
|
+
def authenticate(auth_url='https://identity.api.rackspacecloud.com/v1.1/auth',username=Chef::Config[:knife][:rackspace_api_username] ,password=Chef::Config[:knife][:rackspace_api_key])
|
132
|
+
auth_json = {
|
133
|
+
"credentials" => {
|
134
|
+
"username" => username,
|
135
|
+
"key" => password
|
136
|
+
}
|
137
|
+
}
|
138
|
+
headers = {"Content-Type" => "application/json"}
|
139
|
+
auth_data = make_web_call('post', auth_url, headers, auth_json.to_json)
|
140
|
+
lb_data = JSON.parse(auth_data.body)
|
141
|
+
lb_returned = {'auth_token' => lb_data['auth']['token']['id'], 'lb_urls' => lb_data['auth']['serviceCatalog']['cloudLoadBalancers'] }
|
142
|
+
return lb_returned
|
143
|
+
end
|
144
|
+
def msg_pair(label, value, color=:cyan)
|
145
|
+
if value && !value.to_s.empty?
|
146
|
+
puts "#{ui.color(label, color)}: #{value}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
|
2
|
+
require 'chef/knife/rackspace_server_create'
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RaxClusterBuild < RackspaceServerCreate
|
6
|
+
|
7
|
+
include Knife::RackspaceBase
|
8
|
+
|
9
|
+
deps do
|
10
|
+
require 'fog'
|
11
|
+
require 'readline'
|
12
|
+
require 'chef/json_compat'
|
13
|
+
require 'chef/knife/bootstrap'
|
14
|
+
Chef::Knife::Bootstrap.load_deps
|
15
|
+
end
|
16
|
+
|
17
|
+
banner "knife rackspace server create (options)"
|
18
|
+
|
19
|
+
option :flavor,
|
20
|
+
:short => "-f FLAVOR",
|
21
|
+
:long => "--flavor FLAVOR",
|
22
|
+
:description => "The flavor of server; default is 2 (512 MB)",
|
23
|
+
:proc => Proc.new { |f| Chef::Config[:knife][:flavor] = f.to_i },
|
24
|
+
:default => 2
|
25
|
+
|
26
|
+
option :image,
|
27
|
+
:short => "-I IMAGE",
|
28
|
+
:long => "--image IMAGE",
|
29
|
+
:description => "The image of the server",
|
30
|
+
:proc => Proc.new { |i| Chef::Config[:knife][:image] = i.to_s }
|
31
|
+
|
32
|
+
option :server_name,
|
33
|
+
:short => "-S NAME",
|
34
|
+
:long => "--server-name NAME",
|
35
|
+
:description => "The server name"
|
36
|
+
|
37
|
+
option :chef_node_name,
|
38
|
+
:short => "-N NAME",
|
39
|
+
:long => "--node-name NAME",
|
40
|
+
:description => "The Chef node name for your new node"
|
41
|
+
|
42
|
+
option :private_network,
|
43
|
+
:long => "--private-network",
|
44
|
+
:description => "Use the private IP for bootstrapping rather than the public IP",
|
45
|
+
:boolean => true,
|
46
|
+
:default => false
|
47
|
+
|
48
|
+
option :ssh_user,
|
49
|
+
:short => "-x USERNAME",
|
50
|
+
:long => "--ssh-user USERNAME",
|
51
|
+
:description => "The ssh username; default is 'root'",
|
52
|
+
:default => "root"
|
53
|
+
|
54
|
+
option :ssh_password,
|
55
|
+
:short => "-P PASSWORD",
|
56
|
+
:long => "--ssh-password PASSWORD",
|
57
|
+
:description => "The ssh password"
|
58
|
+
|
59
|
+
option :identity_file,
|
60
|
+
:short => "-i IDENTITY_FILE",
|
61
|
+
:long => "--identity-file IDENTITY_FILE",
|
62
|
+
:description => "The SSH identity file used for authentication"
|
63
|
+
|
64
|
+
option :prerelease,
|
65
|
+
:long => "--prerelease",
|
66
|
+
:description => "Install the pre-release chef gems"
|
67
|
+
|
68
|
+
option :bootstrap_version,
|
69
|
+
:long => "--bootstrap-version VERSION",
|
70
|
+
:description => "The version of Chef to install",
|
71
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
72
|
+
|
73
|
+
option :distro,
|
74
|
+
:short => "-d DISTRO",
|
75
|
+
:long => "--distro DISTRO",
|
76
|
+
:description => "Bootstrap a distro using a template; default is 'chef-full'",
|
77
|
+
:proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
|
78
|
+
:default => "chef-full"
|
79
|
+
|
80
|
+
option :template_file,
|
81
|
+
:long => "--template-file TEMPLATE",
|
82
|
+
:description => "Full path to location of template to use",
|
83
|
+
:proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
|
84
|
+
:default => false
|
85
|
+
|
86
|
+
option :run_list,
|
87
|
+
:short => "-r RUN_LIST",
|
88
|
+
:long => "--run-list RUN_LIST",
|
89
|
+
:description => "Comma separated list of roles/recipes to apply",
|
90
|
+
:proc => lambda { |o| o.split(/[\s,]+/) },
|
91
|
+
:default => []
|
92
|
+
|
93
|
+
option :first_boot_attributes,
|
94
|
+
:short => "-j JSON_ATTRIBS",
|
95
|
+
:long => "--json-attributes",
|
96
|
+
:description => "A JSON string to be added to the first run of chef-client",
|
97
|
+
:proc => lambda { |o| JSON.parse(o) },
|
98
|
+
:default => {}
|
99
|
+
|
100
|
+
option :rackspace_metadata,
|
101
|
+
:short => "-M JSON",
|
102
|
+
:long => "--rackspace-metadata JSON",
|
103
|
+
:description => "JSON string version of metadata hash to be supplied with the server create call",
|
104
|
+
:proc => Proc.new { |m| Chef::Config[:knife][:rackspace_metadata] = JSON.parse(m) },
|
105
|
+
:default => ""
|
106
|
+
|
107
|
+
option :host_key_verify,
|
108
|
+
:long => "--[no-]host-key-verify",
|
109
|
+
:description => "Verify host key, enabled by default",
|
110
|
+
:boolean => true,
|
111
|
+
:default => true
|
112
|
+
|
113
|
+
def tcp_test_ssh(hostname)
|
114
|
+
tcp_socket = TCPSocket.new(hostname, 22)
|
115
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
116
|
+
if readable
|
117
|
+
Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
|
118
|
+
yield
|
119
|
+
true
|
120
|
+
else
|
121
|
+
false
|
122
|
+
end
|
123
|
+
rescue Errno::ETIMEDOUT
|
124
|
+
false
|
125
|
+
rescue Errno::EPERM
|
126
|
+
false
|
127
|
+
rescue Errno::ECONNREFUSED
|
128
|
+
sleep 2
|
129
|
+
false
|
130
|
+
rescue Errno::EHOSTUNREACH
|
131
|
+
sleep 2
|
132
|
+
false
|
133
|
+
ensure
|
134
|
+
tcp_socket && tcp_socket.close
|
135
|
+
end
|
136
|
+
|
137
|
+
def run
|
138
|
+
$stdout.sync = true
|
139
|
+
|
140
|
+
unless Chef::Config[:knife][:image]
|
141
|
+
ui.error("You have not provided a valid image value. Please note the short option for this value recently changed from '-i' to '-I'.")
|
142
|
+
exit 1
|
143
|
+
end
|
144
|
+
|
145
|
+
node_name = get_node_name(config[:chef_node_name] || config[:server_name])
|
146
|
+
|
147
|
+
server = connection.servers.create(
|
148
|
+
:name => node_name,
|
149
|
+
:image_id => Chef::Config[:knife][:image],
|
150
|
+
:flavor_id => locate_config_value(:flavor),
|
151
|
+
:metadata => Chef::Config[:knife][:rackspace_metadata]
|
152
|
+
)
|
153
|
+
|
154
|
+
msg_pair("Instance ID", server.id)
|
155
|
+
msg_pair("Host ID", server.host_id)
|
156
|
+
msg_pair("Name", server.name)
|
157
|
+
msg_pair("Flavor", server.flavor.name)
|
158
|
+
msg_pair("Image", server.image.name)
|
159
|
+
msg_pair("Metadata", server.metadata)
|
160
|
+
|
161
|
+
print "\n#{ui.color("Waiting server", :magenta)}"
|
162
|
+
|
163
|
+
# wait for it to be ready to do stuff
|
164
|
+
server.wait_for { print "."; ready? }
|
165
|
+
|
166
|
+
puts("\n")
|
167
|
+
|
168
|
+
msg_pair("Public DNS Name", public_dns_name(server))
|
169
|
+
msg_pair("Public IP Address", public_ip(server))
|
170
|
+
msg_pair("Private IP Address", private_ip(server))
|
171
|
+
msg_pair("Password", server.password)
|
172
|
+
|
173
|
+
print "\n#{ui.color("Waiting for sshd", :magenta)}"
|
174
|
+
|
175
|
+
#which IP address to bootstrap
|
176
|
+
bootstrap_ip_address = public_ip(server)
|
177
|
+
if config[:private_network]
|
178
|
+
bootstrap_ip_address = private_ip(server)
|
179
|
+
end
|
180
|
+
Chef::Log.debug("Bootstrap IP Address #{bootstrap_ip_address}")
|
181
|
+
if bootstrap_ip_address.nil?
|
182
|
+
ui.error("No IP address available for bootstrapping.")
|
183
|
+
exit 1
|
184
|
+
end
|
185
|
+
|
186
|
+
print(".") until tcp_test_ssh(bootstrap_ip_address) {
|
187
|
+
sleep @initial_sleep_delay ||= 10
|
188
|
+
puts("done")
|
189
|
+
}
|
190
|
+
bootstrap_for_node(server, bootstrap_ip_address).run
|
191
|
+
|
192
|
+
puts "\n"
|
193
|
+
msg_pair("Instance ID", server.id)
|
194
|
+
msg_pair("Host ID", server.host_id)
|
195
|
+
msg_pair("Name", server.name)
|
196
|
+
msg_pair("Flavor", server.flavor.name)
|
197
|
+
msg_pair("Image", server.image.name)
|
198
|
+
msg_pair("Metadata", server.metadata)
|
199
|
+
msg_pair("Public DNS Name", public_dns_name(server))
|
200
|
+
msg_pair("Public IP Address", public_ip(server))
|
201
|
+
msg_pair("Private IP Address", private_ip(server))
|
202
|
+
msg_pair("Password", server.password)
|
203
|
+
msg_pair("Environment", config[:environment] || '_default')
|
204
|
+
msg_pair("Run List", config[:run_list].join(', '))
|
205
|
+
ipaddress = ''
|
206
|
+
#if not server.private_ip_address
|
207
|
+
# ipaddress = server.public_ip_address
|
208
|
+
#end
|
209
|
+
#if not ipaddress
|
210
|
+
# ipaddress = "false"
|
211
|
+
#end
|
212
|
+
return_hash = {"public_ip" => public_ip(server), "private_ip" => private_ip(server), "server_name" => server.name, "server_id" => server.id}
|
213
|
+
return return_hash
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class Chef
|
2
|
+
class Knife
|
3
|
+
class RaxClusterChange < Knife
|
4
|
+
attr_accessor :headers, :rax_endpoint, :lb_id
|
5
|
+
include Knife::RaxClusterBase
|
6
|
+
|
7
|
+
banner "knife rax cluster change LB_ID (chef_options)"
|
8
|
+
deps do
|
9
|
+
require 'fog'
|
10
|
+
require 'readline'
|
11
|
+
require 'chef/json_compat'
|
12
|
+
require 'chef/knife/bootstrap'
|
13
|
+
Chef::Knife::Bootstrap.load_deps
|
14
|
+
end
|
15
|
+
|
16
|
+
option :lb_region,
|
17
|
+
:short => "-r lb_region",
|
18
|
+
:long => "--load-balancer-region lb_region",
|
19
|
+
:description => "Load balancer region (only supports ORD || DFW)",
|
20
|
+
:proc => Proc.new { |lb_region| Chef::Config[:knife][:lb_region] = lb_region},
|
21
|
+
:default => "ORD"
|
22
|
+
|
23
|
+
option :run_list,
|
24
|
+
:long => "--run-list run_list",
|
25
|
+
:description => "Pass a comma delimted run list --run-list 'recipe[apt],role[base]'",
|
26
|
+
:proc => Proc.new { |run_list| Chef::Config[:knife][:run_list] = run_list}
|
27
|
+
|
28
|
+
option :chef_env,
|
29
|
+
:long => "--chef-env environment",
|
30
|
+
:description => "Pass a comma delimted run list --run-list 'recipe[apt],role[base]'",
|
31
|
+
:proc => Proc.new { |chef_env| Chef::Config[:knife][:chef_env] = chef_env}
|
32
|
+
|
33
|
+
def change_chef_vars(instances, &block)
|
34
|
+
instances.each { |inst|
|
35
|
+
query = "name:#{inst['server_name']}"
|
36
|
+
query_nodes = Chef::Search::Query.new
|
37
|
+
query_nodes.search('node', query) do |node_item|
|
38
|
+
yield node_item
|
39
|
+
end
|
40
|
+
}
|
41
|
+
end
|
42
|
+
def run
|
43
|
+
if !config[:run_list] and !config[:chef_env]
|
44
|
+
ui.fatal "Please specify either --run-list or --chef-env to change on your cluster"
|
45
|
+
exit(1)
|
46
|
+
end
|
47
|
+
if @name_args.empty?
|
48
|
+
ui.fatal "Please specify a load balancer ID to update"
|
49
|
+
exit(1)
|
50
|
+
end
|
51
|
+
lb_auth = authenticate()
|
52
|
+
headers = {"x-auth-token" => lb_auth['auth_token'], "content-type" => "application/json"}
|
53
|
+
lb_url = ""
|
54
|
+
lb_auth['lb_urls'].each {|lb|
|
55
|
+
if config[:lb_region].to_s.downcase == lb['region'].to_s.downcase
|
56
|
+
lb_url = lb['publicURL']
|
57
|
+
break
|
58
|
+
end
|
59
|
+
lb_url = lb['publicURL']
|
60
|
+
}
|
61
|
+
@name_args.each {|arg|
|
62
|
+
lb_url = lb_url + "/loadbalancers/#{arg}"
|
63
|
+
lb_data = make_web_call("get", lb_url, headers)
|
64
|
+
lb_data = JSON.parse(lb_data.body)
|
65
|
+
instances = []
|
66
|
+
lb_data['loadBalancer']['metadata'].each{|md|
|
67
|
+
instances << {"server_name" => md['key'], "uuid" => md['uuid']}
|
68
|
+
}
|
69
|
+
|
70
|
+
if config[:run_list]
|
71
|
+
config[:run_list] = config[:run_list].split(",")
|
72
|
+
change_chef_vars(instances) { |node_item|
|
73
|
+
ui.msg "Changing #{node_item.name} run list to #{config[:run_list]}"
|
74
|
+
node_item.run_list(config[:run_list])
|
75
|
+
node_item.save
|
76
|
+
}
|
77
|
+
end
|
78
|
+
if config[:chef_env]
|
79
|
+
change_chef_vars(instances){|node_item|
|
80
|
+
ui.msg "Changing #{node_item.name} chef environment to #{config[:chef_env]}"
|
81
|
+
node_item.chef_environment(config[:chef_env])
|
82
|
+
node_item.save
|
83
|
+
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
}
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'chef/knife/rax_cluster_base'
|
2
|
+
require 'chef/knife/rax_cluster_build'
|
3
|
+
|
4
|
+
class Chef
|
5
|
+
class Knife
|
6
|
+
class RaxClusterCreate < Knife
|
7
|
+
attr_accessor :headers, :rax_endpoint, :lb_name
|
8
|
+
include Knife::RaxClusterBase
|
9
|
+
banner "knife rax cluster create (cluster_name) [options]"
|
10
|
+
deps do
|
11
|
+
require 'fog'
|
12
|
+
require 'readline'
|
13
|
+
require 'chef/json_compat'
|
14
|
+
require 'chef/knife/bootstrap'
|
15
|
+
Chef::Knife::Bootstrap.load_deps
|
16
|
+
end
|
17
|
+
|
18
|
+
option :algorithm,
|
19
|
+
:short => "-a Load_balacner_algorithm",
|
20
|
+
:long => "--algorithm algorithm",
|
21
|
+
:description => "Load balancer algorithm",
|
22
|
+
:proc => Proc.new { |algorithm| Chef::Config[:knife][:algorithm] = algorithm },
|
23
|
+
:default => "ROUND_ROBIN"
|
24
|
+
|
25
|
+
option :blue_print,
|
26
|
+
:short => "-B Blue_print_file",
|
27
|
+
:long => "--map blue_print_file",
|
28
|
+
:description => "Path to blue Print json file",
|
29
|
+
:proc => Proc.new { |i| Chef::Config[:knife][:blue_print] = i.to_s }
|
30
|
+
|
31
|
+
option :port,
|
32
|
+
:short => "-lb_port port",
|
33
|
+
:long => "--load-balancer-port port",
|
34
|
+
:description => "Load balancer port",
|
35
|
+
:proc => Proc.new { |port| Chef::Config[:knife][:port] = port},
|
36
|
+
:default => "80"
|
37
|
+
|
38
|
+
option :timeout,
|
39
|
+
:short => "-t timeout",
|
40
|
+
:long => "--load-balancer-timeout timeout",
|
41
|
+
:description => "Load balancer timeout",
|
42
|
+
:proc => Proc.new { |timeout| Chef::Config[:knife][:timeout] = timeout},
|
43
|
+
:default => "30"
|
44
|
+
|
45
|
+
option :lb_region,
|
46
|
+
:short => "-r lb_region",
|
47
|
+
:long => "--load-balancer-region lb_region",
|
48
|
+
:description => "Load balancer region (only supports ORD || DFW)",
|
49
|
+
:proc => Proc.new { |lb_region| Chef::Config[:knife][:lb_region] = lb_region},
|
50
|
+
:default => "ORD"
|
51
|
+
|
52
|
+
option :protocol,
|
53
|
+
:short => "-p protocol",
|
54
|
+
:long => "--load-balancer-protocol protocol",
|
55
|
+
:description => "Load balancer protocol",
|
56
|
+
:proc => Proc.new { |protocol| Chef::Config[:knife][:protocol] = protocol},
|
57
|
+
:default => 'HTTP'
|
58
|
+
|
59
|
+
option :generate_map_template,
|
60
|
+
:short => "-G",
|
61
|
+
:long => "--generate_map_template",
|
62
|
+
:description => "Generate server map Template in current dir named map_template.json"
|
63
|
+
|
64
|
+
#option :session_persistence,
|
65
|
+
#:short => "-S on_or_off",
|
66
|
+
#:long => "--session-persistence session_persistence_on_or_off",
|
67
|
+
#:description => "Load balancer session persistence on or off",
|
68
|
+
#:proc => Proc.new { |session_persistence| Chef::Config[:knife][:session_persistence] = session_persistence}
|
69
|
+
|
70
|
+
def generate_map_template
|
71
|
+
file_name = "./map_template.json"
|
72
|
+
template = %q(
|
73
|
+
{
|
74
|
+
"blue_print" :
|
75
|
+
{
|
76
|
+
"name_convention" : "web",
|
77
|
+
"run_list" : [
|
78
|
+
"recipe[apt]"
|
79
|
+
],
|
80
|
+
"quantity" : 1,
|
81
|
+
"chef_env" : "dev",
|
82
|
+
"image_ref" : "a9753ff4-f46c-427d-9498-1358564f622f",
|
83
|
+
"flavor" : 2
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
}
|
88
|
+
)
|
89
|
+
|
90
|
+
File.open(file_name, 'w') { |file| file.write(template)}
|
91
|
+
end
|
92
|
+
def create_lb(instances)
|
93
|
+
lb_request = {
|
94
|
+
"loadBalancer" => {
|
95
|
+
"name" => @lb_name.to_s + "_cluster",
|
96
|
+
"port" => config[:port] || '80',
|
97
|
+
"protocol" => config[:protocol] || 'HTTP',
|
98
|
+
"algorithm" => config[:algorithm] || 'ROUND_ROBIN',
|
99
|
+
"virtualIps" => [
|
100
|
+
{
|
101
|
+
"type" => "PUBLIC"
|
102
|
+
}
|
103
|
+
],
|
104
|
+
"nodes" => [],
|
105
|
+
"metadata" => []
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
instances.each {|inst|
|
110
|
+
lb_request['loadBalancer']['nodes'] << {"address" => inst['ip_address'], 'port' =>Chef::Config[:knife][:port] || '80', "condition" => "ENABLED" }
|
111
|
+
lb_request['loadBalancer']['metadata'] << {"key" => inst['server_name'], "value" => inst['uuid']}
|
112
|
+
}
|
113
|
+
lb_authenticate = authenticate()
|
114
|
+
lb_url = ""
|
115
|
+
lb_authenticate['lb_urls'].each {|lb|
|
116
|
+
if config[:lb_region].to_s.downcase == lb['region'].to_s.downcase
|
117
|
+
lb_url = lb['publicURL']
|
118
|
+
break
|
119
|
+
end
|
120
|
+
lb_url = lb['publicURL']
|
121
|
+
}
|
122
|
+
lb_url = lb_url + "/loadbalancers"
|
123
|
+
|
124
|
+
headers = {'Content-type' => 'application/json', 'x-auth-token' => lb_authenticate['auth_token']}
|
125
|
+
create_lb_call = make_web_call("post",lb_url, headers, lb_request.to_json )
|
126
|
+
lb_details = JSON.parse(create_lb_call.body)
|
127
|
+
ui.msg "Load Balancer Cluster Sucesfully Created"
|
128
|
+
ui.msg "Load Balancer ID: #{lb_details['loadBalancer']['id']}"
|
129
|
+
ui.msg "Load Balancer Name: #{lb_details['loadBalancer']['name']}"
|
130
|
+
lb_ip = ""
|
131
|
+
lb_details['loadBalancer']['virtualIps'].each {|lb| (lb['ipVersion'] == "IPV4") ? lb_ip = lb['address'] : "not_found"}
|
132
|
+
ui.msg "Load Balancer IP Address: #{lb_ip}"
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def deploy(blue_print,update_cluster=nil)
|
137
|
+
(File.exist?(blue_print)) ? map_contents = JSON.parse(File.read(blue_print)) : map_contents = JSON.parse(blue_print)
|
138
|
+
sleep_interval = 1
|
139
|
+
instances = []
|
140
|
+
if map_contents.has_key?("blue_print")
|
141
|
+
bp_values = map_contents['blue_print']
|
142
|
+
bootstrap_nodes = []
|
143
|
+
quantity = bp_values['quantity'].to_i
|
144
|
+
quantity.times do |node_name|
|
145
|
+
node_name = rand(900000000)
|
146
|
+
create_server = Chef::Knife::RaxClusterBuild.new
|
147
|
+
#create_server.config[:identity_file] = config[:identity_file]
|
148
|
+
Chef::Config[:knife][:image] = bp_values['image_ref']
|
149
|
+
create_server.config[:chef_node_name] = bp_values['name_convention'] + node_name.to_s
|
150
|
+
create_server.config[:environment] = bp_values['chef_env']
|
151
|
+
Chef::Config[:environment] = bp_values['chef_env']
|
152
|
+
create_server.config[:run_list] = bp_values['run_list']
|
153
|
+
Chef::Config[:knife][:flavor] = bp_values['flavor']
|
154
|
+
begin
|
155
|
+
bootstrap_nodes << Thread.new { Thread.current['server_return'] = create_server.run }
|
156
|
+
rescue
|
157
|
+
ui.msg "Bootstrapping failed"
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
quantity.times do |times|
|
162
|
+
if quantity > 20
|
163
|
+
sleep_interval = 3
|
164
|
+
end
|
165
|
+
sleep(sleep_interval)
|
166
|
+
bootstrap_nodes[times].join
|
167
|
+
instances << {"server_name" => bootstrap_nodes[times]['server_return']['server_name'],
|
168
|
+
"ip_address" => bootstrap_nodes[times]['server_return']['private_ip'],
|
169
|
+
"uuid" => bootstrap_nodes[times]['server_return']['server_id'],
|
170
|
+
"name_convention" => bp_values['name_convention'],
|
171
|
+
"chef_env" => bp_values['chef_env'],
|
172
|
+
"run_list" => bp_values['run_list']
|
173
|
+
}
|
174
|
+
end
|
175
|
+
end
|
176
|
+
if update_cluster
|
177
|
+
return instances
|
178
|
+
else
|
179
|
+
create_lb(instances)
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
def run
|
187
|
+
#Generate template config
|
188
|
+
if config[:generate_map_template]
|
189
|
+
generate_map_template()
|
190
|
+
ui.msg "Map template saved as ./map_template.json"
|
191
|
+
exit()
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
if @name_args.empty? or @name_args.size > 1
|
196
|
+
ui.fatal "Please specify a single name for your cluster"
|
197
|
+
exit(1)
|
198
|
+
end
|
199
|
+
#Set load balancer name
|
200
|
+
@lb_name = @name_args[0]
|
201
|
+
|
202
|
+
if config[:blue_print]
|
203
|
+
deploy(config[:blue_print])
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'chef/knife/rax_cluster_base'
|
2
|
+
require 'chef/knife/rackspace_server_delete'
|
3
|
+
|
4
|
+
class Chef
|
5
|
+
class Knife
|
6
|
+
class RaxClusterDelete < Knife
|
7
|
+
attr_accessor :headers, :rax_endpoint, :lb_name
|
8
|
+
include Knife::RaxClusterBase
|
9
|
+
banner "knife rax cluster delete (load_balancer_id) [options]"
|
10
|
+
deps do
|
11
|
+
require 'fog'
|
12
|
+
require 'readline'
|
13
|
+
require 'chef/json_compat'
|
14
|
+
require 'chef/knife/bootstrap'
|
15
|
+
Chef::Knife::Bootstrap.load_deps
|
16
|
+
end
|
17
|
+
|
18
|
+
option :lb_region,
|
19
|
+
:short => "-r lb_region",
|
20
|
+
:long => "--load-balancer-region lb_region",
|
21
|
+
:description => "Load balancer region (only supports ORD || DFW)",
|
22
|
+
:proc => Proc.new { |lb_region| Chef::Config[:knife][:lb_region] = lb_region},
|
23
|
+
:default => "ORD"
|
24
|
+
|
25
|
+
def delete_cluster
|
26
|
+
lb_authenticate = authenticate()
|
27
|
+
lb_url = ""
|
28
|
+
puts config[:lb_region]
|
29
|
+
headers = {"x-auth-token" => lb_authenticate['auth_token'], "content-type" => "application/json"}
|
30
|
+
lb_authenticate['lb_urls'].each {|lb|
|
31
|
+
if config[:lb_region].to_s.downcase == lb['region'].to_s.downcase
|
32
|
+
lb_url = lb['publicURL']
|
33
|
+
break
|
34
|
+
end
|
35
|
+
lb_url = lb['publicURL']
|
36
|
+
}
|
37
|
+
@name_args.each {|arg|
|
38
|
+
server_uuids = []
|
39
|
+
lb_url = lb_url + "/loadbalancers/#{arg}"
|
40
|
+
get_uuids = make_web_call("get", lb_url, headers )
|
41
|
+
if get_uuids.code == '404'
|
42
|
+
ui.msg "Make sure you specify the -r flag to specify what region the LB is located"
|
43
|
+
exit(1)
|
44
|
+
end
|
45
|
+
lb_data = JSON.parse(get_uuids.body)
|
46
|
+
lb_data['loadBalancer']['metadata'].each{|meta|
|
47
|
+
server_uuids << {'uuid' => meta['value'], 'server_name' => meta['key'] }
|
48
|
+
}
|
49
|
+
server_uuids.each { |uuid|
|
50
|
+
rs_delete = RackspaceServerDelete.new
|
51
|
+
rs_delete.config[:yes] = 'yes'
|
52
|
+
rs_delete.name_args = [ uuid['uuid'] ]
|
53
|
+
rs_delete.config[:purge] = true
|
54
|
+
rs_delete.config[:chef_node_name] = uuid['server_name']
|
55
|
+
rs_delete.run
|
56
|
+
}
|
57
|
+
delete_lb_call = make_web_call("delete", lb_url, headers)
|
58
|
+
puts "Deleted loadbalancer id #{arg}"
|
59
|
+
|
60
|
+
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def run
|
65
|
+
if @name_args.empty?
|
66
|
+
ui.fatal "Please specify a Load balancer ID to delete"
|
67
|
+
end
|
68
|
+
ui.confirm("Are you sure you want to delete this Load balancer and ALL nodes associated with it?")
|
69
|
+
delete_cluster
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'chef/knife/rax_cluster_base'
|
2
|
+
require 'chef/knife/rackspace_server_delete'
|
3
|
+
|
4
|
+
class Chef
|
5
|
+
class Knife
|
6
|
+
class RaxClusterExpand < Knife
|
7
|
+
attr_accessor :headers, :rax_endpoint, :lb_id
|
8
|
+
include Knife::RaxClusterBase
|
9
|
+
banner "knife rax cluster expand (load_balancer_id) -B template_file.json"
|
10
|
+
deps do
|
11
|
+
require 'fog'
|
12
|
+
require 'readline'
|
13
|
+
require 'chef/json_compat'
|
14
|
+
require 'chef/knife/bootstrap'
|
15
|
+
Chef::Knife::Bootstrap.load_deps
|
16
|
+
end
|
17
|
+
|
18
|
+
option :blue_print,
|
19
|
+
:short => "-B Blue_print_file",
|
20
|
+
:long => "--map blue_print_file",
|
21
|
+
:description => "Path to blue Print json file",
|
22
|
+
:proc => Proc.new { |i| Chef::Config[:knife][:blue_print] = i.to_s }
|
23
|
+
|
24
|
+
option :port,
|
25
|
+
:short => "-lb_port port",
|
26
|
+
:long => "--load-balancer-port port",
|
27
|
+
:description => "Load balancer port",
|
28
|
+
:proc => Proc.new { |port| Chef::Config[:knife][:port] = port},
|
29
|
+
:default => "80"
|
30
|
+
|
31
|
+
option :lb_region,
|
32
|
+
:short => "-r lb_region",
|
33
|
+
:long => "--load-balancer-region lb_region",
|
34
|
+
:description => "Load balancer region (only supports ORD || DFW)",
|
35
|
+
:proc => Proc.new { |lb_region| Chef::Config[:knife][:lb_region] = lb_region},
|
36
|
+
:default => "ORD"
|
37
|
+
|
38
|
+
def expand_cluster
|
39
|
+
rs_cluster = RaxClusterCreate.new
|
40
|
+
rs_cluster.config[:blue_print] = config[:blue_print]
|
41
|
+
rs_cluster.lb_name = @name_args[0]
|
42
|
+
instance_return = rs_cluster.deploy(config[:blue_print],'update_cluster')
|
43
|
+
lb_auth = authenticate()
|
44
|
+
puts lb_auth['auth_token']
|
45
|
+
headers = {"x-auth-token" => lb_auth['auth_token'], "content-type" => "application/json"}
|
46
|
+
lb_url = ""
|
47
|
+
lb_auth['lb_urls'].each {|lb|
|
48
|
+
if config[:lb_region].to_s.downcase == lb['region'].to_s.downcase
|
49
|
+
lb_url = lb['publicURL']
|
50
|
+
break
|
51
|
+
end
|
52
|
+
lb_url = lb['publicURL']
|
53
|
+
}
|
54
|
+
meta_data_request = {
|
55
|
+
"metadata" => []
|
56
|
+
}
|
57
|
+
node_data_request = {
|
58
|
+
"nodes" => []
|
59
|
+
}
|
60
|
+
meta_url = lb_url + "/loadbalancers/#{@lb_id}/metadata"
|
61
|
+
node_url = lb_url + "/loadbalancers/#{@lb_id}/nodes"
|
62
|
+
|
63
|
+
instance_return.each {|inst|
|
64
|
+
node_data_request['nodes'] << {"address" => inst['ip_address'], 'port' =>Chef::Config[:knife][:port] || '80', "condition" => "ENABLED" }
|
65
|
+
meta_data_request['metadata'] << {"key" => inst['server_name'], "value" => inst['uuid']}
|
66
|
+
}
|
67
|
+
meta_request = make_web_call("post", meta_url, headers, meta_data_request.to_json)
|
68
|
+
lb_status = lb_url + "/loadbalancers/#{@lb_id}"
|
69
|
+
lb_stats = make_web_call("get", lb_status, headers)
|
70
|
+
lb_stats = JSON.parse(lb_stats.body)
|
71
|
+
|
72
|
+
while lb_stats['loadBalancer']['status'].to_s.downcase != 'active'
|
73
|
+
sleep(5)
|
74
|
+
lb_stats = make_web_call("get", lb_status, headers)
|
75
|
+
lb_stats = JSON.parse(lb_stats.body)
|
76
|
+
end
|
77
|
+
node_request = make_web_call("post", node_url, headers, node_data_request.to_json)
|
78
|
+
ui.msg "Load balancer id #{@lb_id} has been updated"
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
def run
|
83
|
+
if @name_args.empty?
|
84
|
+
ui.fatal "Please specify Load balancer ID to add nodes too"
|
85
|
+
exit(1)
|
86
|
+
end
|
87
|
+
if !config[:blue_print]
|
88
|
+
ui.fatal "Please specify a blue print file to parse with -B"
|
89
|
+
exit(1)
|
90
|
+
end
|
91
|
+
|
92
|
+
if config[:blue_print]
|
93
|
+
@lb_id = @name_args[0]
|
94
|
+
expand_cluster
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class Chef
|
2
|
+
class Knife
|
3
|
+
class RaxClusterList < Knife
|
4
|
+
attr_accessor :headers, :rax_endpoint, :lb_id
|
5
|
+
include Knife::RaxClusterBase
|
6
|
+
|
7
|
+
banner "knife rax cluster list -r lb_region"
|
8
|
+
deps do
|
9
|
+
require 'fog'
|
10
|
+
require 'readline'
|
11
|
+
require 'chef/json_compat'
|
12
|
+
require 'chef/knife/bootstrap'
|
13
|
+
Chef::Knife::Bootstrap.load_deps
|
14
|
+
end
|
15
|
+
|
16
|
+
option :lb_region,
|
17
|
+
:short => "-r lb_region",
|
18
|
+
:long => "--load-balancer-region lb_region",
|
19
|
+
:description => "Load balancer region (only supports ORD || DFW)",
|
20
|
+
:proc => Proc.new { |lb_region| Chef::Config[:knife][:lb_region] = lb_region},
|
21
|
+
:default => "ORD"
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
def run
|
26
|
+
lb_auth = authenticate
|
27
|
+
headers = {"x-auth-token" => lb_auth['auth_token'], "content-type" => "application/json"}
|
28
|
+
lb_url = ""
|
29
|
+
lb_auth['lb_urls'].each {|lb|
|
30
|
+
if config[:lb_region].to_s.downcase == lb['region'].to_s.downcase
|
31
|
+
lb_url = lb['publicURL']
|
32
|
+
break
|
33
|
+
end
|
34
|
+
lb_url = lb['publicURL']
|
35
|
+
}
|
36
|
+
lb_url = lb_url + "/loadbalancers"
|
37
|
+
lb_list = make_web_call("get", lb_url, headers)
|
38
|
+
lb_list = JSON.parse(lb_list.body)
|
39
|
+
lb_list['loadBalancers'].each {|lb|
|
40
|
+
if (lb['name'] =~ /_cluster/i)
|
41
|
+
msg_pair("LB Details for #{lb['name']}", " ")
|
42
|
+
msg_pair("\s\s\s\sLB ID", "#{lb['id']}")
|
43
|
+
msg_pair("\s\s\s\sLB Port", "#{lb['port']}")
|
44
|
+
msg_pair("\s\s\s\sLB Algorithm", "#{lb['algorithm']}")
|
45
|
+
msg_pair("\s\s\s\sLB Protocol", "#{lb['protocol']}")
|
46
|
+
msg_pair("\s\s\s\sLB Node Count", "#{lb['nodeCount']}")
|
47
|
+
ui.msg "\n\n"
|
48
|
+
end
|
49
|
+
}
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module MyKnifePlugins
|
2
|
+
# Make sure you subclass from Chef::Knife
|
3
|
+
class OverRide < Chef::Knife
|
4
|
+
|
5
|
+
|
6
|
+
banner "Cloud Builder"
|
7
|
+
deps do
|
8
|
+
|
9
|
+
require 'chef/knife/rackspace/rackspace_server_create'
|
10
|
+
require 'json'
|
11
|
+
require 'fog'
|
12
|
+
require "thread"
|
13
|
+
require 'chef/knife/rackspace/rackspace_base'
|
14
|
+
require 'net/ssh/multi'
|
15
|
+
require 'readline'
|
16
|
+
require 'chef/knife/bootstrap'
|
17
|
+
require 'chef/json_compat'
|
18
|
+
Chef::Knife::Bootstrap.load_deps
|
19
|
+
#include Knife::RackspaceBase
|
20
|
+
end
|
21
|
+
option :server_map,
|
22
|
+
:short => "-M MAP_File",
|
23
|
+
:long => "--map Map_File",
|
24
|
+
:description => "Path to Server Map json file",
|
25
|
+
:proc => Proc.new { |i| Chef::Config[:knife][:server_map] = i.to_s }
|
26
|
+
|
27
|
+
option :provider,
|
28
|
+
:short => "-P Provider",
|
29
|
+
:long => "--provider provider",
|
30
|
+
:description => "Specify RAX, open_stack",
|
31
|
+
:default => 'RAX'
|
32
|
+
#:proc => Proc.new { |i| Chef::Config[:knife][:server_map] = i.to_s }
|
33
|
+
|
34
|
+
option :generate_map_template,
|
35
|
+
:short => "-G",
|
36
|
+
:long => "--generate_map_template",
|
37
|
+
:description => "Generate server map Template in current dir named map_template.json"
|
38
|
+
|
39
|
+
#option :image,
|
40
|
+
#:short => "-I IMAGE",
|
41
|
+
#:long => "--image IMAGE",
|
42
|
+
#:description => "The image of the server",
|
43
|
+
#:proc => Proc.new { |i| Chef::Config[:knife][:image] = i.to_s }
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
# This method will be executed when you run this knife command.
|
49
|
+
def generate_map_template
|
50
|
+
file_name = "./map_template.json"
|
51
|
+
template = %q(
|
52
|
+
{
|
53
|
+
"servers" : [
|
54
|
+
{
|
55
|
+
"name_convention" : "web",
|
56
|
+
"run_list" : [
|
57
|
+
"role[base]",
|
58
|
+
"role[narciss]"
|
59
|
+
],
|
60
|
+
"quantity" : 2,
|
61
|
+
"chef_env" : "dev",
|
62
|
+
"image_ref" : "c195ef3b-9195-4474-b6f7-16e5bd86acd0",
|
63
|
+
"flavor" : 2
|
64
|
+
|
65
|
+
}
|
66
|
+
]
|
67
|
+
})
|
68
|
+
File.open(file_name, 'w') { |file| file.write(template)}
|
69
|
+
end
|
70
|
+
#Populates server_calls with map data
|
71
|
+
def parse_server_map(map_file)
|
72
|
+
map_contents = JSON.parse(File.read(map_file))
|
73
|
+
server_calls = {}
|
74
|
+
if map_contents.has_key?("servers")
|
75
|
+
for i in map_contents['servers']
|
76
|
+
server_calls[i['name_convention']] = {
|
77
|
+
"run_list" => i['run_list'] ,
|
78
|
+
"quantity" => i['quantity'], "chef_env" => i['chef_env'],
|
79
|
+
"image_ref" => i['image_ref']
|
80
|
+
}
|
81
|
+
#i['quantity'].times do
|
82
|
+
run_list = i['run_list'].join(', ')
|
83
|
+
#Thread.new {
|
84
|
+
create_server = Chef::Knife::RackspaceServerCreate.new
|
85
|
+
#create_server.config[:image] = i['image_ref']
|
86
|
+
Chef::Config[:knife][:image] = i['image_ref']
|
87
|
+
create_server.config[:server_name] = i['name_convention']
|
88
|
+
create_server.config[:environment] = i['chef_env']
|
89
|
+
create_server.config[:run_list] = i['run_list']
|
90
|
+
#create_server.config[:flavor] = i['flavor']
|
91
|
+
Chef::Config[:knife][:flavor] = i['flavor']
|
92
|
+
create_server.run
|
93
|
+
##}
|
94
|
+
#end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
else
|
99
|
+
ui.fatal "JSON file incorrect format"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def launch_build
|
105
|
+
yield
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
def run
|
110
|
+
#Generate template config
|
111
|
+
if config[:generate_map_template]
|
112
|
+
generate_map_template()
|
113
|
+
end
|
114
|
+
#Parses Map and takes action
|
115
|
+
if config[:server_map]
|
116
|
+
parse_server_map(config[:server_map])
|
117
|
+
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: knife-rackspace-cluster
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.2
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- zack feldstein
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2013-02-19 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: knife-rackspace
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: fog
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - "="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.8.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
description: Creates rax clusters
|
38
|
+
email: zack.feldstein@rackspace.com
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files: []
|
44
|
+
|
45
|
+
files:
|
46
|
+
- lib/chef/knife/rax_cluster_base.rb
|
47
|
+
- lib/chef/knife/rax_cluster_build.rb
|
48
|
+
- lib/chef/knife/rax_cluster_change.rb
|
49
|
+
- lib/chef/knife/rax_cluster_create.rb
|
50
|
+
- lib/chef/knife/rax_cluster_delete.rb
|
51
|
+
- lib/chef/knife/rax_cluster_expand.rb
|
52
|
+
- lib/chef/knife/rax_cluster_list.rb
|
53
|
+
- lib/chef/knife/super_rax.rb
|
54
|
+
homepage: http://github.com/jrcloud/knife_rax_cluster
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.23
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Knife rax cluster
|
81
|
+
test_files: []
|
82
|
+
|