dev-lxc 1.7.0 → 2.0.0

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.
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