jupiter 0.0.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,100 @@
1
+ module Jupiter
2
+ class Cli < Thor
3
+
4
+ desc "state", "Shows power state for supplied virtual machine (VMware Tools must be running on VM)"
5
+ method_option :host, type: :string, required: true, desc: 'Host server the VM resides on'
6
+ method_option :guest, type: :string, required: true, desc: 'The virtual machine'
7
+
8
+ def state
9
+ vmserver = Jupiter.host_by_name(options[:host])
10
+ puts vmserver.vm_state(options[:guest])
11
+ vmserver.close_ssh
12
+ end
13
+
14
+ desc "ip", "Shows IP address for supplied virtual machine (VMware Tools must be running on VM)"
15
+ method_option :host, type: :string, required: true, desc: 'Host server the VM resides on'
16
+ method_option :guest, type: :string, required: true, desc: 'The virtual machine'
17
+
18
+ def ip
19
+ vmserver = Jupiter.host_by_name(options[:host])
20
+ puts vmserver.guest_ip(options[:guest])
21
+ vmserver.close_ssh
22
+ end
23
+
24
+ desc "manage", "Provision a new or manage an existing virtual machine"
25
+ method_option :pretend, default: false, desc: 'Connect to the vmware host but do nothing'
26
+ method_option :host, type: :string, required: true, desc: 'Host server the VM resides on'
27
+ method_option :guest, type: :string, required: false, desc: 'The virtual machine to manage'
28
+
29
+ def manage
30
+ Screen.new.clear
31
+ output = Jupiter::Cli::Output.new(self)
32
+ output.draw_menu
33
+ output.draw_new_line
34
+
35
+ if options[:guest].nil?
36
+ task = output.ask_which_task
37
+ else
38
+ task = '2'
39
+ end
40
+
41
+ if task == '1'
42
+ targetvm = ''
43
+ vmserver = Jupiter.host_by_name(options[:host])
44
+ output.manage_host(vmserver, options)
45
+ end
46
+
47
+ if task == '2'
48
+ vmserver = Jupiter.host_by_name(options[:host])
49
+ output.manage_vm(vmserver, options)
50
+ end
51
+ end
52
+
53
+ desc "register", "Registers the named VM with the supplied ESXi server"
54
+ method_option :host, type: :string, required: true, desc: 'Host server the VM resides on'
55
+ method_option :guest, type: :string, required: true, desc: 'The virtual machine'
56
+
57
+ def register
58
+ vmserver = Jupiter.host_by_name(options[:host])
59
+ puts vmserver.register_vm(options[:guest])
60
+ vmserver.close_ssh
61
+ end
62
+
63
+ desc "unregister", "Unregisters the named VM from the supplied ESXi server"
64
+ method_option :host, type: :string, required: true, desc: 'Host server the VM resides on'
65
+ method_option :guest, type: :string, required: true, desc: 'The virtual machine'
66
+
67
+ def unregister
68
+ vmserver = Jupiter.host_by_name(options[:host])
69
+ puts vmserver.unregister_vm(options[:guest])
70
+ vmserver.close_ssh
71
+ end
72
+
73
+ desc "runtime", "Start an Pry session with Jupiter loaded"
74
+ method_option :config
75
+ def runtime
76
+ # puts config
77
+ require 'pry'
78
+ pry
79
+ end
80
+
81
+ end
82
+
83
+ class Input
84
+ def clean_filename(filename)
85
+ filename.gsub(/[^a-zA-Z 0-9\-_]/, "").gsub(/\s/,'-')
86
+ # filename.gsub(/[^\w\s_-]+/, '')
87
+ # filename.gsub(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2')
88
+ # filename.gsub(/\s+/, '_')
89
+ end
90
+ end
91
+
92
+ class Screen
93
+ def clear
94
+ puts "\e[H\e[2J" # ANSI escape sequence to clear screen
95
+ end
96
+ end
97
+
98
+
99
+ end
100
+
@@ -0,0 +1,44 @@
1
+ module Jupiter
2
+ class Cli
3
+ module Colorizer
4
+ extend self
5
+
6
+ def color_map
7
+ {
8
+ red: 31,
9
+ white: 37,
10
+ yellow: 33,
11
+ blue: 34,
12
+ green: 32,
13
+ cyan: 36,
14
+ magenta: 35,
15
+ }
16
+ end
17
+
18
+ color_map.each do |color, code|
19
+ define_method color do |text|
20
+ colorize text, code
21
+ end
22
+ end
23
+
24
+ def color(color, text)
25
+ public_send(color, text)
26
+ end
27
+
28
+ def colorize(text, color_code)
29
+ "\e[#{color_code}m#{text}\e[0m"
30
+ end
31
+
32
+ # ANSI control codes
33
+ # 30 Black text
34
+ # 31 Red text
35
+ # 32 Green text
36
+ # 33 Yellow text
37
+ # 34 Blue text
38
+ # 35 Magenta text
39
+ # 36 Cyan text
40
+ # 37 White text
41
+ # 39 Default text color
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,355 @@
1
+ module Jupiter
2
+ class Cli
3
+ class Output
4
+
5
+ attr_accessor :cli
6
+
7
+ def initialize(cli)
8
+ self.cli = cli
9
+ end
10
+
11
+ def clearscreen
12
+ puts "\e[H\e[2J" # ANSI escape sequence to clear screen
13
+ end
14
+
15
+ def draw_menu
16
+ draw_line('#', 80, '', 'white', '')
17
+ draw_line('#', 80, 'WELCOME TO JUPITER', 'white', 'green')
18
+ draw_line('#', 80, '', 'white', '')
19
+ draw_line('#', 80, "We hope the god of sky and thunder will make your day most good and stuff.", 'white', 'green')
20
+ draw_line('#', 80, '', 'white', '')
21
+ end
22
+
23
+ def ask_which_task
24
+ valid_answers = ['1','2']
25
+ task = nil
26
+ until valid_answers.include? task do
27
+ draw_line('', 80, 'Would you like to create a new virtual machine or work with an existing one?', 'white', 'cyan')
28
+ draw_new_line
29
+ draw_line('', 80, '1. Clone / Create a Virtual Machine ', 'white', 'cyan')
30
+ draw_line('', 80, '2. Work with an existing Virtual Machine', 'white', 'cyan')
31
+ draw_new_line
32
+ task = cli.ask("Please select a number:")
33
+ if valid_answers.include?(task) == false
34
+ puts "\n\n* INVALID ANSWER *\n\n"
35
+ end
36
+ end
37
+ return task
38
+ end
39
+
40
+ def ask_how_to_clone
41
+ valid_answers = ['1','2']
42
+ task = nil
43
+ until valid_answers.include? task do
44
+ draw_line('', 80, 'Would you like to clone one of these virtual machines or use a template?', 'white', 'cyan')
45
+ draw_new_line
46
+ draw_line('', 80, '1. Clone Virtual Machine', 'white', 'cyan')
47
+ draw_line('', 80, '2. Use a Template', 'white', 'cyan')
48
+ draw_new_line
49
+ task = cli.ask("Please select a number:")
50
+ if valid_answers.include?(task) == false
51
+ puts "\n\n* INVALID ANSWER *\n\n"
52
+ end
53
+ end
54
+ return task
55
+ end
56
+
57
+ def ask_vm_to_clone(vmserver)
58
+ valid_answers = vmserver.server_vm_list
59
+ list_virtual_machines(vmserver)
60
+ draw_line('#', 80, "Note that these VMs might not all be registered on the server.", 'white', 'red')
61
+ draw_line('#', 80, "You can only clone VMs that are registered.", 'white', 'red')
62
+ draw_line('#', 80, '', 'white', '')
63
+ targetvm = nil
64
+ until valid_answers.include? targetvm do
65
+ targetvm = cli.ask("\nPlease enter the valid name of the virtual machine you wish to clone:")
66
+ if valid_answers.include?(targetvm) == false
67
+ puts "\n\n* INVALID VIRTUAL MACHINE NAME *\n\n"
68
+ end
69
+ end
70
+ return targetvm
71
+ end
72
+
73
+ def select_vm_to_manage(vmserver)
74
+ vm_list = vmserver.server_vm_list
75
+ draw_line('#', 80, '', 'white', '')
76
+ draw_line('#', 80, "These are the currect virtual machines found on #{vmserver.name}", 'white', 'green')
77
+ draw_line('#', 80, '', 'white', '')
78
+ draw_new_line
79
+ vm_list.each_index { |x| puts "#{x + 1}. #{vm_list[x]}" }
80
+ draw_new_line
81
+ draw_line('#', 80, '', 'white', '')
82
+ targetvm = cli.ask("\nPlease select the number of the Virtual Machine you wish to manage:")
83
+ target = targetvm.to_i - 1
84
+ if vm_list[target].nil? || target < 0
85
+ raise InvalidSelectionError.new("Not a valid selection.")
86
+ else
87
+ targetvm = vmserver.server_vm_list[target].to_s
88
+ draw_new_line
89
+ draw_line('#', 80, '', 'white', '')
90
+ draw_line('#', 80, "#{targetvm} is selected for managment", 'white', 'green')
91
+ draw_line('#', 80, '', 'white', '')
92
+ return targetvm
93
+ end
94
+ end
95
+
96
+ def ask_new_vm_name(vmserver)
97
+ draw_line('#', 80, '', 'white', '')
98
+ draw_line('#', 80, "Naming the new virtual machine", 'white', 'green')
99
+ draw_line('#', 80, '', 'white', '')
100
+ draw_new_line
101
+ draw_line('', 80, 'When naming the new virtual machine please use only alpha-numeric', 'white', 'cyan')
102
+ draw_line('', 80, 'characters, dashes and underscores.', 'white', 'cyan')
103
+ draw_new_line
104
+ new_vm_name = cli.ask("\nPlease enter the name of the new virtual machine without spaces:")
105
+ new_vm = Jupiter::Input.new.clean_filename(new_vm_name)
106
+ draw_line('#', 80, '', 'white', '')
107
+ draw_line('#', 80, "The new virtual machine's name will be", 'white', 'cyan')
108
+ draw_line('#', 80, '', 'white', '')
109
+ draw_line(' ', 80, "#{new_vm}", 'white', 'green')
110
+ draw_line('#', 80, '', 'white', '')
111
+ return new_vm
112
+ end
113
+
114
+ def vm_current_state(vmserver, targetvm)
115
+ puts %Q[\nConnecting to VMware host server #{vmserver.name}...]
116
+ puts %Q[\n#{targetvm}'s current state is "#{vmserver.vm_state(targetvm)}"\nNote that this is only valid if VMware Tools is running on the virtual machine.\nIf the kernel has been upgraded recently, you many need to run vmware-config-tools.pl again.]
117
+ return vmserver.vm_state(targetvm)
118
+ end
119
+
120
+ def show_vm_ip(vmserver, vm)
121
+ draw_line('#', 80, '', 'white', '')
122
+ draw_line('#', 80, "#{vm}'s current IP is: #{vmserver.guest_ip(vm)}", 'white', 'green')
123
+ draw_line('#', 80, '', 'white', '')
124
+ draw_new_line
125
+ end
126
+
127
+ def list_virtual_machines(vmserver)
128
+ draw_line('#', 80, '', 'white', '')
129
+ draw_line('#', 80, "These are the currect virtual machines found on #{vmserver.name}", 'white', 'green')
130
+ draw_line('#', 80, '', 'white', '')
131
+ draw_new_line
132
+ vmserver.server_vm_list.each { |x| puts "* #{x}" }
133
+ draw_new_line
134
+ draw_line('#', 80, '', 'white', '')
135
+ end
136
+
137
+ def show_templates_available(templatehost)
138
+ templatevm = ''
139
+ templatelist = templatehost.templates_list
140
+ draw_line('#', 80, '', 'white', '')
141
+ draw_line('#', 80, "These are the currect templates found on #{templatehost.host}", 'white', 'green')
142
+ draw_line('#', 80, '', 'white', '')
143
+ draw_new_line
144
+ templatelist.each { |x| puts "* #{x.gsub('.tar.gz','')}" }
145
+ while templatelist.include?(templatevm + '.tar.gz') == false do
146
+ templatevm = cli.ask("\nPlease enter the valid name of the template you wish to use:")
147
+ end
148
+ return templatevm
149
+ end
150
+
151
+ def draw_new_line
152
+ puts "\n"
153
+ end
154
+
155
+ def draw_line(character, length, text, color1, color2)
156
+ line_minus_text = length - text.length
157
+ half_line = line_minus_text / 2
158
+ line_segment1 = half_line - 1
159
+ line_segment2 = half_line - 1
160
+ line_to_messure = %Q[#{character * line_segment1} #{text} #{character * line_segment2}]
161
+
162
+ line_segment2 += 1 if line_to_messure.length < length
163
+
164
+ if color1.empty? && text.empty?
165
+ output_line = character * length
166
+ elsif color1.empty? && text.length > 0
167
+ output_line = %Q[#{character * line_segment1} #{text} #{character * line_segment2}]
168
+ elsif text.empty?
169
+ output_line = character * length
170
+ output_line = Colorizer.color(color1, output_line)
171
+ else
172
+ line_segment1 = character * line_segment1
173
+ line_segment2 = character * line_segment2
174
+ line_segment1 = Colorizer.color(color1, line_segment1)
175
+ line_segment2 = Colorizer.color(color1, line_segment2)
176
+ text = Colorizer.color(color2, text)
177
+ output_line = %Q[#{line_segment1} #{text} #{line_segment2}]
178
+ end
179
+
180
+ puts output_line
181
+ end
182
+
183
+ def manage_vm(vmserver, options)
184
+ if options[:guest].nil?
185
+ vmname = select_vm_to_manage(vmserver)
186
+ else
187
+ vmname = options[:guest]
188
+ end
189
+ vmstate = vm_current_state(vmserver, vmname)
190
+ return if options[:pretend]
191
+
192
+ if vmstate != 'running'
193
+ power_on = cli.ask("\nWould you like to power on #{vmname}? (yes/no)\n")
194
+ if power_on == 'yes'
195
+ vmserver.power_on_vm(vmname)
196
+ elsif power_on == 'no'
197
+ puts %Q[\nSince #{vmname} is not powered on some options will not be available.\n]
198
+ else
199
+ puts %Q[\nNot a valid response. Exiting...\n]
200
+ return
201
+ end
202
+ end
203
+ draw_new_line
204
+ draw_line('#', 80, '', 'white', '')
205
+ draw_line('#', 80, "Please select a task to perform on #{vmname}", 'white', 'green')
206
+ draw_line('#', 80, '', 'white', '')
207
+ draw_new_line
208
+ puts %Q[ 1. Change hostname\n]
209
+ puts %Q[ 2. Reboot (graceful restart)\n]
210
+ puts %Q[ 3. Shutdown (graceful stop)\n]
211
+ puts %Q[ 4. Cycle Power (non-graceful restart)\n]
212
+ puts %Q[ 5. Kill Power (non-graceful)\n]
213
+ puts %Q[ 6. Remove deploy user from sudoers\n]
214
+ puts %Q[ 7. Display current IP address of #{vmname}\n]
215
+ puts %Q[ 8. Increase the alocated HD space for #{vmname} (VM must be off)\n]
216
+ puts %Q[ 9. Grow the LVM partition to fill the HD on #{vmname}\n]
217
+ puts %Q[ 10. Generate Nagios config file for #{vmname}\n]
218
+ choice = cli.ask("\n Enter Choice:").to_i
219
+
220
+ case choice
221
+ when 1
222
+ newname = cli.ask("\nPlease enter a valid hostname for the new virtual machine (lowercase with no spaces or special characters): ")
223
+ new_hostname = Jupiter::Input.new.clean_filename(newname)
224
+ draw_line('#', 80, '', 'white', '')
225
+ draw_line('#', 80, "The new hostname will be", 'white', 'cyan')
226
+ draw_line('#', 80, '', 'white', '')
227
+ draw_line(' ', 80, "#{new_hostname}", 'white', 'green')
228
+ draw_line('#', 80, '', 'white', '')
229
+ ip = vmserver.guest_ip(vmname)
230
+ vm = Jupiter::VirtualMachine.new(ip, 2500)
231
+ puts vm.rename_linux_guest(new_hostname)
232
+ manage_vm(vmserver, options)
233
+ when 2
234
+ puts %Q[\nSending reboot command to #{vmname}...\n]
235
+ vmserver.reboot_vm(vmname)
236
+ when 3
237
+ puts %Q[\nSending shutdown command to #{vmname}...\n]
238
+ vmserver.shutdown_vm(vmname)
239
+ when 4
240
+ cycle = cli.ask("\nAre you sure? Only use this option if the system is locked up. Otherwise use the standard restart options for the guest OS. (yes / no): ")
241
+ if cycle == 'yes'
242
+ puts %Q[\nCycling the power for #{vmname}...\n]
243
+ vmserver.power_cycle_vm(vmname)
244
+ else
245
+ puts %[\nExiting...\n]
246
+ return
247
+ end
248
+ when 5
249
+ kill = cli.ask("\nAre you sure? (yes / no): ")
250
+ if kill == 'yes'
251
+ puts %Q[\nKilling power for #{vmname}...\n]
252
+ vmserver.power_off_vm(vmname)
253
+ else
254
+ puts %[\nExiting...\n]
255
+ return
256
+ end
257
+ when 6
258
+ ip = vmserver.guest_ip(vmname)
259
+ vm = Jupiter::VirtualMachine.new(ip, 2500)
260
+ puts vm.remove_from_sudo_group('deploy')
261
+ manage_vm(vmserver, options)
262
+ when 7
263
+ puts vmserver.guest_ip(vmname)
264
+ manage_vm(vmserver, options)
265
+ when 8
266
+ current_size = vmserver.vmdk_size(vmname)
267
+ puts %Q[\nThe current HD size is #{current_size} GB.\n]
268
+ size_in_gb = cli.ask(%Q[\nPlease enter the new size for the virtual HD in gigabytes: ])
269
+ vmserver.resize_vmdk(vmname, size_in_gb)
270
+ new_size = vmserver.vmdk_size(vmname)
271
+ puts %Q[\nThe new HD size is #{new_size} GB.\n]
272
+ manage_vm(vmserver, options)
273
+ when 9
274
+ ip = vmserver.guest_ip(vmname)
275
+ vm = Jupiter::VirtualMachine.new(ip, 2500)
276
+ puts vm.resize_lvm_volume_step_1
277
+ vmserver.reboot_vm(vmname)
278
+ sleep 45
279
+ puts vm.resize_lvm_volume_step_2
280
+ manage_vm(vmserver, options)
281
+ when 10
282
+ draw_new_line
283
+ draw_line('#', 80, '', 'white', '')
284
+ draw_line('#', 80, "Nagios File Generation", 'white', 'cyan')
285
+ draw_line('#', 80, '', 'white', '')
286
+ draw_line(' ', 80, "This will generate a Nagios object entry that can be pasted into", 'white', 'green')
287
+ draw_line(' ', 80, "your nagios config or uploaded to your nagios server if a proper", 'white', 'green')
288
+ draw_line(' ', 80, "nagios server entry is in your Jupiter yaml file.", 'white', 'green')
289
+ draw_new_line
290
+ draw_line(' ', 80, "This will also only be valid if your system has a static IP address and", 'white', 'green')
291
+ draw_line(' ', 80, "the services will only be functional if the nagios client is installed.", 'white', 'green')
292
+ draw_line('#', 80, '', 'white', '')
293
+ nagios_choice = cli.ask("\n Enter 1 to display Nagios entry or 2 to upload to Nagios Server:").to_i
294
+ nagios = Jupiter::Nagios.new
295
+ nagios.action(nagios_choice, vmname, vmname, vmserver.guest_ip(vmname))
296
+ manage_vm(vmserver, options)
297
+ else
298
+ puts %[\nInvalid choice. Exiting...\n]
299
+ return
300
+ end
301
+ end
302
+
303
+ def manage_host(vmserver, options)
304
+
305
+ list_virtual_machines(vmserver)
306
+ clone_from = ask_how_to_clone
307
+
308
+ if clone_from == '1'
309
+ targetvm = ask_vm_to_clone(vmserver)
310
+ new_vm_name = ask_new_vm_name(vmserver)
311
+ vm_current_state(vmserver, targetvm)
312
+ return if options[:pretend]
313
+
314
+ puts %Q[\nStarting cloning of #{targetvm} to a new virtual machine called #{new_vm_name}. This may take some time. Please wait...]
315
+ time = Benchmark.realtime {
316
+ vmserver.clone_vm(targetvm, new_vm_name)
317
+ }
318
+ puts %Q[\nFinished cloning.]
319
+ puts %Q[\nElapses time: #{time}\n]
320
+ puts %Q[\nNow booting the new VM...\n]
321
+ sleep 10
322
+
323
+ show_vm_ip(vmserver, new_vm_name)
324
+
325
+ newname = cli.ask("\n#{new_vm_name} is the name used by the ESX server to reference the VM but we need to also change the VM's actually hostname.\nPlease enter a valid hostname for the new virtual machine: (lowercase with no spaces or special characters)")
326
+ ip = vmserver.guest_ip(new_vm_name)
327
+ vm = Jupiter::VirtualMachine.new(ip, 2500)
328
+ puts vm.rename_linux_guest(newname)
329
+ draw_line('#', 80, '', 'white', '')
330
+
331
+ elsif clone_from == '2'
332
+ templatehost = Jupiter::Template.new(vmserver)
333
+ templatevm = show_templates_available(templatehost)
334
+ new_vm_name = ask_new_vm_name(vmserver)
335
+ templatelist = templatehost.templates_list
336
+ raise InvalidNameError.new("#{new_vm_name} is already taken as a name.") if templatelist.include?(new_vm_name)
337
+ puts %Q[\nConnecting to template share on #{templatehost.host} to transfer template.]
338
+ puts %Q[The transfer may take some time depending on the file size and bandwidth...]
339
+ return if options[:pretend]
340
+ puts templatehost.transfer_template(templatevm, new_vm_name)
341
+ sleep 10 # Give time for new VM to spinup before offering managment menu
342
+ manage_vm(vmserver, options)
343
+ else
344
+ puts %Q[\n"#{clone_from}" is not a valid option. Exiting...\n]
345
+ return
346
+ end
347
+ end
348
+
349
+ class InvalidNameError < StandardError; end
350
+ class InvalidSelectionError < StandardError; end
351
+
352
+ end
353
+ end
354
+
355
+ end