bosh_agent 1.5.0.pre.1113
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/CHANGELOG +0 -0
- data/bin/bosh_agent +102 -0
- data/lib/bosh_agent/alert.rb +191 -0
- data/lib/bosh_agent/alert_processor.rb +96 -0
- data/lib/bosh_agent/apply_plan/helpers.rb +30 -0
- data/lib/bosh_agent/apply_plan/job.rb +235 -0
- data/lib/bosh_agent/apply_plan/package.rb +58 -0
- data/lib/bosh_agent/apply_plan/plan.rb +96 -0
- data/lib/bosh_agent/bootstrap.rb +341 -0
- data/lib/bosh_agent/config.rb +5 -0
- data/lib/bosh_agent/configuration.rb +102 -0
- data/lib/bosh_agent/disk_util.rb +103 -0
- data/lib/bosh_agent/errors.rb +25 -0
- data/lib/bosh_agent/ext.rb +48 -0
- data/lib/bosh_agent/file_aggregator.rb +78 -0
- data/lib/bosh_agent/file_matcher.rb +45 -0
- data/lib/bosh_agent/handler.rb +440 -0
- data/lib/bosh_agent/heartbeat.rb +74 -0
- data/lib/bosh_agent/heartbeat_processor.rb +45 -0
- data/lib/bosh_agent/http_handler.rb +135 -0
- data/lib/bosh_agent/infrastructure/aws/registry.rb +177 -0
- data/lib/bosh_agent/infrastructure/aws/settings.rb +59 -0
- data/lib/bosh_agent/infrastructure/aws.rb +17 -0
- data/lib/bosh_agent/infrastructure/dummy.rb +24 -0
- data/lib/bosh_agent/infrastructure/openstack/registry.rb +220 -0
- data/lib/bosh_agent/infrastructure/openstack/settings.rb +76 -0
- data/lib/bosh_agent/infrastructure/openstack.rb +17 -0
- data/lib/bosh_agent/infrastructure/vsphere/settings.rb +135 -0
- data/lib/bosh_agent/infrastructure/vsphere.rb +16 -0
- data/lib/bosh_agent/infrastructure.rb +25 -0
- data/lib/bosh_agent/message/apply.rb +184 -0
- data/lib/bosh_agent/message/base.rb +38 -0
- data/lib/bosh_agent/message/compile_package.rb +250 -0
- data/lib/bosh_agent/message/drain.rb +195 -0
- data/lib/bosh_agent/message/list_disk.rb +25 -0
- data/lib/bosh_agent/message/logs.rb +108 -0
- data/lib/bosh_agent/message/migrate_disk.rb +55 -0
- data/lib/bosh_agent/message/mount_disk.rb +102 -0
- data/lib/bosh_agent/message/ssh.rb +109 -0
- data/lib/bosh_agent/message/state.rb +47 -0
- data/lib/bosh_agent/message/unmount_disk.rb +29 -0
- data/lib/bosh_agent/monit.rb +354 -0
- data/lib/bosh_agent/monit_client.rb +158 -0
- data/lib/bosh_agent/mounter.rb +42 -0
- data/lib/bosh_agent/ntp.rb +32 -0
- data/lib/bosh_agent/platform/centos/disk.rb +27 -0
- data/lib/bosh_agent/platform/centos/network.rb +39 -0
- data/lib/bosh_agent/platform/centos/templates/centos-ifcfg.erb +9 -0
- data/lib/bosh_agent/platform/centos/templates/dhclient_conf.erb +56 -0
- data/lib/bosh_agent/platform/centos/templates/logrotate.erb +8 -0
- data/lib/bosh_agent/platform/centos.rb +4 -0
- data/lib/bosh_agent/platform/dummy/templates/dummy_template.erb +1 -0
- data/lib/bosh_agent/platform/linux/adapter.rb +36 -0
- data/lib/bosh_agent/platform/linux/disk.rb +121 -0
- data/lib/bosh_agent/platform/linux/logrotate.rb +32 -0
- data/lib/bosh_agent/platform/linux/network.rb +124 -0
- data/lib/bosh_agent/platform/linux/password.rb +22 -0
- data/lib/bosh_agent/platform/linux.rb +4 -0
- data/lib/bosh_agent/platform/ubuntu/network.rb +59 -0
- data/lib/bosh_agent/platform/ubuntu/templates/dhclient_conf.erb +56 -0
- data/lib/bosh_agent/platform/ubuntu/templates/interfaces.erb +14 -0
- data/lib/bosh_agent/platform/ubuntu/templates/logrotate.erb +8 -0
- data/lib/bosh_agent/platform/ubuntu.rb +4 -0
- data/lib/bosh_agent/platform.rb +26 -0
- data/lib/bosh_agent/remote_exception.rb +62 -0
- data/lib/bosh_agent/runner.rb +36 -0
- data/lib/bosh_agent/settings.rb +61 -0
- data/lib/bosh_agent/sigar_box.rb +26 -0
- data/lib/bosh_agent/smtp_server.rb +96 -0
- data/lib/bosh_agent/state.rb +100 -0
- data/lib/bosh_agent/syslog_monitor.rb +53 -0
- data/lib/bosh_agent/template.rb +50 -0
- data/lib/bosh_agent/util.rb +190 -0
- data/lib/bosh_agent/version.rb +8 -0
- data/lib/bosh_agent.rb +92 -0
- metadata +332 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
module ApplyPlan
|
5
|
+
class Package
|
6
|
+
|
7
|
+
class InstallationError < StandardError; end
|
8
|
+
|
9
|
+
include ApplyPlan::Helpers
|
10
|
+
|
11
|
+
attr_reader :install_path
|
12
|
+
attr_reader :link_path
|
13
|
+
|
14
|
+
def initialize(spec)
|
15
|
+
validate_spec(spec)
|
16
|
+
|
17
|
+
@base_dir = Bosh::Agent::Config.base_dir
|
18
|
+
@name = spec['name']
|
19
|
+
@version = spec['version']
|
20
|
+
@checksum = spec['sha1']
|
21
|
+
@blobstore_id = spec['blobstore_id']
|
22
|
+
|
23
|
+
@install_path = File.join(@base_dir, 'data', 'packages',
|
24
|
+
@name, @version)
|
25
|
+
@link_path = File.join(@base_dir, 'packages', @name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def install_for_job(job)
|
29
|
+
unless @installed_for_sys
|
30
|
+
fetch_bits
|
31
|
+
@installed_for_sys = true
|
32
|
+
end
|
33
|
+
create_symlink_in_job(job) if job
|
34
|
+
rescue SystemCallError => e
|
35
|
+
install_failed("System call error: #{e.message}")
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def create_symlink_in_job(job)
|
41
|
+
symlink_path = symlink_path_in_job(job)
|
42
|
+
FileUtils.mkdir_p(File.dirname(symlink_path))
|
43
|
+
|
44
|
+
Bosh::Agent::Util.create_symlink(@install_path, symlink_path)
|
45
|
+
end
|
46
|
+
|
47
|
+
def symlink_path_in_job(job)
|
48
|
+
File.join(job.install_path, 'packages', @name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def install_failed(message)
|
52
|
+
raise InstallationError, 'Failed to install package ' +
|
53
|
+
"'#{@name}': #{message}"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
module ApplyPlan
|
5
|
+
class Plan
|
6
|
+
|
7
|
+
attr_reader :deployment
|
8
|
+
attr_reader :jobs
|
9
|
+
attr_reader :packages
|
10
|
+
|
11
|
+
def initialize(spec)
|
12
|
+
unless spec.is_a?(Hash)
|
13
|
+
raise ArgumentError, "Invalid spec format, Hash expected, " +
|
14
|
+
"#{spec.class} given"
|
15
|
+
end
|
16
|
+
|
17
|
+
@spec = spec
|
18
|
+
@deployment = spec["deployment"]
|
19
|
+
@jobs = []
|
20
|
+
@packages = []
|
21
|
+
@config_binding = Bosh::Agent::Util.config_binding(spec)
|
22
|
+
|
23
|
+
job_spec = spec["job"]
|
24
|
+
package_specs = spec["packages"]
|
25
|
+
|
26
|
+
# By default stemcell VM has '' as job
|
27
|
+
# in state.yml, handling this very special case
|
28
|
+
if job_spec && job_spec != ""
|
29
|
+
job_name = job_spec["name"]
|
30
|
+
if is_legacy_spec?(job_spec)
|
31
|
+
@jobs << Job.new(job_name, job_spec["template"], job_spec,
|
32
|
+
@config_binding)
|
33
|
+
else
|
34
|
+
job_spec["templates"].each do |template_spec|
|
35
|
+
@jobs << Job.new(job_name, template_spec["name"], template_spec,
|
36
|
+
@config_binding)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if package_specs
|
42
|
+
unless package_specs.is_a?(Hash)
|
43
|
+
raise ArgumentError, "Invalid package specs format " +
|
44
|
+
"in apply spec, Hash expected " +
|
45
|
+
"#{package_specs.class} given"
|
46
|
+
end
|
47
|
+
|
48
|
+
package_specs.each_pair do |package_name, package_spec|
|
49
|
+
@packages << Package.new(package_spec)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def is_legacy_spec?(job_spec)
|
55
|
+
return job_spec["template"] && !job_spec["templates"]
|
56
|
+
end
|
57
|
+
|
58
|
+
def has_jobs?
|
59
|
+
!@jobs.empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
def has_packages?
|
63
|
+
!@packages.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
def configured?
|
67
|
+
@spec.key?("configuration_hash")
|
68
|
+
end
|
69
|
+
|
70
|
+
def install_jobs
|
71
|
+
@jobs.each do |job|
|
72
|
+
job.install
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def install_packages
|
77
|
+
@jobs.each do |job|
|
78
|
+
@packages.each do |package|
|
79
|
+
package.install_for_job(job)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Configure the 1+ job templates (job colocation)
|
85
|
+
# They are reversed for the purposes of ensuring monit
|
86
|
+
# starts them in the order that they are specified
|
87
|
+
# in the original deployment manifest
|
88
|
+
def configure_jobs
|
89
|
+
@jobs.reverse.each_with_index do |job, job_index|
|
90
|
+
job.configure(job_index)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,341 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'netaddr'
|
5
|
+
require 'erb'
|
6
|
+
require 'tempfile'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'pathname'
|
9
|
+
require 'openssl'
|
10
|
+
|
11
|
+
module Bosh::Agent
|
12
|
+
class Bootstrap
|
13
|
+
include Bosh::Exec
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
FileUtils.mkdir_p(File.join(base_dir, 'bosh'))
|
17
|
+
@platform = Bosh::Agent::Config.platform
|
18
|
+
end
|
19
|
+
|
20
|
+
def logger
|
21
|
+
Bosh::Agent::Config.logger
|
22
|
+
end
|
23
|
+
|
24
|
+
def base_dir
|
25
|
+
Bosh::Agent::Config.base_dir
|
26
|
+
end
|
27
|
+
|
28
|
+
def store_path
|
29
|
+
File.join(base_dir, 'store')
|
30
|
+
end
|
31
|
+
|
32
|
+
def configure
|
33
|
+
logger.info("Configuring instance")
|
34
|
+
|
35
|
+
load_settings
|
36
|
+
logger.info("Loaded settings: #{@settings.inspect}")
|
37
|
+
|
38
|
+
if @settings
|
39
|
+
update_iptables
|
40
|
+
update_passwords
|
41
|
+
update_agent_id
|
42
|
+
update_credentials
|
43
|
+
update_hostname
|
44
|
+
update_mbus
|
45
|
+
update_blobstore
|
46
|
+
setup_networking
|
47
|
+
update_time
|
48
|
+
setup_data_disk
|
49
|
+
setup_tmp
|
50
|
+
|
51
|
+
Bosh::Agent::Monit.setup_monit_user
|
52
|
+
Bosh::Agent::Monit.setup_alerts
|
53
|
+
|
54
|
+
mount_persistent_disk
|
55
|
+
harden_permissions
|
56
|
+
end
|
57
|
+
{ "settings" => @settings }
|
58
|
+
end
|
59
|
+
|
60
|
+
def load_settings
|
61
|
+
@settings = Bosh::Agent::Settings.load
|
62
|
+
Bosh::Agent::Config.settings = @settings
|
63
|
+
end
|
64
|
+
|
65
|
+
def iptables(cmd)
|
66
|
+
output = %x{iptables #{cmd} 2> /dev/null}
|
67
|
+
if $?.exitstatus != 0
|
68
|
+
raise Bosh::Agent::Error, "`iptables #{cmd}` failed"
|
69
|
+
end
|
70
|
+
output
|
71
|
+
end
|
72
|
+
|
73
|
+
def update_iptables
|
74
|
+
return unless rules = @settings['iptables']
|
75
|
+
|
76
|
+
if rules["drop_output"]
|
77
|
+
chain = "agent-filter"
|
78
|
+
append_chain = "-A OUTPUT -j #{chain}"
|
79
|
+
|
80
|
+
begin
|
81
|
+
iptables("-N #{chain}")
|
82
|
+
rescue
|
83
|
+
iptables("-F #{chain}")
|
84
|
+
end
|
85
|
+
|
86
|
+
unless iptables("-S").include?(append_chain)
|
87
|
+
iptables(append_chain)
|
88
|
+
end
|
89
|
+
|
90
|
+
rules["drop_output"].each do |dest|
|
91
|
+
rule = "-A #{chain} -d #{dest} -m owner ! --uid-owner root -j DROP"
|
92
|
+
iptables(rule)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_passwords
|
98
|
+
@platform.update_passwords(@settings) unless @settings["env"].nil?
|
99
|
+
end
|
100
|
+
|
101
|
+
def update_agent_id
|
102
|
+
Bosh::Agent::Config.agent_id = @settings["agent_id"]
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_credentials
|
106
|
+
env = @settings["env"]
|
107
|
+
if env && bosh_env = env["bosh"]
|
108
|
+
if bosh_env["credentials"]
|
109
|
+
Bosh::Agent::Config.credentials = bosh_env["credentials"]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def update_hostname
|
115
|
+
agent_id = @settings['agent_id']
|
116
|
+
|
117
|
+
template = ERB.new(ETC_HOST_TEMPLATE, 0, '%<>-')
|
118
|
+
result = template.result(binding)
|
119
|
+
File.open('/etc/hosts', 'w') { |f| f.puts(result) }
|
120
|
+
|
121
|
+
`hostname #{agent_id}`
|
122
|
+
File.open('/etc/hostname', 'w') { |f| f.puts(agent_id) }
|
123
|
+
end
|
124
|
+
|
125
|
+
def update_mbus
|
126
|
+
Bosh::Agent::Config.mbus = @settings['mbus']
|
127
|
+
end
|
128
|
+
|
129
|
+
def update_blobstore
|
130
|
+
blobstore_settings = @settings["blobstore"]
|
131
|
+
|
132
|
+
blobstore_provider = blobstore_settings["provider"]
|
133
|
+
blobstore_options = blobstore_settings["options"]
|
134
|
+
|
135
|
+
Bosh::Agent::Config.blobstore_provider = blobstore_provider
|
136
|
+
Bosh::Agent::Config.blobstore_options.merge!(blobstore_options)
|
137
|
+
end
|
138
|
+
|
139
|
+
def setup_networking
|
140
|
+
Bosh::Agent::Config.platform.setup_networking
|
141
|
+
end
|
142
|
+
|
143
|
+
def update_time
|
144
|
+
ntp_servers = @settings['ntp'].join(" ")
|
145
|
+
unless ntp_servers.empty?
|
146
|
+
logger.info("Configure ntp-servers: #{ntp_servers}")
|
147
|
+
Bosh::Agent::Util.update_file(ntp_servers, '/var/vcap/bosh/etc/ntpserver')
|
148
|
+
output = `ntpdate #{ntp_servers}`
|
149
|
+
logger.info(output)
|
150
|
+
else
|
151
|
+
logger.warn("no ntp-servers configured")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def setup_data_disk
|
156
|
+
data_mount = File.join(base_dir, "data")
|
157
|
+
FileUtils.mkdir_p(data_mount)
|
158
|
+
|
159
|
+
data_disk = Bosh::Agent::Config.platform.get_data_disk_device_name
|
160
|
+
if data_disk
|
161
|
+
unless File.blockdev?(data_disk)
|
162
|
+
logger.warn("Data disk is not a block device: #{data_disk}")
|
163
|
+
return
|
164
|
+
end
|
165
|
+
|
166
|
+
swap_partition = "#{data_disk}1"
|
167
|
+
data_partition = "#{data_disk}2"
|
168
|
+
|
169
|
+
swap_turned_on = sh("cat /proc/swaps | grep #{swap_partition}", :on_error => :return).success?
|
170
|
+
data_partition_mounted = sh("mount | grep #{data_partition}", :on_error => :return).success?
|
171
|
+
|
172
|
+
if Dir.glob("#{data_disk}[1-2]").empty?
|
173
|
+
logger.info("Found unformatted drive")
|
174
|
+
logger.info("Partition #{data_disk}")
|
175
|
+
Bosh::Agent::Util.partition_disk(data_disk, data_sfdisk_input)
|
176
|
+
|
177
|
+
logger.info("Create swap and data partitions")
|
178
|
+
sh "mkswap #{swap_partition}"
|
179
|
+
|
180
|
+
mke2fs_options = ["-t ext4", "-j"]
|
181
|
+
mke2fs_options << "-E lazy_itable_init=1" if Bosh::Agent::Util.lazy_itable_init_enabled?
|
182
|
+
sh "/sbin/mke2fs #{mke2fs_options.join(" ")} #{data_partition}"
|
183
|
+
end
|
184
|
+
|
185
|
+
unless swap_turned_on
|
186
|
+
logger.info("Swapon partition #{swap_partition}")
|
187
|
+
sh "swapon #{swap_partition}"
|
188
|
+
end
|
189
|
+
|
190
|
+
unless data_partition_mounted
|
191
|
+
unless Pathname.new(data_mount).mountpoint?
|
192
|
+
logger.info("Mount data partition #{data_partition} to #{data_mount}")
|
193
|
+
sh "mount #{data_partition} #{data_mount}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
setup_data_sys
|
199
|
+
end
|
200
|
+
|
201
|
+
def data_sfdisk_input
|
202
|
+
",#{swap_size},S\n,,L\n"
|
203
|
+
end
|
204
|
+
|
205
|
+
def swap_size
|
206
|
+
data_disk = Bosh::Agent::Config.platform.get_data_disk_device_name
|
207
|
+
disk_size = Util.block_device_size(data_disk)
|
208
|
+
if mem_total > disk_size/2
|
209
|
+
return (disk_size/2)/1024
|
210
|
+
else
|
211
|
+
return mem_total/1024
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def mem_total
|
216
|
+
# MemTotal: 3952180 kB
|
217
|
+
File.readlines('/proc/meminfo').first.split(/\s+/)[1].to_i
|
218
|
+
end
|
219
|
+
|
220
|
+
def setup_data_sys
|
221
|
+
%w{log run}.each do |dir|
|
222
|
+
path = "#{base_dir}/data/sys/#{dir}"
|
223
|
+
%x[mkdir -p #{path}]
|
224
|
+
%x[chown root:vcap #{path}]
|
225
|
+
%x[chmod 0750 #{path}]
|
226
|
+
end
|
227
|
+
%x[ln -nsf #{base_dir}/data/sys #{base_dir}/sys]
|
228
|
+
end
|
229
|
+
|
230
|
+
def setup_tmp
|
231
|
+
# use a custom TMPDIR for agent itself
|
232
|
+
agent_tmp_dir = File.join(base_dir, 'data', 'tmp')
|
233
|
+
FileUtils.mkdir_p(agent_tmp_dir)
|
234
|
+
ENV["TMPDIR"] = agent_tmp_dir
|
235
|
+
|
236
|
+
# first time: for /tmp on the root fs
|
237
|
+
tmp_permissions
|
238
|
+
|
239
|
+
unless Pathname.new('/tmp').mountpoint?
|
240
|
+
tmp_size = 128
|
241
|
+
root_tmp = File.join(base_dir, 'data', 'root_tmp')
|
242
|
+
|
243
|
+
# If it's not mounted on /tmp - we don't care - blow it away
|
244
|
+
%x[/usr/bin/truncate -s #{tmp_size}M #{root_tmp}]
|
245
|
+
%x[chmod 0700 #{root_tmp}]
|
246
|
+
%x[mke2fs -t ext4 -m 1 -F #{root_tmp}]
|
247
|
+
|
248
|
+
%x[mount -t ext4 -o loop #{root_tmp} /tmp]
|
249
|
+
|
250
|
+
# 2nd time for the new /tmp mount
|
251
|
+
tmp_permissions
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def tmp_permissions
|
256
|
+
%x[chown root:#{BOSH_APP_USER} /tmp]
|
257
|
+
%x[chmod 0770 /tmp]
|
258
|
+
%x[chmod 0700 /var/tmp]
|
259
|
+
end
|
260
|
+
|
261
|
+
def mount_persistent_disk
|
262
|
+
if @settings['disks']['persistent'].keys.size > 1
|
263
|
+
# hell on earth
|
264
|
+
raise Bosh::Agent::FatalError, "Fatal: more than one persistent disk on boot"
|
265
|
+
else
|
266
|
+
cid = @settings['disks']['persistent'].keys.first
|
267
|
+
if cid
|
268
|
+
Bosh::Agent::Config.platform.mount_persistent_disk(cid)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def harden_permissions
|
274
|
+
setup_cron_at_allow
|
275
|
+
|
276
|
+
# use this instead of removing vcap from the cdrom group, as there
|
277
|
+
# is no way to easily do that from the command line
|
278
|
+
root_only_rw = %w{
|
279
|
+
/dev/sr0
|
280
|
+
}
|
281
|
+
root_only_rw.each do |path|
|
282
|
+
%x[chmod 0660 #{path}]
|
283
|
+
%x[chown root:root #{path}]
|
284
|
+
end
|
285
|
+
|
286
|
+
root_app_user_rw = %w{
|
287
|
+
/dev/log
|
288
|
+
}
|
289
|
+
root_app_user_rw.each do |path|
|
290
|
+
%x[chmod 0660 #{path}]
|
291
|
+
%x[chown root:#{BOSH_APP_USER} #{path}]
|
292
|
+
end
|
293
|
+
|
294
|
+
root_app_user_rwx = %w{
|
295
|
+
/dev/shm
|
296
|
+
/var/lock
|
297
|
+
}
|
298
|
+
root_app_user_rwx.each do |path|
|
299
|
+
%x[chmod 0770 #{path}]
|
300
|
+
%x[chown root:#{BOSH_APP_USER} #{path}]
|
301
|
+
end
|
302
|
+
|
303
|
+
root_rw_app_user_read = %w{
|
304
|
+
/etc/cron.allow
|
305
|
+
/etc/at.allow
|
306
|
+
}
|
307
|
+
root_rw_app_user_read.each do |path|
|
308
|
+
%x[chmod 0640 #{path}]
|
309
|
+
%x[chown root:#{BOSH_APP_USER} #{path}]
|
310
|
+
end
|
311
|
+
|
312
|
+
no_other_read = %w{
|
313
|
+
/data/vcap/data
|
314
|
+
/data/vcap/store
|
315
|
+
}
|
316
|
+
no_other_read.each do |path|
|
317
|
+
%[chmod o-r #{path}]
|
318
|
+
end
|
319
|
+
|
320
|
+
end
|
321
|
+
|
322
|
+
def setup_cron_at_allow
|
323
|
+
%w{/etc/cron.allow /etc/at.allow}.each do |file|
|
324
|
+
File.open(file, 'w') { |fh| fh.puts(BOSH_APP_USER) }
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
ETC_HOST_TEMPLATE = <<TEMPLATE
|
329
|
+
127.0.0.1 localhost <%= agent_id %>
|
330
|
+
|
331
|
+
# The following lines are desirable for IPv6 capable hosts
|
332
|
+
::1 localhost ip6-localhost ip6-loopback <%= agent_id %>
|
333
|
+
fe00::0 ip6-localnet
|
334
|
+
ff00::0 ip6-mcastprefix
|
335
|
+
ff02::1 ip6-allnodes
|
336
|
+
ff02::2 ip6-allrouters
|
337
|
+
ff02::3 ip6-allhosts
|
338
|
+
TEMPLATE
|
339
|
+
|
340
|
+
end
|
341
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Bosh::Agent
|
2
|
+
class Configuration
|
3
|
+
DEFAULT_BASE_DIR = '/var/vcap'
|
4
|
+
CONFIG_OPTIONS = [
|
5
|
+
:base_dir,
|
6
|
+
:logger,
|
7
|
+
:mbus,
|
8
|
+
:agent_id,
|
9
|
+
:configure,
|
10
|
+
:blobstore,
|
11
|
+
:blobstore_provider,
|
12
|
+
:blobstore_options,
|
13
|
+
:system_root,
|
14
|
+
:infrastructure_name,
|
15
|
+
:platform_name,
|
16
|
+
:nats,
|
17
|
+
:process_alerts,
|
18
|
+
:smtp_port,
|
19
|
+
:smtp_user,
|
20
|
+
:smtp_password,
|
21
|
+
:heartbeat_interval,
|
22
|
+
:settings_file,
|
23
|
+
:settings,
|
24
|
+
:state,
|
25
|
+
:credentials
|
26
|
+
]
|
27
|
+
|
28
|
+
CONFIG_OPTIONS.each do |option|
|
29
|
+
attr_accessor option
|
30
|
+
end
|
31
|
+
|
32
|
+
def clear
|
33
|
+
CONFIG_OPTIONS.each do |option|
|
34
|
+
send("#{option}=", nil)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup(config)
|
39
|
+
@configure = config['configure']
|
40
|
+
|
41
|
+
# it is the responsibillity of the caller to make sure the dir exist
|
42
|
+
logging_config = config.fetch('logging', {})
|
43
|
+
@logger = Logger.new(logging_config.fetch('file', STDOUT))
|
44
|
+
@logger.level = Logger.const_get(logging_config.fetch('level', 'info').upcase)
|
45
|
+
|
46
|
+
@base_dir = config['base_dir'] || DEFAULT_BASE_DIR
|
47
|
+
@agent_id = config['agent_id']
|
48
|
+
|
49
|
+
@mbus = config['mbus']
|
50
|
+
|
51
|
+
@blobstore_options = config['blobstore_options']
|
52
|
+
@blobstore_provider = config['blobstore_provider']
|
53
|
+
|
54
|
+
@infrastructure_name = config['infrastructure_name']
|
55
|
+
@platform_name = config['platform_name']
|
56
|
+
|
57
|
+
@system_root = config['root_dir'] || '/'
|
58
|
+
|
59
|
+
@process_alerts = config['process_alerts']
|
60
|
+
@smtp_port = config['smtp_port']
|
61
|
+
@smtp_user = 'vcap'
|
62
|
+
@smtp_password = random_password(8)
|
63
|
+
|
64
|
+
@heartbeat_interval = config['heartbeat_interval']
|
65
|
+
|
66
|
+
unless @configure
|
67
|
+
@logger.info("Configuring Agent with: #{config.inspect}")
|
68
|
+
end
|
69
|
+
|
70
|
+
@settings_file = File.join(@base_dir, 'bosh', 'settings.json')
|
71
|
+
|
72
|
+
@credentials = config['credentials']
|
73
|
+
|
74
|
+
@settings = {}
|
75
|
+
|
76
|
+
@state = State.new(File.join(@base_dir, 'bosh', 'state.yml'))
|
77
|
+
end
|
78
|
+
|
79
|
+
def infrastructure
|
80
|
+
@infrastructure ||= Bosh::Agent::Infrastructure.new(@infrastructure_name).infrastructure
|
81
|
+
end
|
82
|
+
|
83
|
+
def platform
|
84
|
+
@platform ||= Bosh::Agent::Platform.platform(@platform_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
def random_password(len)
|
88
|
+
OpenSSL::Random.random_bytes(len).unpack('H*')[0]
|
89
|
+
end
|
90
|
+
|
91
|
+
def default_ip
|
92
|
+
ip = nil
|
93
|
+
@state['networks'].each do |k, v|
|
94
|
+
ip = v['ip'] if ip.nil?
|
95
|
+
if v.key?('default')
|
96
|
+
ip = v['ip']
|
97
|
+
end
|
98
|
+
end
|
99
|
+
ip
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Bosh::Agent
|
2
|
+
class DiskUtil
|
3
|
+
class << self
|
4
|
+
def logger
|
5
|
+
Bosh::Agent::Config.logger
|
6
|
+
end
|
7
|
+
|
8
|
+
def base_dir
|
9
|
+
Bosh::Agent::Config.base_dir
|
10
|
+
end
|
11
|
+
|
12
|
+
def mount_entry(partition)
|
13
|
+
File.read('/proc/mounts').lines.select { |l| l.match(/#{partition}/) }.first
|
14
|
+
end
|
15
|
+
|
16
|
+
GUARD_RETRIES = 600
|
17
|
+
GUARD_SLEEP = 1
|
18
|
+
|
19
|
+
def umount_guard(mountpoint)
|
20
|
+
umount_attempts = GUARD_RETRIES
|
21
|
+
|
22
|
+
loop do
|
23
|
+
umount_output = `umount #{mountpoint} 2>&1`
|
24
|
+
|
25
|
+
if $?.exitstatus == 0
|
26
|
+
break
|
27
|
+
elsif umount_attempts != 0 && umount_output =~ /device is busy/
|
28
|
+
#when umount2 syscall fails and errno == EBUSY, umount.c outputs:
|
29
|
+
# "umount: %s: device is busy.\n"
|
30
|
+
sleep GUARD_SLEEP
|
31
|
+
umount_attempts -= 1
|
32
|
+
else
|
33
|
+
raise Bosh::Agent::MessageHandlerError,
|
34
|
+
"Failed to umount #{mountpoint}: #{umount_output}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attempts = GUARD_RETRIES - umount_attempts
|
39
|
+
logger.info("umount_guard #{mountpoint} succeeded (#{attempts})")
|
40
|
+
end
|
41
|
+
|
42
|
+
# Pay a penalty on this check the first time a persistent disk is added to a system
|
43
|
+
def ensure_no_partition?(disk, partition)
|
44
|
+
check_count = 2
|
45
|
+
check_count.times do
|
46
|
+
if sfdisk_lookup_partition(disk, partition).empty?
|
47
|
+
# keep on trying
|
48
|
+
else
|
49
|
+
if File.blockdev?(partition)
|
50
|
+
return false # break early if partition is there
|
51
|
+
end
|
52
|
+
end
|
53
|
+
sleep 1
|
54
|
+
end
|
55
|
+
|
56
|
+
# Double check that the /dev entry is there
|
57
|
+
if File.blockdev?(partition)
|
58
|
+
return false
|
59
|
+
else
|
60
|
+
return true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def sfdisk_lookup_partition(disk, partition)
|
65
|
+
`sfdisk -Llq #{disk}`.lines.select { |l| l.match(%q{/\A#{partition}.*83.*Linux}) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_usage
|
69
|
+
usage = {
|
70
|
+
system: fs_usage_safe('/')
|
71
|
+
}
|
72
|
+
ephemeral_percent = fs_usage_safe(File.join(base_dir, 'data'))
|
73
|
+
usage[:ephemeral] = ephemeral_percent if ephemeral_percent
|
74
|
+
persistent_percent = fs_usage_safe(File.join(base_dir, 'store'))
|
75
|
+
usage[:persistent] = persistent_percent if persistent_percent
|
76
|
+
|
77
|
+
usage
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
# Calculate file_system_usage
|
82
|
+
def fs_usage_safe(path)
|
83
|
+
sigar = SigarBox.create_sigar
|
84
|
+
fs_list = sigar.file_system_list
|
85
|
+
|
86
|
+
fs = fs_list.find {|fs| fs.dir_name == path}
|
87
|
+
return unless fs
|
88
|
+
|
89
|
+
usage = sigar.file_system_usage(path)
|
90
|
+
space_usage_percent = (usage.use_percent * 100)
|
91
|
+
|
92
|
+
# inode pct calculation taken from 'df' src
|
93
|
+
# http://lingrok.org/xref/coreutils/src/df.c
|
94
|
+
inode_total = usage.files
|
95
|
+
inode_used_100 = (inode_total - usage.free_files) * 100
|
96
|
+
inode_usage_percent = inode_used_100 / inode_total + (inode_used_100 % inode_total != 0 ? 1 : 0)
|
97
|
+
|
98
|
+
{ percent: space_usage_percent.to_i.to_s, inode_percent: inode_usage_percent.to_i.to_s }
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|