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,18 @@
1
+ module Jupiter
2
+ class Configuration
3
+ attr_accessor :nagios, :hosts, :vmusers, :templates
4
+
5
+ def self.load_from_yaml!
6
+ config = YAML.load_file('config.yaml')
7
+ new(config)
8
+ end
9
+
10
+ def initialize(options = {})
11
+ @hosts = options.fetch(:hosts) {[]}
12
+ @vmusers = options.fetch(:vmusers) { {} }
13
+ @templates = options.fetch(:templates) { {} }
14
+ @nagios = options.fetch(:nagios) { {} }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,257 @@
1
+ module Jupiter
2
+ class Host
3
+
4
+ attr_accessor :name, :vmpath, :host, :sshport, :sshuser, :sshpass, :dcname, :timestamp
5
+
6
+ def initialize(options)
7
+ self.name = options.fetch(:name)
8
+ self.vmpath = options.fetch(:vmpath)
9
+ self.host = options.fetch(:host)
10
+ self.sshport = options.fetch(:sshport)
11
+ self.sshuser = options.fetch(:sshuser)
12
+ self.sshpass = options.fetch(:sshpass)
13
+ self.dcname = options.fetch(:dcname)
14
+ self.timestamp = Time.new.strftime('%Y-%m-%d-%H%M').freeze
15
+ end
16
+
17
+ def server_uptime
18
+ puts ssh.exec!("uptime")
19
+ close_ssh
20
+ end
21
+
22
+ def server_vm_list
23
+ vm_list = ssh.exec!("ls #{vmpath}").to_s
24
+ close_ssh
25
+ vm_list.split("\n")
26
+ end
27
+
28
+ def ssh
29
+ @ssh ||= Net::SSH.start(host, sshuser, password: sshpass, port: sshport)
30
+ end
31
+
32
+ def close_ssh
33
+ ssh.close
34
+ @ssh = nil
35
+ true
36
+ end
37
+
38
+ def vmhost
39
+ if /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.match(host)
40
+ vim = RbVmomi::VIM.connect host: host, user: sshuser, password: sshpass, insecure: true
41
+ dc = vim.serviceInstance.find_datacenter(dcname) or fail "datacenter not found"
42
+ else
43
+ return false
44
+ end
45
+ end
46
+
47
+ def guestvm(vmname)
48
+ vm = vmhost.find_vm(vmname) or fail "\n\nVirtual Machine was not found. Although its files might exist, it may not be registered on the server. You can register the VM with the following command:\n\njupiter register --guest=#{vmname} --host=#{name}\n\n"
49
+ end
50
+
51
+ def guest_ip(vm)
52
+ result = guestvm(vm).guest_ip
53
+ end
54
+
55
+ def vm_state(vm)
56
+ result = guestvm(vm).guest.guestState
57
+ end
58
+
59
+ def snapshot_create(vm)
60
+ result = guestvm(vm).CreateSnapshot_Task(name: "snapshot_#{timestamp}", memory: false, quiesce: true)
61
+ end
62
+
63
+ def snapshots_remove(vm)
64
+ result = guestvm(vm).RemoveAllSnapshots_Task
65
+ end
66
+
67
+ def consolidate_snapshots(vm)
68
+ result = guestvm(vm).ConsolidateVMDisks_Task
69
+ end
70
+
71
+ def register_vm(vm)
72
+ ssh.exec!("vim-cmd solo/registervm #{vmpath}/#{vm}/#{vm}.vmx")
73
+ snapshots_remove(vm)
74
+ end
75
+
76
+ def unregister_vm(vm)
77
+ ssh.exec!("vim-cmd vmsvc/unregister #{vmpath}/#{vm}/#{vm}.vmx")
78
+ end
79
+
80
+ def power_on_vm(vm)
81
+ result = guestvm(vm).PowerOnVM_Task
82
+ end
83
+
84
+ def power_off_vm(vm)
85
+ result = guestvm(vm).PowerOffVM_Task
86
+ end
87
+
88
+ def power_cycle_vm(vm)
89
+ guestvm(vm).PowerOffVM_Task
90
+ sleep 5
91
+ guestvm(vm).PowerOnVM_Task
92
+ end
93
+
94
+ def reboot_vm(vm)
95
+ result = guestvm(vm).RebootGuest
96
+ end
97
+
98
+ def shutdown_vm(vm)
99
+ result = guestvm(vm).ShutdownGuest
100
+ end
101
+
102
+ def clone_vm(vm, newvm)
103
+ snapshot_create(vm)
104
+ sleep 5
105
+ ssh.exec!("mkdir #{vmpath}/#{newvm}")
106
+ ssh.exec!("cp -Rfv #{vmpath}/#{vm}/* #{vmpath}/#{newvm}")
107
+ rename_cp_files(vm, newvm)
108
+ update_vmx(vm, newvm)
109
+ update_vmxf(vm, newvm)
110
+ update_vmdk(vm, newvm)
111
+ register_vm(newvm)
112
+ close_ssh
113
+ snapshots_remove(vm)
114
+ consolidate_snapshots(vm)
115
+ sleep 5
116
+ power_on_vm(newvm)
117
+ sleep 5
118
+ answer_vm_question(newvm) # Needed to answer the "Was the VM moved" question that pops up.
119
+ sleep 5
120
+ power_on_vm(newvm)
121
+ end
122
+
123
+ def rename_cp_files(vm, newvm)
124
+ old_files_path = "#{vmpath}/#{newvm}/#{vm}"
125
+ new_files_path = "#{vmpath}/#{newvm}/#{newvm}"
126
+ ssh.exec!("rm -f #{vmpath}/#{newvm}/vmware-*.log")
127
+ ssh.exec!("rm -f #{old_files_path}.vmx~")
128
+ ssh.exec!("rm -f #{old_files_path}.vmsd")
129
+ ssh.exec!("rm -f #{old_files_path}-*.vmsn")
130
+ ssh.exec!("rm -f #{old_files_path}-0000*.vmdk")
131
+ ssh.exec!("mv #{old_files_path}-aux.xml #{new_files_path}-aux.xml")
132
+ ssh.exec!("mv #{old_files_path}-flat.vmdk #{new_files_path}-flat.vmdk")
133
+ ssh.exec!("mv #{old_files_path}.nvram #{new_files_path}.nvram")
134
+ ssh.exec!("mv #{old_files_path}.vmdk #{new_files_path}.vmdk")
135
+ ssh.exec!("mv #{old_files_path}.vmx #{new_files_path}.vmx")
136
+ ssh.exec!("mv #{old_files_path}.vmxf #{new_files_path}.vmxf")
137
+ end
138
+
139
+ def change_config_value(key, value)
140
+ self[key] = value
141
+ return self
142
+ end
143
+
144
+ def read_config_to_hash(file)
145
+ file_contents = ssh.exec!(%Q[cat #{file}])
146
+ Hash[file_contents.split("\n").map { |line| line.split('=').map(&:strip).map { |value| value.gsub(/\A"|"\z/, '') } }]
147
+ end
148
+
149
+ def uuid(newvm)
150
+ @uuid ||= Jupiter::UUID.new.generate(newvm)
151
+ end
152
+
153
+ def flatten_hash(hash_to_flatten)
154
+ hash_to_flatten.map{|k,v| "#{k} = \"#{v}\""}.join("\n")
155
+ end
156
+
157
+ def write_to_file(file_content, full_path_to_file)
158
+ ssh.exec!(%Q[echo '#{file_content}' > #{full_path_to_file}])
159
+ end
160
+
161
+ def generate_mac_address
162
+ (1..6).map{"%0.2X"%rand(256)}.join(":")
163
+ end
164
+
165
+ def update_vmx(vm, newvm)
166
+ vmx_hash = read_config_to_hash("#{vmpath}/#{newvm}/#{newvm}.vmx")
167
+ vmx_hash['ethernet0.generatedAddress'] = generate_mac_address
168
+ vmx_hash['uuid.bios'] = uuid(newvm)
169
+ vmx_hash['uuid.location'] = uuid(newvm)
170
+ vmx_hash['displayName'] = newvm
171
+ vmx_hash['nvram'] = "#{newvm}.nvram"
172
+ vmx_hash['extendedConfigFile'] = "#{newvm}.vmxf"
173
+ vmx_hash['scsi0:0.fileName'] = "#{newvm}.vmdk"
174
+ swap_derived_name = vmx_hash['sched.swap.derivedName']
175
+ swap_derived_name["#{vm}/#{vm}"]= "#{newvm}/#{newvm}"
176
+ vmx_hash['sched.swap.derivedName'] = swap_derived_name
177
+ write_to_file(flatten_hash(vmx_hash), "#{vmpath}/#{newvm}/#{newvm}.vmx")
178
+ return true
179
+ end
180
+
181
+ def update_vmxf(vm, newvm)
182
+ file_with_path = "#{vmpath}/#{newvm}/#{newvm}.vmxf"
183
+ newuuid = uuid(newvm)
184
+ file_contents = "<?xml version=\"1.0\"?>\n<Foundry>\n<VM>\n<VMId type=\"string\">#{newuuid}</VMId>\n<ClientMetaData>\n<clientMetaDataAttributes/>\n<HistoryEventList/></ClientMetaData>\n<vmxPathName type=\"string\">#{newvm}.vmx</vmxPathName></VM></Foundry>"
185
+ write_to_file(file_contents, file_with_path)
186
+ return true
187
+ end
188
+
189
+ def update_vmdk(vm, newvm)
190
+ file_with_path = "#{vmpath}/#{newvm}/#{newvm}.vmdk"
191
+ file_contents = ssh.exec!(%Q[cat #{file_with_path}])
192
+ file_contents[vm]= newvm
193
+ write_to_file(file_contents, file_with_path)
194
+ return true
195
+ end
196
+
197
+ def vmdk_size(vm)
198
+ file_with_path = "#{vmpath}/#{vm}/#{vm}.vmdk"
199
+ vmdk_contents = ssh.exec!(%Q[cat #{file_with_path}])
200
+ close_ssh
201
+ vmfs_sectors = vmdk_contents[/\RW(.*?)VMFS/,1]
202
+ gb_size = ((vmfs_sectors.to_f * 512) / (1024 * 1024 * 1024))
203
+ end
204
+
205
+ def resize_vmdk(vm, size_in_GB)
206
+ size_in_GB.tr!('^.0-9', '')
207
+ raise InvalidGBSizeError.new("You can only increase the size of the drive, not reduce it.") if vmdk_size(vm).to_f > size_in_GB.to_f
208
+ raise InvalidVMStateError.new("#{vm} must be shutdown before the drive can be resized.") if vm_state(vm) == 'running'
209
+ result = ssh.exec!(%Q[vmkfstools -X #{size_in_GB}G #{vmpath}/#{vm}/#{vm}.vmdk])
210
+ close_ssh
211
+ return result
212
+ end
213
+
214
+ def answer_vm_question(vm)
215
+ result = guestvm(vm).AnswerVM(questionId: '_vmx1', answerChoice: '2')
216
+ end
217
+
218
+ def setup_template(original_name, new_name)
219
+ extract_template(original_name, new_name)
220
+ rename_copied_template(original_name, new_name)
221
+ update_vmx(original_name, new_name)
222
+ update_vmxf(original_name, new_name)
223
+ update_vmdk(original_name, new_name)
224
+ register_vm(new_name)
225
+ close_ssh
226
+ sleep 5
227
+ power_on_vm(new_name)
228
+ sleep 5
229
+ answer_vm_question(new_name) # Needed to answer the "Was the VM moved" question that pops up.
230
+ sleep 5
231
+ power_on_vm(new_name)
232
+ end
233
+
234
+ def extract_template(original_name, new_name)
235
+ file_exists = ssh.exec!("find #{vmpath}/#{original_name}.tar.gz").chomp
236
+ if file_exists == "#{vmpath}/#{original_name}.tar.gz"
237
+ ssh.exec!("cd #{vmpath} && tar -zxvf #{original_name}.tar.gz && rm #{original_name}.tar.gz")
238
+ else
239
+ close_ssh
240
+ return false
241
+ end
242
+ end
243
+
244
+ def rename_copied_template(original_name, new_name)
245
+ if !new_name.nil? && !original_name.nil?
246
+ ssh.exec!("mv #{vmpath}/#{original_name} #{vmpath}/#{new_name}")
247
+ rename_cp_files(original_name, new_name)
248
+ else
249
+ return false
250
+ end
251
+ end
252
+
253
+ class InvalidVMStateError < StandardError; end
254
+ class InvalidGBSizeError < StandardError; end
255
+
256
+ end
257
+ end
@@ -0,0 +1,188 @@
1
+ module Jupiter
2
+ class TextTemplate
3
+
4
+ def initialize( template )
5
+ @template = template.clone()
6
+ @values = {}
7
+ end
8
+
9
+ def set( name, value )
10
+ @values[ name ] = value
11
+ end
12
+
13
+ def run()
14
+ @template.gsub( /:::(.*?):::/ ) {
15
+ raise "Key '#{$1}' found in template but the value has not been set" unless ( @values.has_key?( $1 ) )
16
+ @values[ $1 ].to_s
17
+ }
18
+ end
19
+
20
+ def to_s()
21
+ run()
22
+ end
23
+
24
+ end
25
+
26
+ class Nagios
27
+
28
+ def initialize
29
+ @url = Jupiter.nagios.fetch(:url)
30
+ @location = Jupiter.nagios.fetch(:location)
31
+ @sshuser = Jupiter.nagios.fetch(:sshuser)
32
+ @sshpass = Jupiter.nagios.fetch(:sshpass)
33
+ @sshport = Jupiter.nagios.fetch(:sshport)
34
+ @host = Jupiter.nagios.fetch(:host)
35
+ end
36
+
37
+ def ask(question)
38
+ print question + " "
39
+ answer = STDIN.gets.chomp
40
+ if answer == "" or answer.nil?
41
+ return nil
42
+ else
43
+ return answer
44
+ end
45
+ end
46
+
47
+ def host_template(machines_name, machines_alias, machines_ip)
48
+ template_host = Jupiter::TextTemplate.new(
49
+ "\ndefine host {"\
50
+ "\n use linux-server"\
51
+ "\n host_name :::HOSTNAME:::"\
52
+ "\n alias :::HOSTALIAS:::"\
53
+ "\n address :::IPADDRESS:::"\
54
+ "\n statusmap_image linux40.gd2"\
55
+ "\n parents GATEWAY1"\
56
+ "\n}\n"
57
+ )
58
+ template_host.set( 'HOSTNAME', machines_name)
59
+ template_host.set( 'HOSTALIAS', machines_alias)
60
+ template_host.set( 'IPADDRESS', machines_ip)
61
+
62
+ return template_host
63
+ end
64
+
65
+ def services_template(machines_name)
66
+ template_services = Jupiter::TextTemplate.new(
67
+ "\ndefine service {"\
68
+ "\n use local-service"\
69
+ "\n host_name :::HOSTNAME:::"\
70
+ "\n service_description Root Partition"\
71
+ "\n check_command check_local_disk!20%!10%!/"\
72
+ "\n notifications_enabled 1"\
73
+ "\n}\n"\
74
+ "\ndefine service{"\
75
+ "\n use local-service"\
76
+ "\n host_name :::HOSTNAME:::"\
77
+ "\n service_description Current Users"\
78
+ "\n check_command check_local_users!20!50"\
79
+ "\n notifications_enabled 1"\
80
+ "\n}\n"\
81
+ "\ndefine service{"\
82
+ "\n use local-service"\
83
+ "\n host_name :::HOSTNAME:::"\
84
+ "\n service_description Total Processes"\
85
+ "\n check_command check_local_procs!250!400!RSZDT"\
86
+ "\n notifications_enabled 1"\
87
+ "\n}\n"\
88
+ "\ndefine service{"\
89
+ "\n use local-service"\
90
+ "\n host_name :::HOSTNAME:::"\
91
+ "\n service_description Current Load"\
92
+ "\n check_command check_local_load!5.0,4.0,3.0!10.0,6.0,4.0"\
93
+ "\n notifications_enabled 1"\
94
+ "\n}\n"\
95
+ "\ndefine service{"\
96
+ "\n use local-service"\
97
+ "\n host_name :::HOSTNAME:::"\
98
+ "\n service_description Swap Usage"\
99
+ "\n check_command check_local_swap!20!10"\
100
+ "\n notifications_enabled 1"\
101
+ "\n}\n"\
102
+ "\ndefine service{"\
103
+ "\n use local-service"\
104
+ "\n host_name :::HOSTNAME:::"\
105
+ "\n service_description SSH"\
106
+ "\n check_command check_ssh!-p 2500"\
107
+ "\n notifications_enabled 0"\
108
+ "\n}\n"\
109
+ "\ndefine service{"\
110
+ "\n use local-service"\
111
+ "\n host_name :::HOSTNAME:::"\
112
+ "\n service_description HTTP"\
113
+ "\n check_command check_http"\
114
+ "\n notifications_enabled 0"\
115
+ "\n}"\
116
+ )
117
+ template_services.set( 'HOSTNAME', machines_name)
118
+
119
+ return template_services
120
+ end
121
+
122
+ def action(choice, machines_name, machines_alias, machines_ip)
123
+ if (choice == 1)
124
+ include_services = ask("\nWould you like to include default services? (yes / no): ")
125
+ if (include_services == 'yes')
126
+ puts "\n\n\n"
127
+ puts generate_template(machines_name, machines_alias, machines_ip, 1)
128
+ puts "\n\n\n"
129
+ end
130
+ if (include_services == 'no')
131
+ puts "\n\n\n"
132
+ puts generate_template(machines_name, machines_alias, machines_ip, 0)
133
+ puts "\n\n\n"
134
+ end
135
+ elsif (choice == 2)
136
+ include_services = ask("\nWould you like to include default services? (yes / no): ")
137
+ if (include_services == 'yes')
138
+ puts "\n\n\n"
139
+ upload_nagios_config(machines_name, machines_alias, machines_ip, 1)
140
+ puts "Completed adding nagios config for #{machines_name}. Please restart Nagios to see changes."
141
+ puts "\n\n\n"
142
+ end
143
+ if (include_services == 'no')
144
+ puts "\n\n\n"
145
+ upload_nagios_config(machines_name, machines_alias, machines_ip, 0)
146
+ puts "Added Nagios configuration for #{machines_name}. Please restart Nagios to see changes."
147
+ puts "\n\n\n"
148
+ end
149
+ else
150
+ raise InvalidSelectionError.new("Not a valid selection.")
151
+ end
152
+ end
153
+
154
+ def generate_template(machines_name, machines_alias, machines_ip, services)
155
+ host = host_template(machines_name, machines_alias, machines_ip)
156
+ serv = services_template(machines_name)
157
+ if (services == 1)
158
+ template = "########## GENERATED BY JUPITER ##########\n#{host}#{serv}\n\n########## GENERATED BY JUPITER ##########"
159
+ else
160
+ template = "########## GENERATED BY JUPITER ##########\n#{host}\n\n########## GENERATED BY JUPITER ##########"
161
+ end
162
+ return template
163
+ end
164
+
165
+ def upload_nagios_config(machines_name, machines_alias, machines_ip, services)
166
+ template = generate_template(machines_name, machines_alias, machines_ip, services)
167
+ puts template
168
+ puts ssh.exec!(%Q[echo "#{template}" > #{@location}/#{machines_name}.cfg])
169
+ close_ssh
170
+ end
171
+
172
+ def ssh
173
+ @ssh ||= Net::SSH.start(@host, @sshuser, password: @sshpass, port: @sshport)
174
+ end
175
+
176
+ def close_ssh
177
+ ssh.close
178
+ @ssh = nil
179
+ true
180
+ end
181
+
182
+ end
183
+
184
+ class InvalidSelectionError < StandardError; end
185
+
186
+ end
187
+
188
+