jupiter 0.0.1 → 0.8.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.
@@ -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