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,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
+