dev-lxc 2.6.1 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +17 -17
- data/CHANGELOG.md +317 -313
- data/Gemfile +4 -4
- data/LICENSE +201 -201
- data/README.md +287 -279
- data/Rakefile +1 -1
- data/bin/dev-lxc +9 -9
- data/dev-lxc.gemspec +26 -26
- data/docs/adhoc_clusters.md +22 -22
- data/docs/base_containers.md +44 -44
- data/docs/configuration.md +203 -203
- data/docs/dev-lxc_version_2.md +10 -10
- data/docs/manage_multiple_clusters.md +30 -30
- data/docs/usage.md +187 -187
- data/lib/dev-lxc/cli.rb +434 -434
- data/lib/dev-lxc/cluster.rb +1211 -1211
- data/lib/dev-lxc/container.rb +123 -123
- data/lib/dev-lxc/server.rb +197 -197
- data/lib/dev-lxc/version.rb +3 -3
- data/lib/dev-lxc.rb +118 -118
- metadata +2 -2
data/lib/dev-lxc/cli.rb
CHANGED
@@ -1,434 +1,434 @@
|
|
1
|
-
require "yaml"
|
2
|
-
require 'dev-lxc'
|
3
|
-
require 'thor'
|
4
|
-
|
5
|
-
module DevLXC::CLI
|
6
|
-
class DevLXC < Thor
|
7
|
-
|
8
|
-
no_commands{
|
9
|
-
def get_cluster(config_file=nil)
|
10
|
-
config_file ||= "dev-lxc.yml"
|
11
|
-
if ! File.exists?(config_file)
|
12
|
-
puts "ERROR: Cluster config file '#{config_file}' does not exist."
|
13
|
-
puts " Create a `./dev-lxc.yml` file or specify the path using `--config`."
|
14
|
-
exit 1
|
15
|
-
end
|
16
|
-
cluster_config = YAML.load(IO.read(config_file))
|
17
|
-
::DevLXC::Cluster.new(cluster_config)
|
18
|
-
end
|
19
|
-
|
20
|
-
def print_elapsed_time(elapsed_time)
|
21
|
-
printf "dev-lxc is finished. (%im %.2fs)\n", elapsed_time / 60, elapsed_time % 60
|
22
|
-
end
|
23
|
-
}
|
24
|
-
|
25
|
-
desc "show-config", "Show calculated configuration"
|
26
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
27
|
-
option :include_products, :type => :boolean, :desc => "Calculate required products"
|
28
|
-
def show_config
|
29
|
-
get_cluster(options[:config]).show_config(options[:include_products])
|
30
|
-
end
|
31
|
-
|
32
|
-
desc "create-base-container [BASE_CONTAINER_NAME]", "Create a base container"
|
33
|
-
option :options, :aliases => "-o", :desc => "Specify additional options for the lxc create"
|
34
|
-
def create_base_container(base_container_name=nil)
|
35
|
-
start_time = Time.now
|
36
|
-
base_container_names = %w(b-ubuntu-1204 b-ubuntu-1404 b-ubuntu-1604 b-centos-5 b-centos-6 b-centos-7)
|
37
|
-
if base_container_name.nil? || ! base_container_names.include?(base_container_name)
|
38
|
-
base_container_names_with_index = base_container_names.map.with_index{ |a, i| [i+1, *a]}
|
39
|
-
print_table base_container_names_with_index
|
40
|
-
selection = ask("Which base container do you want to create?", :limited_to => base_container_names_with_index.map{|c| c[0].to_s})
|
41
|
-
base_container_name = base_container_names[selection.to_i - 1]
|
42
|
-
end
|
43
|
-
::DevLXC.create_base_container(base_container_name, options[:options])
|
44
|
-
puts
|
45
|
-
print_elapsed_time(Time.now - start_time)
|
46
|
-
end
|
47
|
-
|
48
|
-
desc "init", "Provide a cluster config file"
|
49
|
-
option :chef, :type => :boolean, :desc => "Standalone Chef Server"
|
50
|
-
option :chef_tier, :type => :boolean, :desc => "Chef Server using Tier topology with one backend"
|
51
|
-
option :chef_backend, :type => :boolean, :desc => "Chef Server using Chef Backend HA topology with three backends"
|
52
|
-
option :nodes, :type => :boolean, :desc => "Node Servers"
|
53
|
-
option :analytics, :type => :boolean, :desc => "Analytics Server"
|
54
|
-
option :compliance, :type => :boolean, :desc => "Compliance Server"
|
55
|
-
option :supermarket, :type => :boolean, :desc => "Supermarket Server"
|
56
|
-
option :automate, :type => :boolean, :desc => "Automate Server"
|
57
|
-
option :build_nodes, :type => :boolean, :desc => "Build Nodes"
|
58
|
-
option :runners, :type => :boolean, :desc => "Runners"
|
59
|
-
option :adhoc, :type => :boolean, :desc => "Adhoc Servers"
|
60
|
-
option :append, :aliases => "-a", :type => :boolean, :desc => "Do not generate the global config header"
|
61
|
-
option :filename, :aliases => "-f", :desc => "Write generated content to FILE rather than standard output."
|
62
|
-
def init
|
63
|
-
header = %Q(# enable_build_snapshots automatically makes container snapshots at key times during the build process
|
64
|
-
# default value is `true`
|
65
|
-
#enable_build_snapshots: true
|
66
|
-
|
67
|
-
# base_container must be the name of an existing container
|
68
|
-
base_container: b-ubuntu-1404
|
69
|
-
|
70
|
-
# memory_per_server sets the maximum amount of user memory (including file cache) for each server.
|
71
|
-
# dev-lxc will set the `memory.limit_in_bytes` cgroup for each server to apply this limit.
|
72
|
-
# If no units are specified, the value is interpreted as bytes.
|
73
|
-
# You can use suffixes to represent larger units
|
74
|
-
# The default behavior is that no limit is set.
|
75
|
-
#memory_per_server: 4G
|
76
|
-
|
77
|
-
# list any host directories you want mounted into the servers
|
78
|
-
#mounts:
|
79
|
-
# - /root/work root/work
|
80
|
-
|
81
|
-
# list any SSH public keys you want added to /home/dev-lxc/.ssh/authorized_keys
|
82
|
-
#ssh-keys:
|
83
|
-
# - /root/work/clusters/id_rsa.pub
|
84
|
-
|
85
|
-
# DHCP reserved (static) IPs must be selected from the IP range 10.0.3.150 - 254
|
86
|
-
)
|
87
|
-
chef_tier_config = %Q(
|
88
|
-
chef-server:
|
89
|
-
topology: tier
|
90
|
-
api_fqdn: chef-tier.lxc
|
91
|
-
users: # a user's password will be the same as its username
|
92
|
-
- mary-admin
|
93
|
-
- joe-user
|
94
|
-
orgs:
|
95
|
-
demo:
|
96
|
-
admins:
|
97
|
-
- mary-admin
|
98
|
-
non-admins:
|
99
|
-
- joe-user
|
100
|
-
servers:
|
101
|
-
chef-be.lxc:
|
102
|
-
ipaddress: 10.0.3.201
|
103
|
-
role: backend
|
104
|
-
bootstrap: true
|
105
|
-
products:
|
106
|
-
chef-server:
|
107
|
-
push-jobs-server:
|
108
|
-
reporting:
|
109
|
-
chef-fe1.lxc:
|
110
|
-
ipaddress: 10.0.3.202
|
111
|
-
role: frontend
|
112
|
-
products:
|
113
|
-
chef-server:
|
114
|
-
manage:
|
115
|
-
push-jobs-server:
|
116
|
-
reporting:
|
117
|
-
)
|
118
|
-
chef_config = %Q(
|
119
|
-
chef-server:
|
120
|
-
users: # a user's password will be the same as its username
|
121
|
-
- mary-admin
|
122
|
-
- joe-user
|
123
|
-
orgs:
|
124
|
-
demo:
|
125
|
-
admins:
|
126
|
-
- mary-admin
|
127
|
-
non-admins:
|
128
|
-
- joe-user
|
129
|
-
servers:
|
130
|
-
chef.lxc:
|
131
|
-
ipaddress: 10.0.3.203
|
132
|
-
products:
|
133
|
-
chef-server:
|
134
|
-
manage:
|
135
|
-
push-jobs-server:
|
136
|
-
reporting:
|
137
|
-
)
|
138
|
-
automate_config = %Q(
|
139
|
-
automate:
|
140
|
-
servers:
|
141
|
-
automate.lxc:
|
142
|
-
ipaddress: 10.0.3.200
|
143
|
-
products:
|
144
|
-
delivery:
|
145
|
-
license_path: /path/for/automate.license
|
146
|
-
chef_org: delivery
|
147
|
-
enterprise_name: demo-ent
|
148
|
-
)
|
149
|
-
build_nodes_config = %Q(
|
150
|
-
build-nodes:
|
151
|
-
servers:
|
152
|
-
build-node-1.lxc:
|
153
|
-
products:
|
154
|
-
chefdk: # downloaded only
|
155
|
-
)
|
156
|
-
runners_config = %Q(
|
157
|
-
runners:
|
158
|
-
servers:
|
159
|
-
runner-1.lxc:
|
160
|
-
products:
|
161
|
-
chefdk: # downloaded only
|
162
|
-
)
|
163
|
-
analytics_config = %Q(
|
164
|
-
analytics:
|
165
|
-
servers:
|
166
|
-
analytics.lxc:
|
167
|
-
ipaddress: 10.0.3.204
|
168
|
-
products:
|
169
|
-
analytics:
|
170
|
-
)
|
171
|
-
compliance_config = %Q(
|
172
|
-
compliance:
|
173
|
-
admin_user: admin # the password will be the same as the username
|
174
|
-
servers:
|
175
|
-
compliance.lxc:
|
176
|
-
ipaddress: 10.0.3.205
|
177
|
-
products:
|
178
|
-
compliance:
|
179
|
-
)
|
180
|
-
supermarket_config = %Q(
|
181
|
-
supermarket:
|
182
|
-
servers:
|
183
|
-
supermarket.lxc:
|
184
|
-
ipaddress: 10.0.3.206
|
185
|
-
products:
|
186
|
-
supermarket:
|
187
|
-
)
|
188
|
-
adhoc_config = %Q(
|
189
|
-
adhoc:
|
190
|
-
servers:
|
191
|
-
adhoc.lxc:
|
192
|
-
ipaddress: 10.0.3.207
|
193
|
-
)
|
194
|
-
chef_backend_config = %Q(
|
195
|
-
chef-backend:
|
196
|
-
api_fqdn: chef-ha.lxc
|
197
|
-
users: # a user's password will be the same as its username
|
198
|
-
- mary-admin
|
199
|
-
- joe-user
|
200
|
-
orgs:
|
201
|
-
demo:
|
202
|
-
admins:
|
203
|
-
- mary-admin
|
204
|
-
non-admins:
|
205
|
-
- joe-user
|
206
|
-
servers:
|
207
|
-
chef-backend1.lxc:
|
208
|
-
ipaddress: 10.0.3.208
|
209
|
-
role: backend
|
210
|
-
leader: true
|
211
|
-
products:
|
212
|
-
chef-backend:
|
213
|
-
chef-backend2.lxc:
|
214
|
-
ipaddress: 10.0.3.209
|
215
|
-
role: backend
|
216
|
-
products:
|
217
|
-
chef-backend:
|
218
|
-
chef-backend3.lxc:
|
219
|
-
ipaddress: 10.0.3.210
|
220
|
-
role: backend
|
221
|
-
products:
|
222
|
-
chef-backend:
|
223
|
-
chef-frontend1.lxc:
|
224
|
-
ipaddress: 10.0.3.211
|
225
|
-
role: frontend
|
226
|
-
bootstrap: true
|
227
|
-
products:
|
228
|
-
chef-server:
|
229
|
-
manage:
|
230
|
-
)
|
231
|
-
nodes_config = %Q(
|
232
|
-
nodes:
|
233
|
-
chef_server_url: https://chef.lxc/organizations/demo
|
234
|
-
validation_client_name: demo-validator
|
235
|
-
# comment out or remove the validation_key path to use chef-server keys generated by dev-lxc
|
236
|
-
validation_key: # /path/for/ORG-validator.pem
|
237
|
-
servers:
|
238
|
-
node-1.lxc:
|
239
|
-
products:
|
240
|
-
chef:
|
241
|
-
)
|
242
|
-
config = ""
|
243
|
-
config += header unless options[:append]
|
244
|
-
config += chef_config if options[:chef]
|
245
|
-
config += chef_tier_config if options[:chef_tier]
|
246
|
-
config += analytics_config if options[:analytics]
|
247
|
-
config += compliance_config if options[:compliance]
|
248
|
-
config += supermarket_config if options[:supermarket]
|
249
|
-
config += automate_config if options[:automate]
|
250
|
-
config += build_nodes_config if options[:build_nodes]
|
251
|
-
config += runners_config if options[:runners]
|
252
|
-
config += adhoc_config if options[:adhoc]
|
253
|
-
config += chef_backend_config if options[:chef_backend]
|
254
|
-
config += nodes_config if options[:nodes]
|
255
|
-
if options[:filename]
|
256
|
-
mode = options[:append] ? 'a' : 'w'
|
257
|
-
IO.write(options[:filename], config, mode: mode)
|
258
|
-
else
|
259
|
-
puts config
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
desc "status [SERVER_NAME_REGEX]", "Show status of servers"
|
264
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
265
|
-
def status(server_name_regex=nil)
|
266
|
-
cluster = get_cluster(options[:config])
|
267
|
-
if cluster.config['chef-server'][:topology] == "tier" && cluster.config['chef-server'][:fqdn]
|
268
|
-
printf "Chef Server FQDN: %s\n\n", cluster.config['chef-server'][:fqdn]
|
269
|
-
end
|
270
|
-
if cluster.config['chef-backend'][:fqdn]
|
271
|
-
printf "Chef Server FQDN: %s\n\n", cluster.config['chef-backend'][:fqdn]
|
272
|
-
end
|
273
|
-
if cluster.config['analytics'][:topology] == "tier" && cluster.config['analytics'][:fqdn]
|
274
|
-
printf "Analytics FQDN: %s\n\n", cluster.config['analytics'][:fqdn]
|
275
|
-
end
|
276
|
-
servers = Array.new
|
277
|
-
cluster.get_sorted_servers(server_name_regex).map { |s| servers << s.status }
|
278
|
-
max_server_name_length = servers.max_by { |s| s['name'].length }['name'].length unless servers.empty?
|
279
|
-
servers.each_with_index do |s, server_index|
|
280
|
-
printf "%-#{max_server_name_length}s %-15s %s\n", s['name'], s['state'].upcase, s['ip_addresses']
|
281
|
-
server = cluster.get_server(s['name'])
|
282
|
-
server.snapshot_list.each do |snapname, snaptime, snap_comment|
|
283
|
-
printf " |_ %s %s %s\n", snapname, snaptime, snap_comment
|
284
|
-
end
|
285
|
-
puts if server_index + 1 < servers.length
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
desc "attach [SERVER_NAME_REGEX]", "Attach the terminal to a single server"
|
290
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
291
|
-
def attach(server_name_regex)
|
292
|
-
servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
|
293
|
-
if servers.length > 1
|
294
|
-
puts "ERROR: The following servers matched '#{server_name_regex}'"
|
295
|
-
servers.map { |s| puts " #{s.name}" }
|
296
|
-
puts " Please specify a single server to attach to"
|
297
|
-
exit 1
|
298
|
-
elsif servers.empty?
|
299
|
-
puts "ERROR: No servers matched '#{server_name_regex}'"
|
300
|
-
puts " Please specify a single server to attach to"
|
301
|
-
exit 1
|
302
|
-
end
|
303
|
-
container = servers.first.container
|
304
|
-
if !container.defined? || !container.running?
|
305
|
-
puts "ERROR: Server '#{container.name}' is not running"
|
306
|
-
exit 1
|
307
|
-
end
|
308
|
-
attach_opts = {
|
309
|
-
wait: true,
|
310
|
-
env_policy: LXC::LXC_ATTACH_CLEAR_ENV,
|
311
|
-
extra_env_vars: ["LANG=en_US.UTF-8", "TERM=linux", "HOME=#{ENV['HOME']}"]
|
312
|
-
}
|
313
|
-
shell = ENV['SHELL']
|
314
|
-
container.attach(attach_opts) { system(shell) }
|
315
|
-
end
|
316
|
-
|
317
|
-
desc "chef-repo", "Creates a chef-repo in the current directory using files from the cluster's backend /root/chef-repo"
|
318
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
319
|
-
option :force, :aliases => "-f", :type => :boolean, :desc => "Overwrite any existing knife.rb or pivotal.rb files"
|
320
|
-
option :pivotal, :aliases => "-p", :type => :boolean, :desc => "Also copy pivotal.rb and pivotal.pem"
|
321
|
-
def chef_repo
|
322
|
-
get_cluster(options[:config]).chef_repo(options[:force], options[:pivotal])
|
323
|
-
end
|
324
|
-
|
325
|
-
desc "print-automate-credentials", "Print Automate credentials"
|
326
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
327
|
-
def print_automate_credentials
|
328
|
-
get_cluster(options[:config]).print_automate_credentials
|
329
|
-
end
|
330
|
-
|
331
|
-
desc "run-command [SERVER_NAME_REGEX] [COMMAND]", "Runs a command in each server"
|
332
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
333
|
-
def run_command(server_name_regex=nil, command)
|
334
|
-
start_time = Time.now
|
335
|
-
get_cluster(options[:config]).get_sorted_servers(server_name_regex).each { |s| s.run_command(command); puts }
|
336
|
-
print_elapsed_time(Time.now - start_time)
|
337
|
-
end
|
338
|
-
|
339
|
-
desc "prepare-product-cache [SERVER_NAME_REGEX]", "Download required product packages to cache"
|
340
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
341
|
-
def prepare_product_cache(server_name_regex=nil)
|
342
|
-
start_time = Time.now
|
343
|
-
cluster = get_cluster(options[:config])
|
344
|
-
servers = cluster.get_sorted_servers(server_name_regex)
|
345
|
-
cluster.prep_product_cache(servers, true)
|
346
|
-
print_elapsed_time(Time.now - start_time)
|
347
|
-
end
|
348
|
-
|
349
|
-
desc "up [SERVER_NAME_REGEX]", "Start servers - This is the default if no subcommand is given"
|
350
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
351
|
-
def up(server_name_regex=nil)
|
352
|
-
start_time = Time.now
|
353
|
-
get_cluster(options[:config]).up(server_name_regex)
|
354
|
-
print_elapsed_time(Time.now - start_time)
|
355
|
-
end
|
356
|
-
|
357
|
-
desc "halt [SERVER_NAME_REGEX]", "Shutdown servers"
|
358
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
359
|
-
def halt(server_name_regex=nil)
|
360
|
-
start_time = Time.now
|
361
|
-
get_cluster(options[:config]).halt(server_name_regex)
|
362
|
-
print_elapsed_time(Time.now - start_time)
|
363
|
-
end
|
364
|
-
|
365
|
-
desc "snapshot [SERVER_NAME_REGEX]", "Manage a cluster's snapshots"
|
366
|
-
option :comment, :aliases => "-c", :desc => "Add snapshot comment"
|
367
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
368
|
-
option :destroy, :aliases => "-d", :desc => "Destroy snapshot - use ALL to destroy all snapshots"
|
369
|
-
option :list, :aliases => "-l", :type => :boolean, :desc => "List snapshots"
|
370
|
-
option :restore, :aliases => "-r", :desc => "Restore snapshots"
|
371
|
-
def snapshot(server_name_regex=nil)
|
372
|
-
start_time = Time.now
|
373
|
-
servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
|
374
|
-
if options[:list]
|
375
|
-
servers.each_with_index do |s, server_index|
|
376
|
-
puts s.name
|
377
|
-
s.snapshot_list.each do |snapname, snaptime, snap_comment|
|
378
|
-
printf " |_ %s %s %s\n", snapname, snaptime, snap_comment
|
379
|
-
end
|
380
|
-
puts if server_index + 1 < servers.length
|
381
|
-
end
|
382
|
-
return
|
383
|
-
elsif options[:destroy]
|
384
|
-
snapname = options[:destroy] == 'destroy' ? "LAST" : options[:destroy]
|
385
|
-
servers.each { |s| s.snapshot_destroy(snapname); puts }
|
386
|
-
elsif options[:restore]
|
387
|
-
running_servers = Array.new
|
388
|
-
servers.each do |s|
|
389
|
-
running_servers << s.name if s.container.running?
|
390
|
-
end
|
391
|
-
unless running_servers.empty?
|
392
|
-
puts "ERROR: Aborting snapshot restore because the following servers are running"
|
393
|
-
puts running_servers
|
394
|
-
exit 1
|
395
|
-
end
|
396
|
-
snapname = options[:restore] == 'restore' ? "LAST" : options[:restore]
|
397
|
-
servers.each { |s| s.snapshot_restore(snapname); puts }
|
398
|
-
else
|
399
|
-
running_servers = Array.new
|
400
|
-
servers.each do |s|
|
401
|
-
running_servers << s.name if s.container.running?
|
402
|
-
end
|
403
|
-
unless running_servers.empty?
|
404
|
-
puts "ERROR: Aborting snapshot because the following servers are running"
|
405
|
-
puts running_servers
|
406
|
-
exit 1
|
407
|
-
end
|
408
|
-
servers.each { |s| s.snapshot(options[:comment]); puts }
|
409
|
-
end
|
410
|
-
print_elapsed_time(Time.now - start_time)
|
411
|
-
end
|
412
|
-
|
413
|
-
desc "destroy [SERVER_NAME_REGEX]", "Destroy servers"
|
414
|
-
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
415
|
-
option :force, :aliases => "-f", :type => :boolean, :desc => "Destroy servers without confirmation"
|
416
|
-
def destroy(server_name_regex=nil)
|
417
|
-
servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
|
418
|
-
if servers.empty?
|
419
|
-
puts "No matching server names were found"
|
420
|
-
exit
|
421
|
-
end
|
422
|
-
unless options[:force]
|
423
|
-
confirmation_string = String.new
|
424
|
-
servers.reverse_each { |s| confirmation_string += "#{s.name}\n" }
|
425
|
-
confirmation_string += "Are you sure you want to destroy these servers? (y/N)\n"
|
426
|
-
return unless yes?(confirmation_string)
|
427
|
-
end
|
428
|
-
start_time = Time.now
|
429
|
-
get_cluster(options[:config]).destroy(server_name_regex)
|
430
|
-
print_elapsed_time(Time.now - start_time)
|
431
|
-
end
|
432
|
-
|
433
|
-
end
|
434
|
-
end
|
1
|
+
require "yaml"
|
2
|
+
require 'dev-lxc'
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module DevLXC::CLI
|
6
|
+
class DevLXC < Thor
|
7
|
+
|
8
|
+
no_commands{
|
9
|
+
def get_cluster(config_file=nil)
|
10
|
+
config_file ||= "dev-lxc.yml"
|
11
|
+
if ! File.exists?(config_file)
|
12
|
+
puts "ERROR: Cluster config file '#{config_file}' does not exist."
|
13
|
+
puts " Create a `./dev-lxc.yml` file or specify the path using `--config`."
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
cluster_config = YAML.load(IO.read(config_file))
|
17
|
+
::DevLXC::Cluster.new(cluster_config)
|
18
|
+
end
|
19
|
+
|
20
|
+
def print_elapsed_time(elapsed_time)
|
21
|
+
printf "dev-lxc is finished. (%im %.2fs)\n", elapsed_time / 60, elapsed_time % 60
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
desc "show-config", "Show calculated configuration"
|
26
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
27
|
+
option :include_products, :type => :boolean, :desc => "Calculate required products"
|
28
|
+
def show_config
|
29
|
+
get_cluster(options[:config]).show_config(options[:include_products])
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "create-base-container [BASE_CONTAINER_NAME]", "Create a base container"
|
33
|
+
option :options, :aliases => "-o", :desc => "Specify additional options for the lxc create"
|
34
|
+
def create_base_container(base_container_name=nil)
|
35
|
+
start_time = Time.now
|
36
|
+
base_container_names = %w(b-ubuntu-1204 b-ubuntu-1404 b-ubuntu-1604 b-centos-5 b-centos-6 b-centos-7)
|
37
|
+
if base_container_name.nil? || ! base_container_names.include?(base_container_name)
|
38
|
+
base_container_names_with_index = base_container_names.map.with_index{ |a, i| [i+1, *a]}
|
39
|
+
print_table base_container_names_with_index
|
40
|
+
selection = ask("Which base container do you want to create?", :limited_to => base_container_names_with_index.map{|c| c[0].to_s})
|
41
|
+
base_container_name = base_container_names[selection.to_i - 1]
|
42
|
+
end
|
43
|
+
::DevLXC.create_base_container(base_container_name, options[:options])
|
44
|
+
puts
|
45
|
+
print_elapsed_time(Time.now - start_time)
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "init", "Provide a cluster config file"
|
49
|
+
option :chef, :type => :boolean, :desc => "Standalone Chef Server"
|
50
|
+
option :chef_tier, :type => :boolean, :desc => "Chef Server using Tier topology with one backend"
|
51
|
+
option :chef_backend, :type => :boolean, :desc => "Chef Server using Chef Backend HA topology with three backends"
|
52
|
+
option :nodes, :type => :boolean, :desc => "Node Servers"
|
53
|
+
option :analytics, :type => :boolean, :desc => "Analytics Server"
|
54
|
+
option :compliance, :type => :boolean, :desc => "Compliance Server"
|
55
|
+
option :supermarket, :type => :boolean, :desc => "Supermarket Server"
|
56
|
+
option :automate, :type => :boolean, :desc => "Automate Server"
|
57
|
+
option :build_nodes, :type => :boolean, :desc => "Build Nodes"
|
58
|
+
option :runners, :type => :boolean, :desc => "Runners"
|
59
|
+
option :adhoc, :type => :boolean, :desc => "Adhoc Servers"
|
60
|
+
option :append, :aliases => "-a", :type => :boolean, :desc => "Do not generate the global config header"
|
61
|
+
option :filename, :aliases => "-f", :desc => "Write generated content to FILE rather than standard output."
|
62
|
+
def init
|
63
|
+
header = %Q(# enable_build_snapshots automatically makes container snapshots at key times during the build process
|
64
|
+
# default value is `true`
|
65
|
+
#enable_build_snapshots: true
|
66
|
+
|
67
|
+
# base_container must be the name of an existing container
|
68
|
+
base_container: b-ubuntu-1404
|
69
|
+
|
70
|
+
# memory_per_server sets the maximum amount of user memory (including file cache) for each server.
|
71
|
+
# dev-lxc will set the `memory.limit_in_bytes` cgroup for each server to apply this limit.
|
72
|
+
# If no units are specified, the value is interpreted as bytes.
|
73
|
+
# You can use suffixes to represent larger units - k or K for kilobytes, m or M for megabytes, and g or G for gigabytes.
|
74
|
+
# The default behavior is that no limit is set.
|
75
|
+
#memory_per_server: 4G
|
76
|
+
|
77
|
+
# list any host directories you want mounted into the servers
|
78
|
+
#mounts:
|
79
|
+
# - /root/work root/work
|
80
|
+
|
81
|
+
# list any SSH public keys you want added to /home/dev-lxc/.ssh/authorized_keys
|
82
|
+
#ssh-keys:
|
83
|
+
# - /root/work/clusters/id_rsa.pub
|
84
|
+
|
85
|
+
# DHCP reserved (static) IPs must be selected from the IP range 10.0.3.150 - 254
|
86
|
+
)
|
87
|
+
chef_tier_config = %Q(
|
88
|
+
chef-server:
|
89
|
+
topology: tier
|
90
|
+
api_fqdn: chef-tier.lxc
|
91
|
+
users: # a user's password will be the same as its username
|
92
|
+
- mary-admin
|
93
|
+
- joe-user
|
94
|
+
orgs:
|
95
|
+
demo:
|
96
|
+
admins:
|
97
|
+
- mary-admin
|
98
|
+
non-admins:
|
99
|
+
- joe-user
|
100
|
+
servers:
|
101
|
+
chef-be.lxc:
|
102
|
+
ipaddress: 10.0.3.201
|
103
|
+
role: backend
|
104
|
+
bootstrap: true
|
105
|
+
products:
|
106
|
+
chef-server:
|
107
|
+
push-jobs-server:
|
108
|
+
reporting:
|
109
|
+
chef-fe1.lxc:
|
110
|
+
ipaddress: 10.0.3.202
|
111
|
+
role: frontend
|
112
|
+
products:
|
113
|
+
chef-server:
|
114
|
+
manage:
|
115
|
+
push-jobs-server:
|
116
|
+
reporting:
|
117
|
+
)
|
118
|
+
chef_config = %Q(
|
119
|
+
chef-server:
|
120
|
+
users: # a user's password will be the same as its username
|
121
|
+
- mary-admin
|
122
|
+
- joe-user
|
123
|
+
orgs:
|
124
|
+
demo:
|
125
|
+
admins:
|
126
|
+
- mary-admin
|
127
|
+
non-admins:
|
128
|
+
- joe-user
|
129
|
+
servers:
|
130
|
+
chef.lxc:
|
131
|
+
ipaddress: 10.0.3.203
|
132
|
+
products:
|
133
|
+
chef-server:
|
134
|
+
manage:
|
135
|
+
push-jobs-server:
|
136
|
+
reporting:
|
137
|
+
)
|
138
|
+
automate_config = %Q(
|
139
|
+
automate:
|
140
|
+
servers:
|
141
|
+
automate.lxc:
|
142
|
+
ipaddress: 10.0.3.200
|
143
|
+
products:
|
144
|
+
delivery:
|
145
|
+
license_path: /path/for/automate.license
|
146
|
+
chef_org: delivery
|
147
|
+
enterprise_name: demo-ent
|
148
|
+
)
|
149
|
+
build_nodes_config = %Q(
|
150
|
+
build-nodes:
|
151
|
+
servers:
|
152
|
+
build-node-1.lxc:
|
153
|
+
products:
|
154
|
+
chefdk: # downloaded only
|
155
|
+
)
|
156
|
+
runners_config = %Q(
|
157
|
+
runners:
|
158
|
+
servers:
|
159
|
+
runner-1.lxc:
|
160
|
+
products:
|
161
|
+
chefdk: # downloaded only
|
162
|
+
)
|
163
|
+
analytics_config = %Q(
|
164
|
+
analytics:
|
165
|
+
servers:
|
166
|
+
analytics.lxc:
|
167
|
+
ipaddress: 10.0.3.204
|
168
|
+
products:
|
169
|
+
analytics:
|
170
|
+
)
|
171
|
+
compliance_config = %Q(
|
172
|
+
compliance:
|
173
|
+
admin_user: admin # the password will be the same as the username
|
174
|
+
servers:
|
175
|
+
compliance.lxc:
|
176
|
+
ipaddress: 10.0.3.205
|
177
|
+
products:
|
178
|
+
compliance:
|
179
|
+
)
|
180
|
+
supermarket_config = %Q(
|
181
|
+
supermarket:
|
182
|
+
servers:
|
183
|
+
supermarket.lxc:
|
184
|
+
ipaddress: 10.0.3.206
|
185
|
+
products:
|
186
|
+
supermarket:
|
187
|
+
)
|
188
|
+
adhoc_config = %Q(
|
189
|
+
adhoc:
|
190
|
+
servers:
|
191
|
+
adhoc.lxc:
|
192
|
+
ipaddress: 10.0.3.207
|
193
|
+
)
|
194
|
+
chef_backend_config = %Q(
|
195
|
+
chef-backend:
|
196
|
+
api_fqdn: chef-ha.lxc
|
197
|
+
users: # a user's password will be the same as its username
|
198
|
+
- mary-admin
|
199
|
+
- joe-user
|
200
|
+
orgs:
|
201
|
+
demo:
|
202
|
+
admins:
|
203
|
+
- mary-admin
|
204
|
+
non-admins:
|
205
|
+
- joe-user
|
206
|
+
servers:
|
207
|
+
chef-backend1.lxc:
|
208
|
+
ipaddress: 10.0.3.208
|
209
|
+
role: backend
|
210
|
+
leader: true
|
211
|
+
products:
|
212
|
+
chef-backend:
|
213
|
+
chef-backend2.lxc:
|
214
|
+
ipaddress: 10.0.3.209
|
215
|
+
role: backend
|
216
|
+
products:
|
217
|
+
chef-backend:
|
218
|
+
chef-backend3.lxc:
|
219
|
+
ipaddress: 10.0.3.210
|
220
|
+
role: backend
|
221
|
+
products:
|
222
|
+
chef-backend:
|
223
|
+
chef-frontend1.lxc:
|
224
|
+
ipaddress: 10.0.3.211
|
225
|
+
role: frontend
|
226
|
+
bootstrap: true
|
227
|
+
products:
|
228
|
+
chef-server:
|
229
|
+
manage:
|
230
|
+
)
|
231
|
+
nodes_config = %Q(
|
232
|
+
nodes:
|
233
|
+
chef_server_url: https://chef.lxc/organizations/demo
|
234
|
+
validation_client_name: demo-validator
|
235
|
+
# comment out or remove the validation_key path to use chef-server keys generated by dev-lxc
|
236
|
+
validation_key: # /path/for/ORG-validator.pem
|
237
|
+
servers:
|
238
|
+
node-1.lxc:
|
239
|
+
products:
|
240
|
+
chef:
|
241
|
+
)
|
242
|
+
config = ""
|
243
|
+
config += header unless options[:append]
|
244
|
+
config += chef_config if options[:chef]
|
245
|
+
config += chef_tier_config if options[:chef_tier]
|
246
|
+
config += analytics_config if options[:analytics]
|
247
|
+
config += compliance_config if options[:compliance]
|
248
|
+
config += supermarket_config if options[:supermarket]
|
249
|
+
config += automate_config if options[:automate]
|
250
|
+
config += build_nodes_config if options[:build_nodes]
|
251
|
+
config += runners_config if options[:runners]
|
252
|
+
config += adhoc_config if options[:adhoc]
|
253
|
+
config += chef_backend_config if options[:chef_backend]
|
254
|
+
config += nodes_config if options[:nodes]
|
255
|
+
if options[:filename]
|
256
|
+
mode = options[:append] ? 'a' : 'w'
|
257
|
+
IO.write(options[:filename], config, mode: mode)
|
258
|
+
else
|
259
|
+
puts config
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
desc "status [SERVER_NAME_REGEX]", "Show status of servers"
|
264
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
265
|
+
def status(server_name_regex=nil)
|
266
|
+
cluster = get_cluster(options[:config])
|
267
|
+
if cluster.config['chef-server'][:topology] == "tier" && cluster.config['chef-server'][:fqdn]
|
268
|
+
printf "Chef Server FQDN: %s\n\n", cluster.config['chef-server'][:fqdn]
|
269
|
+
end
|
270
|
+
if cluster.config['chef-backend'][:fqdn]
|
271
|
+
printf "Chef Server FQDN: %s\n\n", cluster.config['chef-backend'][:fqdn]
|
272
|
+
end
|
273
|
+
if cluster.config['analytics'][:topology] == "tier" && cluster.config['analytics'][:fqdn]
|
274
|
+
printf "Analytics FQDN: %s\n\n", cluster.config['analytics'][:fqdn]
|
275
|
+
end
|
276
|
+
servers = Array.new
|
277
|
+
cluster.get_sorted_servers(server_name_regex).map { |s| servers << s.status }
|
278
|
+
max_server_name_length = servers.max_by { |s| s['name'].length }['name'].length unless servers.empty?
|
279
|
+
servers.each_with_index do |s, server_index|
|
280
|
+
printf "%-#{max_server_name_length}s %-15s %s\n", s['name'], s['state'].upcase, s['ip_addresses']
|
281
|
+
server = cluster.get_server(s['name'])
|
282
|
+
server.snapshot_list.each do |snapname, snaptime, snap_comment|
|
283
|
+
printf " |_ %s %s %s\n", snapname, snaptime, snap_comment
|
284
|
+
end
|
285
|
+
puts if server_index + 1 < servers.length
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
desc "attach [SERVER_NAME_REGEX]", "Attach the terminal to a single server"
|
290
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
291
|
+
def attach(server_name_regex)
|
292
|
+
servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
|
293
|
+
if servers.length > 1
|
294
|
+
puts "ERROR: The following servers matched '#{server_name_regex}'"
|
295
|
+
servers.map { |s| puts " #{s.name}" }
|
296
|
+
puts " Please specify a single server to attach to"
|
297
|
+
exit 1
|
298
|
+
elsif servers.empty?
|
299
|
+
puts "ERROR: No servers matched '#{server_name_regex}'"
|
300
|
+
puts " Please specify a single server to attach to"
|
301
|
+
exit 1
|
302
|
+
end
|
303
|
+
container = servers.first.container
|
304
|
+
if !container.defined? || !container.running?
|
305
|
+
puts "ERROR: Server '#{container.name}' is not running"
|
306
|
+
exit 1
|
307
|
+
end
|
308
|
+
attach_opts = {
|
309
|
+
wait: true,
|
310
|
+
env_policy: LXC::LXC_ATTACH_CLEAR_ENV,
|
311
|
+
extra_env_vars: ["LANG=en_US.UTF-8", "TERM=linux", "HOME=#{ENV['HOME']}"]
|
312
|
+
}
|
313
|
+
shell = ENV['SHELL']
|
314
|
+
container.attach(attach_opts) { system(shell) }
|
315
|
+
end
|
316
|
+
|
317
|
+
desc "chef-repo", "Creates a chef-repo in the current directory using files from the cluster's backend /root/chef-repo"
|
318
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
319
|
+
option :force, :aliases => "-f", :type => :boolean, :desc => "Overwrite any existing knife.rb or pivotal.rb files"
|
320
|
+
option :pivotal, :aliases => "-p", :type => :boolean, :desc => "Also copy pivotal.rb and pivotal.pem"
|
321
|
+
def chef_repo
|
322
|
+
get_cluster(options[:config]).chef_repo(options[:force], options[:pivotal])
|
323
|
+
end
|
324
|
+
|
325
|
+
desc "print-automate-credentials", "Print Automate credentials"
|
326
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
327
|
+
def print_automate_credentials
|
328
|
+
get_cluster(options[:config]).print_automate_credentials
|
329
|
+
end
|
330
|
+
|
331
|
+
desc "run-command [SERVER_NAME_REGEX] [COMMAND]", "Runs a command in each server"
|
332
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
333
|
+
def run_command(server_name_regex=nil, command)
|
334
|
+
start_time = Time.now
|
335
|
+
get_cluster(options[:config]).get_sorted_servers(server_name_regex).each { |s| s.run_command(command); puts }
|
336
|
+
print_elapsed_time(Time.now - start_time)
|
337
|
+
end
|
338
|
+
|
339
|
+
desc "prepare-product-cache [SERVER_NAME_REGEX]", "Download required product packages to cache"
|
340
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
341
|
+
def prepare_product_cache(server_name_regex=nil)
|
342
|
+
start_time = Time.now
|
343
|
+
cluster = get_cluster(options[:config])
|
344
|
+
servers = cluster.get_sorted_servers(server_name_regex)
|
345
|
+
cluster.prep_product_cache(servers, true)
|
346
|
+
print_elapsed_time(Time.now - start_time)
|
347
|
+
end
|
348
|
+
|
349
|
+
desc "up [SERVER_NAME_REGEX]", "Start servers - This is the default if no subcommand is given"
|
350
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
351
|
+
def up(server_name_regex=nil)
|
352
|
+
start_time = Time.now
|
353
|
+
get_cluster(options[:config]).up(server_name_regex)
|
354
|
+
print_elapsed_time(Time.now - start_time)
|
355
|
+
end
|
356
|
+
|
357
|
+
desc "halt [SERVER_NAME_REGEX]", "Shutdown servers"
|
358
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
359
|
+
def halt(server_name_regex=nil)
|
360
|
+
start_time = Time.now
|
361
|
+
get_cluster(options[:config]).halt(server_name_regex)
|
362
|
+
print_elapsed_time(Time.now - start_time)
|
363
|
+
end
|
364
|
+
|
365
|
+
desc "snapshot [SERVER_NAME_REGEX]", "Manage a cluster's snapshots"
|
366
|
+
option :comment, :aliases => "-c", :desc => "Add snapshot comment"
|
367
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
368
|
+
option :destroy, :aliases => "-d", :desc => "Destroy snapshot - use ALL to destroy all snapshots"
|
369
|
+
option :list, :aliases => "-l", :type => :boolean, :desc => "List snapshots"
|
370
|
+
option :restore, :aliases => "-r", :desc => "Restore snapshots"
|
371
|
+
def snapshot(server_name_regex=nil)
|
372
|
+
start_time = Time.now
|
373
|
+
servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
|
374
|
+
if options[:list]
|
375
|
+
servers.each_with_index do |s, server_index|
|
376
|
+
puts s.name
|
377
|
+
s.snapshot_list.each do |snapname, snaptime, snap_comment|
|
378
|
+
printf " |_ %s %s %s\n", snapname, snaptime, snap_comment
|
379
|
+
end
|
380
|
+
puts if server_index + 1 < servers.length
|
381
|
+
end
|
382
|
+
return
|
383
|
+
elsif options[:destroy]
|
384
|
+
snapname = options[:destroy] == 'destroy' ? "LAST" : options[:destroy]
|
385
|
+
servers.each { |s| s.snapshot_destroy(snapname); puts }
|
386
|
+
elsif options[:restore]
|
387
|
+
running_servers = Array.new
|
388
|
+
servers.each do |s|
|
389
|
+
running_servers << s.name if s.container.running?
|
390
|
+
end
|
391
|
+
unless running_servers.empty?
|
392
|
+
puts "ERROR: Aborting snapshot restore because the following servers are running"
|
393
|
+
puts running_servers
|
394
|
+
exit 1
|
395
|
+
end
|
396
|
+
snapname = options[:restore] == 'restore' ? "LAST" : options[:restore]
|
397
|
+
servers.each { |s| s.snapshot_restore(snapname); puts }
|
398
|
+
else
|
399
|
+
running_servers = Array.new
|
400
|
+
servers.each do |s|
|
401
|
+
running_servers << s.name if s.container.running?
|
402
|
+
end
|
403
|
+
unless running_servers.empty?
|
404
|
+
puts "ERROR: Aborting snapshot because the following servers are running"
|
405
|
+
puts running_servers
|
406
|
+
exit 1
|
407
|
+
end
|
408
|
+
servers.each { |s| s.snapshot(options[:comment]); puts }
|
409
|
+
end
|
410
|
+
print_elapsed_time(Time.now - start_time)
|
411
|
+
end
|
412
|
+
|
413
|
+
desc "destroy [SERVER_NAME_REGEX]", "Destroy servers"
|
414
|
+
option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
|
415
|
+
option :force, :aliases => "-f", :type => :boolean, :desc => "Destroy servers without confirmation"
|
416
|
+
def destroy(server_name_regex=nil)
|
417
|
+
servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
|
418
|
+
if servers.empty?
|
419
|
+
puts "No matching server names were found"
|
420
|
+
exit
|
421
|
+
end
|
422
|
+
unless options[:force]
|
423
|
+
confirmation_string = String.new
|
424
|
+
servers.reverse_each { |s| confirmation_string += "#{s.name}\n" }
|
425
|
+
confirmation_string += "Are you sure you want to destroy these servers? (y/N)\n"
|
426
|
+
return unless yes?(confirmation_string)
|
427
|
+
end
|
428
|
+
start_time = Time.now
|
429
|
+
get_cluster(options[:config]).destroy(server_name_regex)
|
430
|
+
print_elapsed_time(Time.now - start_time)
|
431
|
+
end
|
432
|
+
|
433
|
+
end
|
434
|
+
end
|