dev-lxc 1.7.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/dev-lxc.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.version = DevLXC::VERSION
9
9
  spec.authors = ["Jeremiah Snapp"]
10
10
  spec.email = ["jeremiah@getchef.com"]
11
- spec.description = %q{A tool for creating Chef server clusters using LXC containers}
11
+ spec.description = %q{A tool for building Chef server clusters using LXC containers}
12
12
  spec.summary = spec.description
13
13
  spec.licenses = "Apache2"
14
14
  spec.homepage = "https://github.com/jeremiahsnapp/dev-lxc"
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake", "~> 0"
23
+ spec.add_runtime_dependency "mixlib-install", "~> 0"
23
24
  spec.add_runtime_dependency "thor", "~> 0"
24
25
  spec.add_runtime_dependency "ruby-lxc", "~> 1.2.0"
25
26
  end
data/lib/dev-lxc/cli.rb CHANGED
@@ -6,59 +6,6 @@ module DevLXC::CLI
6
6
  class DevLXC < Thor
7
7
 
8
8
  no_commands{
9
- def validate_cluster_config(cluster_config)
10
- hostnames = Array.new
11
- mounts = Array.new
12
- packages = Array.new
13
- ssh_keys = Array.new
14
-
15
- mounts.concat(cluster_config['mounts']) unless cluster_config['mounts'].nil?
16
- ssh_keys.concat(cluster_config['ssh-keys']) unless cluster_config['ssh-keys'].nil?
17
-
18
- %w(chef-server analytics compliance supermarket adhoc).each do |server_type|
19
- unless cluster_config[server_type].nil?
20
- hostnames << cluster_config[server_type]['api_fqdn'] unless cluster_config[server_type]['api_fqdn'].nil?
21
- hostnames << cluster_config[server_type]['analytics_fqdn'] unless cluster_config[server_type]['analytics_fqdn'].nil?
22
- hostnames.concat(cluster_config[server_type]['servers'].keys) unless cluster_config[server_type]['servers'].nil?
23
- mounts.concat(cluster_config[server_type]['mounts']) unless cluster_config[server_type]['mounts'].nil?
24
- packages.concat(cluster_config[server_type]['packages'].values) unless cluster_config[server_type]['packages'].nil?
25
- ssh_keys.concat(cluster_config[server_type]['ssh-keys']) unless cluster_config[server_type]['ssh-keys'].nil?
26
- end
27
- end
28
- unless hostnames.empty?
29
- hostnames.each do |hostname|
30
- unless hostname.end_with?(".lxc")
31
- puts "ERROR: Hostname #{hostname} does not end with '.lxc'."
32
- exit 1
33
- end
34
- end
35
- end
36
- unless mounts.empty?
37
- mounts.each do |mount|
38
- unless File.exists?(mount.split.first)
39
- puts "ERROR: Mount source #{mount.split.first} does not exist."
40
- exit 1
41
- end
42
- end
43
- end
44
- unless packages.empty?
45
- packages.each do |package|
46
- unless File.exists?(package)
47
- puts "ERROR: Package #{package} does not exist."
48
- exit 1
49
- end
50
- end
51
- end
52
- unless ssh_keys.empty?
53
- ssh_keys.each do |ssh_key|
54
- unless File.exists?(ssh_key)
55
- puts "ERROR: SSH key #{ssh_key} does not exist."
56
- exit 1
57
- end
58
- end
59
- end
60
- end
61
-
62
9
  def get_cluster(config_file=nil)
63
10
  config_file ||= "dev-lxc.yml"
64
11
  if ! File.exists?(config_file)
@@ -67,162 +14,57 @@ module DevLXC::CLI
67
14
  exit 1
68
15
  end
69
16
  cluster_config = YAML.load(IO.read(config_file))
70
- validate_cluster_config(cluster_config)
71
17
  ::DevLXC::Cluster.new(cluster_config)
72
18
  end
73
19
 
74
- def match_server_name_regex(server_name_regex)
75
- get_cluster(options[:config]).servers.select { |s| s.server.name =~ /#{server_name_regex}/ }
76
- end
77
-
78
20
  def print_elapsed_time(elapsed_time)
79
21
  printf "dev-lxc is finished. (%im %.2fs)\n", elapsed_time / 60, elapsed_time % 60
80
22
  end
81
23
  }
82
24
 
83
- desc "create [PLATFORM_IMAGE_NAME]", "Create a platform image"
25
+ desc "create-base-container [BASE_CONTAINER_NAME]", "Create a base container"
84
26
  option :options, :aliases => "-o", :desc => "Specify additional options for the lxc create"
85
- def create(platform_image_name=nil)
27
+ def create_base_container(base_container_name=nil)
86
28
  start_time = Time.now
87
- platform_image_names = %w(p-ubuntu-1204 p-ubuntu-1404 p-ubuntu-1604 p-centos-5 p-centos-6 p-centos-7)
88
- if platform_image_name.nil? || ! platform_image_names.include?(platform_image_name)
89
- platform_image_names_with_index = platform_image_names.map.with_index{ |a, i| [i+1, *a]}
90
- print_table platform_image_names_with_index
91
- selection = ask("Which platform image do you want to create?", :limited_to => platform_image_names_with_index.map{|c| c[0].to_s})
92
- platform_image_name = platform_image_names[selection.to_i - 1]
29
+ base_container_names = %w(b-ubuntu-1204 b-ubuntu-1404 b-ubuntu-1604 b-centos-5 b-centos-6 b-centos-7)
30
+ if base_container_name.nil? || ! base_container_names.include?(base_container_name)
31
+ base_container_names_with_index = base_container_names.map.with_index{ |a, i| [i+1, *a]}
32
+ print_table base_container_names_with_index
33
+ selection = ask("Which base container do you want to create?", :limited_to => base_container_names_with_index.map{|c| c[0].to_s})
34
+ base_container_name = base_container_names[selection.to_i - 1]
93
35
  end
94
- ::DevLXC.create_platform_image(platform_image_name, options[:options])
95
- puts
96
- print_elapsed_time(Time.now - start_time)
97
- end
98
-
99
- desc "install-chef-client [CONTAINER_NAME]", "Install Chef Client in container"
100
- option :version, :aliases => "-v", :desc => "Specify the version of Chef Client to install"
101
- def install_chef_client(container_name)
102
- start_time = Time.now
103
- container = ::DevLXC::Container.new(container_name)
104
- container.install_chef_client(options[:version])
36
+ ::DevLXC.create_base_container(base_container_name, options[:options])
105
37
  puts
106
38
  print_elapsed_time(Time.now - start_time)
107
39
  end
108
40
 
109
- desc "configure-chef-client [CONTAINER_NAME]", "Configure Chef Client in container"
110
- option :chef_server_url, :aliases => "-s", :desc => "Specify the URL of the Chef Server"
111
- option :validation_client_name, :aliases => "-u", :desc => "Specify the name of the validation client"
112
- option :validation_key, :aliases => "-k", :desc => "Specify the path to the validation key"
113
- option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
114
- def configure_chef_client(container_name)
115
- start_time = Time.now
116
- chef_server_url = options[:chef_server_url]
117
- validation_client_name = options[:validation_client_name]
118
- validation_key = options[:validation_key]
119
- if chef_server_url.nil? && validation_client_name.nil? && validation_key.nil?
120
- cluster = get_cluster(options[:config])
121
- chef_server_bootstrap_backend = ::DevLXC::Container.new(cluster.chef_server_bootstrap_backend, cluster.lxc_config_path)
122
- unless chef_server_bootstrap_backend.defined?
123
- puts "ERROR: Can not copy validation key because Chef Server '#{chef_server_bootstrap_backend.name}' is not created."
124
- exit 1
125
- end
126
- chef_server_url = "https://#{cluster.api_fqdn}/organizations/demo"
127
- validation_client_name = 'demo-validator'
128
- validation_key = "#{chef_server_bootstrap_backend.config_item('lxc.rootfs')}/root/chef-repo/.chef/demo-validator.pem"
129
- elsif chef_server_url.nil? || validation_client_name.nil? || validation_key.nil?
130
- puts "ERROR: All of the --chef-server-url, --validation-client-name and --validation-key options must be set or left unset. Do not set only some of these options."
131
- exit 1
132
- end
133
- container = ::DevLXC::Container.new(container_name)
134
- container.configure_chef_client(chef_server_url, validation_client_name, validation_key)
135
- puts
136
- print_elapsed_time(Time.now - start_time)
137
- end
138
-
139
- desc "bootstrap-container [BASE_CONTAINER_NAME] [CONTAINER_NAME]", "Bootstrap Chef Client in container"
140
- option :version, :aliases => "-v", :desc => "Specify the version of Chef Client to install"
141
- option :run_list, :aliases => "-r", :desc => "Specify the Chef Client run_list"
142
- option :chef_server_url, :aliases => "-s", :desc => "Specify the URL of the Chef Server"
143
- option :validation_client_name, :aliases => "-u", :desc => "Specify the name of the validation client"
144
- option :validation_key, :aliases => "-k", :desc => "Specify the path to the validation key"
145
- option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
146
- def bootstrap_container(base_container_name=nil, container_name)
147
- start_time = Time.now
148
- chef_server_url = options[:chef_server_url]
149
- validation_client_name = options[:validation_client_name]
150
- validation_key = options[:validation_key]
151
- if chef_server_url.nil? && validation_client_name.nil? && validation_key.nil?
152
- cluster = get_cluster(options[:config])
153
- chef_server_bootstrap_backend = ::DevLXC::Container.new(cluster.chef_server_bootstrap_backend, cluster.lxc_config_path)
154
- unless chef_server_bootstrap_backend.defined?
155
- puts "ERROR: Can not copy validation key because Chef Server '#{chef_server_bootstrap_backend.name}' is not created."
156
- exit 1
157
- end
158
- chef_server_url = "https://#{cluster.api_fqdn}/organizations/demo"
159
- validation_client_name = 'demo-validator'
160
- validation_key = "#{chef_server_bootstrap_backend.config_item('lxc.rootfs')}/root/chef-repo/.chef/demo-validator.pem"
161
- elsif chef_server_url.nil? || validation_client_name.nil? || validation_key.nil?
162
- puts "ERROR: All of the --chef-server-url, --validation-client-name and --validation-key options must be set or left unset. Do not set only some of these options."
163
- exit 1
164
- end
165
- container = ::DevLXC::Container.new(container_name)
166
- container.bootstrap_container(base_container_name, options[:version], options[:run_list], chef_server_url, validation_client_name, validation_key)
167
- puts
168
- print_elapsed_time(Time.now - start_time)
169
- end
170
-
171
- desc "init [UNIQUE_STRING]", "Provide a cluster config file with optional uniqueness in server names and FQDNs"
172
- option :open_source, :type => :boolean, :desc => "Standalone Old Open Source Chef Server"
173
- option :tiered_chef, :type => :boolean, :desc => "Tiered Chef Server"
41
+ desc "init", "Provide a cluster config file"
174
42
  option :chef, :type => :boolean, :desc => "Standalone Chef Server"
43
+ option :chef_tier, :type => :boolean, :desc => "Chef Server using Tier topology with one backend"
44
+ option :chef_backend, :type => :boolean, :desc => "Chef Server using Chef Backend HA topology with three backends"
45
+ option :nodes, :type => :boolean, :desc => "Node Servers"
175
46
  option :analytics, :type => :boolean, :desc => "Analytics Server"
176
47
  option :compliance, :type => :boolean, :desc => "Compliance Server"
177
48
  option :supermarket, :type => :boolean, :desc => "Supermarket Server"
178
49
  option :adhoc, :type => :boolean, :desc => "Adhoc Servers"
179
- def init(unique_string=nil)
180
- header = %Q(## platform_image can be one of the following:
181
- ## p-centos-5, p-centos-6, p-centos-7, p-ubuntu-1204, p-ubuntu-1404 or p-ubuntu-1604
182
- platform_image: p-ubuntu-1404
183
-
184
- ## platform_image_options can be set to provide additional arguments to the LXC create command
185
- ## reference arg examples: https://github.com/lxc/lxc/blob/lxc-2.0.0/templates/lxc-download.in#L200-L207
186
- #platform_image_options: --no-validate
50
+ option :append, :aliases => "-a", :type => :boolean, :desc => "Do not generate the global config header"
51
+ option :filename, :aliases => "-f", :desc => "Write generated content to FILE rather than standard output."
52
+ def init
53
+ header = %Q(# base_container must be the name of an existing container
54
+ base_container: b-ubuntu-1404
187
55
 
188
- ## list any host directories you want mounted into the servers
189
- mounts:
190
- - /root/dev root/dev
56
+ # list any host directories you want mounted into the servers
57
+ #mounts:
58
+ # - /root/dev root/dev
191
59
 
192
- ## list any SSH public keys you want added to /home/dev-lxc/.ssh/authorized_keys
60
+ # list any SSH public keys you want added to /home/dev-lxc/.ssh/authorized_keys
193
61
  #ssh-keys:
194
62
  # - /root/dev/clusters/id_rsa.pub
195
63
 
196
- ## DHCP reserved (static) IPs must be selected from the IP range 10.0.3.150 - 254
197
- )
198
-
199
- chef_packages_path = "/root/dev/chef-packages"
200
- open_source_package = "server: #{chef_packages_path}/osc/chef-server_11.1.6-1_amd64.deb"
201
- chef_server_package = "server: #{chef_packages_path}/cs/chef-server-core_12.6.0-1_amd64.deb"
202
- manage_package = "manage: #{chef_packages_path}/manage/chef-manage_2.3.0-1_amd64.deb"
203
- reporting_package = "reporting: #{chef_packages_path}/reporting/opscode-reporting_1.5.6-1_amd64.deb"
204
- push_jobs_server_package = "push-jobs-server: #{chef_packages_path}/push-jobs-server/opscode-push-jobs-server_1.1.6-1_amd64.deb"
205
- analytics_package = "analytics: #{chef_packages_path}/analytics/opscode-analytics_1.4.0-1_amd64.deb"
206
- compliance_package = "compliance: #{chef_packages_path}/compliance/chef-compliance_1.1.9-1_amd64.deb"
207
- supermarket_package = "supermarket: #{chef_packages_path}/supermarket/supermarket_2.5.2-1_amd64.deb"
208
-
209
- open_source_config = %Q(
210
- chef-server:
211
- packages:
212
- #{open_source_package}
213
- api_fqdn: chef.lxc
214
- topology: open-source
215
- servers:
216
- osc-chef.lxc:
217
- ipaddress: 10.0.3.200
64
+ # DHCP reserved (static) IPs must be selected from the IP range 10.0.3.150 - 254
218
65
  )
219
- tiered_chef_config = %Q(
66
+ chef_tier_config = %Q(
220
67
  chef-server:
221
- packages:
222
- #{chef_server_package}
223
- #{manage_package}
224
- #{reporting_package}
225
- #{push_jobs_server_package}
226
68
  topology: tier
227
69
  api_fqdn: chef.lxc
228
70
  servers:
@@ -230,44 +72,53 @@ chef-server:
230
72
  ipaddress: 10.0.3.201
231
73
  role: backend
232
74
  bootstrap: true
75
+ products:
76
+ chef-server:
77
+ push-jobs-server:
78
+ reporting:
233
79
  chef-fe1.lxc:
234
80
  ipaddress: 10.0.3.202
235
81
  role: frontend
82
+ products:
83
+ chef-server:
84
+ manage:
85
+ push-jobs-server:
86
+ reporting:
236
87
  )
237
88
  chef_config = %Q(
238
89
  chef-server:
239
- packages:
240
- #{chef_server_package}
241
- #{manage_package}
242
- #{reporting_package}
243
- #{push_jobs_server_package}
244
90
  servers:
245
91
  chef.lxc:
246
92
  ipaddress: 10.0.3.203
93
+ products:
94
+ chef-server:
95
+ manage:
96
+ push-jobs-server:
97
+ reporting:
247
98
  )
248
99
  analytics_config = %Q(
249
100
  analytics:
250
- packages:
251
- #{analytics_package}
252
101
  servers:
253
102
  analytics.lxc:
254
103
  ipaddress: 10.0.3.204
104
+ products:
105
+ analytics:
255
106
  )
256
107
  compliance_config = %Q(
257
108
  compliance:
258
- packages:
259
- #{compliance_package}
260
109
  servers:
261
110
  compliance.lxc:
262
111
  ipaddress: 10.0.3.205
112
+ products:
113
+ compliance:
263
114
  )
264
115
  supermarket_config = %Q(
265
116
  supermarket:
266
- packages:
267
- #{supermarket_package}
268
117
  servers:
269
118
  supermarket.lxc:
270
119
  ipaddress: 10.0.3.206
120
+ products:
121
+ supermarket:
271
122
  )
272
123
  adhoc_config = %Q(
273
124
  adhoc:
@@ -275,67 +126,95 @@ adhoc:
275
126
  adhoc.lxc:
276
127
  ipaddress: 10.0.3.207
277
128
  )
278
- config = header
279
- config += open_source_config if options[:open_source]
129
+ chef_backend_config = %Q(
130
+ chef-backend:
131
+ api_fqdn: chef.lxc
132
+ servers:
133
+ chef-backend1.lxc:
134
+ ipaddress: 10.0.3.208
135
+ role: backend
136
+ leader: true
137
+ products:
138
+ chef-backend:
139
+ channel: current
140
+ chef-backend2.lxc:
141
+ ipaddress: 10.0.3.209
142
+ role: backend
143
+ products:
144
+ chef-backend:
145
+ channel: current
146
+ chef-backend3.lxc:
147
+ ipaddress: 10.0.3.210
148
+ role: backend
149
+ products:
150
+ chef-backend:
151
+ channel: current
152
+ chef-frontend1.lxc:
153
+ ipaddress: 10.0.3.211
154
+ role: frontend
155
+ bootstrap: true
156
+ products:
157
+ chef-server:
158
+ manage:
159
+ )
160
+ nodes_config = %Q(
161
+ nodes:
162
+ servers:
163
+ node-1.lxc:
164
+ products:
165
+ chef:
166
+ )
167
+ config = ""
168
+ config += header unless options[:append]
280
169
  config += chef_config if options[:chef]
281
- config += tiered_chef_config if options[:tiered_chef]
170
+ config += chef_tier_config if options[:chef_tier]
282
171
  config += analytics_config if options[:analytics]
283
172
  config += compliance_config if options[:compliance]
284
173
  config += supermarket_config if options[:supermarket]
285
174
  config += adhoc_config if options[:adhoc]
286
- unless unique_string.nil?
287
- config_hash = YAML.load(config.gsub(/^#/, ''))
288
- config.gsub!(/api_fqdn:\s+#{config_hash['api_fqdn']}/, "api_fqdn: #{unique_string}#{config_hash['api_fqdn']}")
289
- config.gsub!(/analytics_fqdn:\s+#{config_hash['analytics_fqdn']}/, "analytics_fqdn: #{unique_string}#{config_hash['analytics_fqdn']}")
290
- %w(chef-server analytics compliance supermarket adhoc).each do |server_type|
291
- if config_hash[server_type]
292
- config_hash[server_type]['servers'].keys.each do |server_name|
293
- config.gsub!(/ #{server_name}:/, " #{unique_string}#{server_name}:")
294
- end
295
- end
296
- end
175
+ config += chef_backend_config if options[:chef_backend]
176
+ config += nodes_config if options[:nodes]
177
+ if options[:filename]
178
+ mode = options[:append] ? 'a' : 'w'
179
+ IO.write(options[:filename], config, mode: mode)
180
+ else
181
+ puts config
297
182
  end
298
- puts config
299
- end
300
-
301
- desc "global-status", "Show status of all dev-lxc images and servers"
302
- def global_status
303
- containers = Array.new
304
- LXC::list_containers({config_path: '/var/lib/dev-lxc'}).map { |c| containers << ::DevLXC::Container.new(c, '/var/lib/dev-lxc').status }
305
- max_container_name_length = containers.max_by { |c| c['name'].length }['name'].length unless containers.empty?
306
- containers.each { |c| printf "%#{max_container_name_length}s %-15s %s\n", c['name'], c['state'], c['ip_addresses'] }
307
183
  end
308
184
 
309
185
  desc "status [SERVER_NAME_REGEX]", "Show status of servers"
310
186
  option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
311
187
  def status(server_name_regex=nil)
312
188
  cluster = get_cluster(options[:config])
313
- puts "Chef Server FQDN: #{cluster.api_fqdn}\n" if cluster.api_fqdn
314
- puts "Analytics FQDN: #{cluster.analytics_fqdn}\n" if cluster.analytics_fqdn
315
- puts "Compliance FQDN: #{cluster.compliance_fqdn}\n" if cluster.compliance_fqdn
316
- puts "Supermarket FQDN: #{cluster.supermarket_fqdn}\n" if cluster.supermarket_fqdn
317
- puts
189
+ if cluster.config['chef-server'][:topology] == "tier" && cluster.config['chef-server'][:fqdn]
190
+ printf "Chef Server FQDN: %s\n\n", cluster.config['chef-server'][:fqdn]
191
+ end
192
+ if cluster.config['chef-backend'][:fqdn]
193
+ printf "Chef Server FQDN: %s\n\n", cluster.config['chef-backend'][:fqdn]
194
+ end
195
+ if cluster.config['analytics'][:topology] == "tier" && cluster.config['analytics'][:fqdn]
196
+ printf "Analytics FQDN: %s\n\n", cluster.config['analytics'][:fqdn]
197
+ end
318
198
  servers = Array.new
319
- match_server_name_regex(server_name_regex).map { |s| servers << s.server.status }
199
+ cluster.get_sorted_servers(server_name_regex).map { |s| servers << s.status }
320
200
  max_server_name_length = servers.max_by { |s| s['name'].length }['name'].length unless servers.empty?
321
- servers.each { |s| printf "%#{max_server_name_length}s %-15s %s\n", s['name'], s['state'], s['ip_addresses'] }
322
- end
323
-
324
- desc "realpath [SERVER_NAME_REGEX] [ROOTFS_PATH]", "Returns the real path to a file in each server"
325
- option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
326
- def realpath(server_name_regex=nil, rootfs_path)
327
- realpath = Array.new
328
- match_server_name_regex(server_name_regex).map { |s| realpath << s.realpath(rootfs_path) }
329
- puts realpath.compact
201
+ servers.each_with_index do |s, server_index|
202
+ printf "%-#{max_server_name_length}s %-15s %s\n", s['name'], s['state'].upcase, s['ip_addresses']
203
+ server = cluster.get_server(s['name'])
204
+ server.snapshot_list.each do |snapname, snaptime, snap_comment|
205
+ printf " |_ %s %s %s\n", snapname, snaptime, snap_comment
206
+ end
207
+ puts if server_index + 1 < servers.length
208
+ end
330
209
  end
331
210
 
332
211
  desc "attach [SERVER_NAME_REGEX]", "Attach the terminal to a single server"
333
212
  option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
334
213
  def attach(server_name_regex)
335
- servers = match_server_name_regex(server_name_regex)
214
+ servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
336
215
  if servers.length > 1
337
216
  puts "ERROR: The following servers matched '#{server_name_regex}'"
338
- servers.map { |s| puts " #{s.server.name}" }
217
+ servers.map { |s| puts " #{s.name}" }
339
218
  puts " Please specify a single server to attach to"
340
219
  exit 1
341
220
  elsif servers.empty?
@@ -343,9 +222,9 @@ adhoc:
343
222
  puts " Please specify a single server to attach to"
344
223
  exit 1
345
224
  end
346
- server = servers.first.server
347
- unless server.defined? && server.running?
348
- puts "ERROR: Server '#{server.name}' is not running"
225
+ container = servers.first.container
226
+ if !container.defined? || !container.running?
227
+ puts "ERROR: Server '#{container.name}' is not running"
349
228
  exit 1
350
229
  end
351
230
  attach_opts = {
@@ -354,7 +233,7 @@ adhoc:
354
233
  extra_env_vars: ["LANG=en_US.UTF-8", "TERM=linux", "HOME=#{ENV['HOME']}"]
355
234
  }
356
235
  shell = ENV['SHELL']
357
- server.attach(attach_opts) { system(shell) }
236
+ container.attach(attach_opts) { system(shell) }
358
237
  end
359
238
 
360
239
  desc "chef-repo", "Creates a chef-repo in the current directory using files from the cluster's backend /root/chef-repo"
@@ -365,48 +244,11 @@ adhoc:
365
244
  get_cluster(options[:config]).chef_repo(options[:force], options[:pivotal])
366
245
  end
367
246
 
368
- desc "list-images [SERVER_NAME_REGEX]", "List of each servers' images created during the build process"
369
- option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
370
- def list_images(server_name_regex=nil)
371
- lxc_config_path = get_cluster(options[:config]).lxc_config_path
372
- images = Hash.new { |h,k| h[k] = Hash.new { |h,k| h[k] = Array.new } }
373
- match_server_name_regex(server_name_regex).each do |s|
374
- images[s.platform_image_name][s.shared_image_name] << s.server.name
375
- end
376
- images.each_with_index do |(platform_name, shared), images_index|
377
- shared.each_with_index do |(shared_name, final), shared_index|
378
- printf "Platform: %27s %s\n", (LXC::Container.new(platform_name, lxc_config_path).defined? ? "Created" : "Not Created"), platform_name
379
- unless shared_name.empty?
380
- puts "|"
381
- printf "\\_ Shared: %26s %s\n", (LXC::Container.new(shared_name, lxc_config_path).defined? ? "Created" : "Not Created"), shared_name
382
- end
383
- final.each_with_index do |final_name, final_index|
384
- puts " |"
385
- unique_name = "u-#{final_name}"
386
- printf " \\_ Unique: %23s %s\n", (LXC::Container.new(unique_name, lxc_config_path).defined? ? "Created" : "Not Created"), unique_name
387
-
388
- shared_connector = (final_index + 1 < final.length ? "|" : " ")
389
-
390
- custom_name = "c-#{final_name}"
391
- if LXC::Container.new(custom_name, lxc_config_path).defined?
392
- printf " #{shared_connector} \\_ Custom: %20s %s\n", "Created", custom_name
393
- custom_spacing = " "
394
- final_width = 11
395
- else
396
- final_width = 14
397
- end
398
- printf " #{shared_connector} #{custom_spacing}\\_ Final Server: %#{final_width}s %s\n", (LXC::Container.new(final_name, lxc_config_path).defined? ? "Created" : "Not Created"), final_name
399
- end
400
- puts if (shared_index + 1 < shared.length) || (images_index + 1 < images.length)
401
- end
402
- end
403
- end
404
-
405
247
  desc "run-command [SERVER_NAME_REGEX] [COMMAND]", "Runs a command in each server"
406
248
  option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
407
249
  def run_command(server_name_regex=nil, command)
408
250
  start_time = Time.now
409
- match_server_name_regex(server_name_regex).each { |s| s.run_command(command); puts }
251
+ get_cluster(options[:config]).get_sorted_servers(server_name_regex).each { |s| s.run_command(command); puts }
410
252
  print_elapsed_time(Time.now - start_time)
411
253
  end
412
254
 
@@ -414,7 +256,8 @@ adhoc:
414
256
  option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
415
257
  def up(server_name_regex=nil)
416
258
  start_time = Time.now
417
- match_server_name_regex(server_name_regex).each { |s| s.start; puts }
259
+ get_cluster(options[:config]).up(server_name_regex)
260
+ puts
418
261
  print_elapsed_time(Time.now - start_time)
419
262
  end
420
263
 
@@ -422,53 +265,75 @@ adhoc:
422
265
  option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
423
266
  def halt(server_name_regex=nil)
424
267
  start_time = Time.now
425
- match_server_name_regex(server_name_regex).reverse_each { |s| s.stop; puts }
268
+ get_cluster(options[:config]).get_sorted_servers(server_name_regex).reverse_each { |s| s.stop; puts }
426
269
  print_elapsed_time(Time.now - start_time)
427
270
  end
428
271
 
429
- desc "snapshot [SERVER_NAME_REGEX]", "Create a snapshot of servers"
272
+ desc "snapshot [SERVER_NAME_REGEX]", "Manage a cluster's snapshots"
273
+ option :comment, :aliases => "-c", :desc => "Add snapshot comment"
430
274
  option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
431
- option :force, :aliases => "-f", :type => :boolean, :desc => "Overwrite existing custom images"
275
+ option :destroy, :aliases => "-d", :desc => "Destroy snapshot - use ALL to destroy all snapshots"
276
+ option :list, :aliases => "-l", :type => :boolean, :desc => "List snapshots"
277
+ option :restore, :aliases => "-r", :desc => "Restore snapshots"
432
278
  def snapshot(server_name_regex=nil)
433
279
  start_time = Time.now
434
- non_stopped_servers = Array.new
435
- existing_custom_images = Array.new
436
- lxc_config_path = get_cluster(options[:config]).lxc_config_path
437
- match_server_name_regex(server_name_regex).each do |s|
438
- non_stopped_servers << s.server.name if s.server.state != :stopped
439
- existing_custom_images << s.server.name if LXC::Container.new("c-#{s.server.name}", lxc_config_path).defined?
440
- end
441
- unless non_stopped_servers.empty?
442
- puts "ERROR: Aborting snapshot because the following servers are not stopped"
443
- puts non_stopped_servers
444
- exit 1
445
- end
446
- unless existing_custom_images.empty? || options[:force]
447
- puts "ERROR: The following servers already have a custom image"
448
- puts " Use the `--force` or `-f` option to overwrite existing custom images"
449
- puts existing_custom_images
450
- exit 1
280
+ servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
281
+ if options[:list]
282
+ servers.each_with_index do |s, server_index|
283
+ puts s.name
284
+ s.snapshot_list.each do |snapname, snaptime, snap_comment|
285
+ printf " |_ %s %s %s\n", snapname, snaptime, snap_comment
286
+ end
287
+ puts if server_index + 1 < servers.length
288
+ end
289
+ return
290
+ elsif options[:destroy]
291
+ snapname = options[:destroy] == 'destroy' ? "LAST" : options[:destroy]
292
+ servers.each { |s| s.snapshot_destroy(snapname); puts }
293
+ elsif options[:restore]
294
+ running_servers = Array.new
295
+ servers.each do |s|
296
+ running_servers << s.name if s.container.running?
297
+ end
298
+ unless running_servers.empty?
299
+ puts "ERROR: Aborting snapshot restore because the following servers are running"
300
+ puts running_servers
301
+ exit 1
302
+ end
303
+ snapname = options[:restore] == 'restore' ? "LAST" : options[:restore]
304
+ servers.each { |s| s.snapshot_restore(snapname); puts }
305
+ else
306
+ running_servers = Array.new
307
+ servers.each do |s|
308
+ running_servers << s.name if s.container.running?
309
+ end
310
+ unless running_servers.empty?
311
+ puts "ERROR: Aborting snapshot because the following servers are running"
312
+ puts running_servers
313
+ exit 1
314
+ end
315
+ servers.each { |s| s.snapshot(options[:comment]); puts }
451
316
  end
452
- match_server_name_regex(server_name_regex).each { |s| s.snapshot(options[:force]); puts }
453
317
  print_elapsed_time(Time.now - start_time)
454
318
  end
455
319
 
456
320
  desc "destroy [SERVER_NAME_REGEX]", "Destroy servers"
457
321
  option :config, :desc => "Specify a cluster's YAML config file. `./dev-lxc.yml` will be used by default"
458
- option :custom, :aliases => "-c", :type => :boolean, :desc => "Also destroy the custom images"
459
- option :unique, :aliases => "-u", :type => :boolean, :desc => "Also destroy the unique images"
460
- option :shared, :aliases => "-s", :type => :boolean, :desc => "Also destroy the shared images"
461
- option :platform, :aliases => "-p", :type => :boolean, :desc => "Also destroy the platform images"
322
+ option :force, :aliases => "-f", :type => :boolean, :desc => "Destroy servers without confirmation"
462
323
  def destroy(server_name_regex=nil)
463
- start_time = Time.now
464
- match_server_name_regex(server_name_regex).reverse_each do |s|
465
- s.destroy
466
- s.destroy_image(:custom) if options[:custom]
467
- s.destroy_image(:unique) if options[:unique]
468
- s.destroy_image(:shared) if options[:shared]
469
- s.destroy_image(:platform) if options[:platform]
470
- puts
324
+ servers = get_cluster(options[:config]).get_sorted_servers(server_name_regex)
325
+ if servers.empty?
326
+ puts "No matching server names were found"
327
+ exit
328
+ end
329
+ unless options[:force]
330
+ confirmation_string = String.new
331
+ servers.reverse_each { |s| confirmation_string += "#{s.name}\n" }
332
+ confirmation_string += "Are you sure you want to destroy these servers? (y/N)\n"
333
+ return unless yes?(confirmation_string)
471
334
  end
335
+ start_time = Time.now
336
+ servers.reverse_each { |s| s.destroy; puts }
472
337
  print_elapsed_time(Time.now - start_time)
473
338
  end
474
339