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,124 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Platform::Linux::Network
|
5
|
+
|
6
|
+
def initialize(template_dir)
|
7
|
+
@template_dir = template_dir
|
8
|
+
@config = Bosh::Agent::Config
|
9
|
+
@infrastructure = @config.infrastructure
|
10
|
+
@logger = @config.logger
|
11
|
+
@networks = []
|
12
|
+
@dns = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup_networking
|
16
|
+
case @config.infrastructure_name
|
17
|
+
when "vsphere"
|
18
|
+
setup_networking_from_settings
|
19
|
+
when "aws"
|
20
|
+
setup_dhcp_from_settings
|
21
|
+
when "openstack"
|
22
|
+
setup_dhcp_from_settings
|
23
|
+
else
|
24
|
+
raise Bosh::Agent::FatalError, "Setup networking failed, unsupported infrastructure #{Bosh::Agent::Config.infrastructure_name}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def setup_networking_from_settings
|
30
|
+
mac_addresses = detect_mac_addresses
|
31
|
+
|
32
|
+
networks.values.each do |v|
|
33
|
+
mac = v["mac"]
|
34
|
+
|
35
|
+
unless mac_addresses.has_key?(mac)
|
36
|
+
raise Bosh::Agent::FatalError, "#{mac} from settings not present in instance"
|
37
|
+
end
|
38
|
+
|
39
|
+
v["interface"] = mac_addresses[mac]
|
40
|
+
|
41
|
+
begin
|
42
|
+
net_cidr = NetAddr::CIDR.create("#{v['ip']} #{v['netmask']}")
|
43
|
+
v["network"] = net_cidr.network
|
44
|
+
v["broadcast"] = net_cidr.broadcast
|
45
|
+
rescue NetAddr::ValidationError => e
|
46
|
+
raise Bosh::Agent::FatalError, e.to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
write_network_interfaces
|
51
|
+
write_resolv_conf
|
52
|
+
gratuitous_arp
|
53
|
+
end
|
54
|
+
|
55
|
+
def setup_dhcp_from_settings
|
56
|
+
unless dns.empty?
|
57
|
+
write_dhcp_conf
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def dns
|
62
|
+
default_dns_network = networks.values.detect do |settings|
|
63
|
+
settings.fetch('default', []).include?('dns') && settings["dns"]
|
64
|
+
end
|
65
|
+
default_dns_network ? default_dns_network["dns"] : []
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def networks
|
71
|
+
@config.settings["networks"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def detect_mac_addresses
|
75
|
+
mac_addresses = {}
|
76
|
+
Dir['/sys/class/net/*'].each do |dev_path|
|
77
|
+
dev = File.basename(dev_path)
|
78
|
+
mac = File.read(File.join(dev_path, 'address')).strip
|
79
|
+
mac_addresses[mac] = dev
|
80
|
+
end
|
81
|
+
mac_addresses
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_resolv_conf
|
85
|
+
template = ERB.new("<% dns.each do |server| %>\nnameserver <%= server %>\n<% end %>\n", 0, '%<>')
|
86
|
+
result = template.result(binding)
|
87
|
+
Bosh::Agent::Util::update_file(result, '/etc/resolv.conf')
|
88
|
+
end
|
89
|
+
|
90
|
+
def gratuitous_arp
|
91
|
+
# HACK to send a gratuitous arp every 10 seconds for the first minute
|
92
|
+
# after networking has been reconfigured.
|
93
|
+
Thread.new do
|
94
|
+
6.times do
|
95
|
+
@networks.each do |name, network|
|
96
|
+
until File.exist?("/sys/class/net/#{network['interface']}")
|
97
|
+
sleep 0.1
|
98
|
+
end
|
99
|
+
|
100
|
+
arp_cmd = "arping -c 1 -U -I #{network['interface']} #{network['ip']}"
|
101
|
+
@logger.info(arp_cmd)
|
102
|
+
Bosh::Exec.sh "#{arp_cmd}"
|
103
|
+
end
|
104
|
+
sleep 10
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def write_network_interfaces
|
110
|
+
raise Bosh::Agent::UnimplementedMethod.new
|
111
|
+
end
|
112
|
+
|
113
|
+
def write_dhcp_conf
|
114
|
+
raise Bosh::Agent::UnimplementedMethod.new
|
115
|
+
end
|
116
|
+
|
117
|
+
def load_erb(file)
|
118
|
+
path = File.expand_path(file, @template_dir)
|
119
|
+
File.open(path) do |f|
|
120
|
+
f.read
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Platform::Linux::Password
|
5
|
+
USERS = ['root', Bosh::Agent::BOSH_APP_USER]
|
6
|
+
|
7
|
+
# Update passwords
|
8
|
+
def update(settings)
|
9
|
+
password = settings.fetch('env', {}).fetch('bosh', {})['password']
|
10
|
+
|
11
|
+
if password
|
12
|
+
USERS.each { |user| update_password(user, password) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
# Actually update password
|
18
|
+
def update_password(user, encrypted_password)
|
19
|
+
Bosh::Exec.sh "usermod -p '#{encrypted_password}' #{user} 2>%"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Platform::Ubuntu::Network < Platform::Linux::Network
|
5
|
+
include Bosh::Exec
|
6
|
+
|
7
|
+
def initialize(template_dir)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def write_network_interfaces
|
12
|
+
template = ERB.new(load_erb("interfaces.erb"), 0, '%<>-')
|
13
|
+
result = template.result(binding)
|
14
|
+
network_updated = Bosh::Agent::Util::update_file(result, '/etc/network/interfaces')
|
15
|
+
if network_updated
|
16
|
+
@logger.info("Updated networking")
|
17
|
+
restart_networking_service
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def restart_networking_service
|
22
|
+
# ubuntu 10.04 networking startup/upstart stuff is quite borked
|
23
|
+
networks.each do |k, v|
|
24
|
+
interface = v['interface']
|
25
|
+
@logger.info("Restarting #{interface}")
|
26
|
+
output = sh("service network-interface stop INTERFACE=#{interface}").output
|
27
|
+
output += sh("service network-interface start INTERFACE=#{interface}").output
|
28
|
+
@logger.info("Restarted networking: #{output}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_dhcp_conf
|
33
|
+
template = ERB.new(load_erb("dhclient_conf.erb"), 0, '%<>-')
|
34
|
+
result = template.result(binding)
|
35
|
+
updated = Bosh::Agent::Util::update_file(result, '/etc/dhcp3/dhclient.conf')
|
36
|
+
if updated
|
37
|
+
@logger.info("Updated dhclient.conf")
|
38
|
+
restart_dhclient
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Executing /sbin/dhclient starts another dhclient process, so it'll cause
|
43
|
+
# a conflict with the existing system dhclient process and dns changes will
|
44
|
+
# be flip floping each lease time. So in order to refresh dhclient
|
45
|
+
# configuration we need to restart networking.
|
46
|
+
#
|
47
|
+
# If dhclient3 cannot release a lease because it collides with a network
|
48
|
+
# restart (message "receive_packet failed on eth0: Network is down"
|
49
|
+
# appears at /var/log/syslog) then the old dhclient3 process won't be
|
50
|
+
# killed (see bug LP #38140), and there will be two dhclient3 process
|
51
|
+
# running (and dns changes will be flip floping each lease time). So
|
52
|
+
# before restarting the network, we first kill all dhclient3 process.
|
53
|
+
def restart_dhclient
|
54
|
+
sh("pkill dhclient3", :on_error => :return)
|
55
|
+
sh("/etc/init.d/networking restart", :on_error => :return)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Configuration file for /sbin/dhclient, which is included in Debian's
|
2
|
+
# dhcp3-client package.
|
3
|
+
#
|
4
|
+
# This is a sample configuration file for dhclient. See dhclient.conf's
|
5
|
+
# man page for more information about the syntax of this file
|
6
|
+
# and a more comprehensive list of the parameters understood by
|
7
|
+
# dhclient.
|
8
|
+
#
|
9
|
+
# Normally, if the DHCP server provides reasonable information and does
|
10
|
+
# not leave anything out (like the domain name, for example), then
|
11
|
+
# few changes must be made to this file, if any.
|
12
|
+
#
|
13
|
+
|
14
|
+
option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
|
15
|
+
|
16
|
+
send host-name "<hostname>";
|
17
|
+
#send dhcp-client-identifier 1:0:a0:24:ab:fb:9c;
|
18
|
+
#send dhcp-lease-time 3600;
|
19
|
+
#supersede domain-name "fugue.com home.vix.com";
|
20
|
+
request subnet-mask, broadcast-address, time-offset, routers,
|
21
|
+
domain-name, domain-name-servers, domain-search, host-name,
|
22
|
+
netbios-name-servers, netbios-scope, interface-mtu,
|
23
|
+
rfc3442-classless-static-routes, ntp-servers;
|
24
|
+
<% dns.reverse.each do |server| -%>
|
25
|
+
prepend domain-name-servers <%= server %>;
|
26
|
+
<% end -%>
|
27
|
+
|
28
|
+
#require subnet-mask, domain-name-servers;
|
29
|
+
#timeout 60;
|
30
|
+
#retry 60;
|
31
|
+
#reboot 10;
|
32
|
+
#select-timeout 5;
|
33
|
+
#initial-interval 2;
|
34
|
+
#script "/etc/dhcp3/dhclient-script";
|
35
|
+
#media "-link0 -link1 -link2", "link0 link1";
|
36
|
+
#reject 192.33.137.209;
|
37
|
+
|
38
|
+
#alias {
|
39
|
+
# interface "eth0";
|
40
|
+
# fixed-address 192.5.5.213;
|
41
|
+
# option subnet-mask 255.255.255.255;
|
42
|
+
#}
|
43
|
+
|
44
|
+
#lease {
|
45
|
+
# interface "eth0";
|
46
|
+
# fixed-address 192.33.137.200;
|
47
|
+
# medium "link0 link1";
|
48
|
+
# option host-name "andare.swiftmedia.com";
|
49
|
+
# option subnet-mask 255.255.255.0;
|
50
|
+
# option broadcast-address 192.33.137.255;
|
51
|
+
# option routers 192.33.137.250;
|
52
|
+
# option domain-name-servers 127.0.0.1;
|
53
|
+
# renew 2 2000/1/12 00:00:01;
|
54
|
+
# rebind 2 2000/1/12 00:00:01;
|
55
|
+
# expire 2 2000/1/12 00:00:01;
|
56
|
+
#}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
auto lo
|
2
|
+
iface lo inet loopback
|
3
|
+
|
4
|
+
<% networks.each do |name, n| -%>
|
5
|
+
auto <%= n["interface"] %>
|
6
|
+
iface <%= n["interface"] %> inet static
|
7
|
+
address <%= n["ip"]%>
|
8
|
+
network <%= n["network"] %>
|
9
|
+
netmask <%= n["netmask"]%>
|
10
|
+
broadcast <%= n["broadcast"] %>
|
11
|
+
<% if n.key?('default') && n['default'].include?('gateway') -%>
|
12
|
+
gateway <%= n["gateway"] %>
|
13
|
+
<% end %>
|
14
|
+
<% end -%>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Bosh::Agent
|
2
|
+
class UnknownPlatform < StandardError;
|
3
|
+
end
|
4
|
+
|
5
|
+
module Platform
|
6
|
+
def self.platform(platform_name)
|
7
|
+
case platform_name
|
8
|
+
when 'ubuntu'
|
9
|
+
template_dir = File.expand_path(File.join(File.dirname(__FILE__), 'platform/ubuntu/templates'))
|
10
|
+
Platform::Linux::Adapter.new(Platform::Linux::Disk.new,
|
11
|
+
Platform::Linux::Logrotate.new(template_dir),
|
12
|
+
Platform::Linux::Password.new,
|
13
|
+
Platform::Ubuntu::Network.new(template_dir))
|
14
|
+
|
15
|
+
when 'centos'
|
16
|
+
template_dir = File.expand_path(File.join(File.dirname(__FILE__), 'platform/centos/templates'))
|
17
|
+
Platform::Linux::Adapter.new(Platform::Centos::Disk.new,
|
18
|
+
Platform::Linux::Logrotate.new(template_dir),
|
19
|
+
Platform::Linux::Password.new,
|
20
|
+
Platform::Centos::Network.new(template_dir))
|
21
|
+
else
|
22
|
+
raise UnknownPlatform, "platform '#{platform_name}' not found"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Bosh::Agent
|
2
|
+
|
3
|
+
# Helper class to pass around and fromat exceptions in the agent
|
4
|
+
# to the director
|
5
|
+
class RemoteException
|
6
|
+
attr_reader :message, :backtrace, :blob
|
7
|
+
|
8
|
+
def initialize(message, backtrace=nil, blob=nil)
|
9
|
+
@message = message
|
10
|
+
@backtrace = backtrace.nil? ? caller : backtrace
|
11
|
+
@blob = blob
|
12
|
+
end
|
13
|
+
|
14
|
+
# Stores the blob in the configured blobstore
|
15
|
+
#
|
16
|
+
# @return [String] blobstore id of the stored object, or an error
|
17
|
+
# string which can be displayed instead of the blob
|
18
|
+
def store_blob
|
19
|
+
bsc_options = Bosh::Agent::Config.blobstore_options
|
20
|
+
bsc_provider = Bosh::Agent::Config.blobstore_provider
|
21
|
+
|
22
|
+
blobstore = Bosh::Blobstore::Client.create(bsc_provider, bsc_options)
|
23
|
+
|
24
|
+
logger.info("Uploading blob for '#{@message}' to blobstore")
|
25
|
+
|
26
|
+
blobstore_id = nil
|
27
|
+
blobstore_id = blobstore.create(@blob)
|
28
|
+
|
29
|
+
blobstore_id
|
30
|
+
rescue Bosh::Blobstore::BlobstoreError => e
|
31
|
+
logger.warn("unable to upload blob for '#{@message}'")
|
32
|
+
"error: unable to upload blob to blobstore: #{e.message}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a hash of the [RemoteException] suitable to convert to json
|
36
|
+
#
|
37
|
+
# @return [Hash] [RemoteException] represented as a [Hash]
|
38
|
+
def to_hash
|
39
|
+
hash = {:message => @message}
|
40
|
+
hash[:backtrace] = @backtrace
|
41
|
+
hash[:blobstore_id] = store_blob if @blob
|
42
|
+
{:exception => hash}
|
43
|
+
end
|
44
|
+
|
45
|
+
def logger
|
46
|
+
Bosh::Agent::Config.logger
|
47
|
+
end
|
48
|
+
|
49
|
+
# Helper class method that creates a [Bosh::Agent::RemoteException]
|
50
|
+
# from an [Exception]
|
51
|
+
#
|
52
|
+
# @return [Bosh::Agent::RemoteException]
|
53
|
+
def self.from(exception)
|
54
|
+
blob = nil
|
55
|
+
if exception.instance_of?(Bosh::Agent::MessageHandlerError)
|
56
|
+
blob = exception.blob
|
57
|
+
end
|
58
|
+
self.new(exception.message, exception.backtrace, blob)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Bosh::Agent
|
2
|
+
class Runner
|
3
|
+
def self.run(options)
|
4
|
+
Config.setup(options)
|
5
|
+
Runner.new.start
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@logger = Config.logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
$stdout.sync = true
|
14
|
+
@logger.info("Starting agent #{VERSION}...")
|
15
|
+
|
16
|
+
if Config.configure
|
17
|
+
@logger.info('Configuring agent...')
|
18
|
+
Bootstrap.new.configure
|
19
|
+
|
20
|
+
Monit.enable
|
21
|
+
Monit.start
|
22
|
+
Monit.start_services
|
23
|
+
else
|
24
|
+
@logger.info("Skipping configuration step (use '-c' argument to configure on start) ")
|
25
|
+
end
|
26
|
+
|
27
|
+
if Config.mbus.start_with?('https')
|
28
|
+
@logger.info('Starting up https agent')
|
29
|
+
require 'bosh_agent/http_handler'
|
30
|
+
HTTPHandler.start
|
31
|
+
else
|
32
|
+
Handler.start
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Settings
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :@settings, :fetch, :[]
|
8
|
+
|
9
|
+
# Loads settings specific to the current infrastructure, and then caches
|
10
|
+
# the result to disk. If it can't fetch settings, it will fall back to
|
11
|
+
# previously cached settings
|
12
|
+
def self.load(cache_path=nil)
|
13
|
+
settings = new(cache_path || Config.settings_file)
|
14
|
+
settings.load
|
15
|
+
settings.cache
|
16
|
+
settings
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(file)
|
20
|
+
@settings = {}
|
21
|
+
@cache_file = file
|
22
|
+
end
|
23
|
+
|
24
|
+
def load
|
25
|
+
load_from_infrastructure
|
26
|
+
cache
|
27
|
+
rescue LoadSettingsError => e
|
28
|
+
logger.info("failed to load infrastructure settings: #{e.message}")
|
29
|
+
load_from_cache
|
30
|
+
end
|
31
|
+
|
32
|
+
def cache
|
33
|
+
json = Yajl::Encoder.encode(@settings)
|
34
|
+
File.open(@cache_file, 'w') do |file|
|
35
|
+
file.write(json)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def load_from_infrastructure
|
42
|
+
@settings = Bosh::Agent::Config.infrastructure.load_settings
|
43
|
+
logger.info("loaded new infrastructure settings: #{@settings.inspect}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_from_cache
|
47
|
+
json = File.read(@cache_file)
|
48
|
+
# perhaps catch json parser errors too and raise as LoadSettingsSerror?
|
49
|
+
@settings = Yajl::Parser.new.parse(json)
|
50
|
+
logger.info("loaded cached settings: #{@settings.inspect}")
|
51
|
+
rescue Errno::ENOENT
|
52
|
+
raise LoadSettingsError, "could neither load infrastructure settings " \
|
53
|
+
"nor cached settings from: #@cache_file"
|
54
|
+
end
|
55
|
+
|
56
|
+
def logger
|
57
|
+
Bosh::Agent::Config.logger
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Bosh
|
2
|
+
module Agent
|
3
|
+
class NullLogger < Logger
|
4
|
+
def initialize
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(level, message)
|
8
|
+
# do nothing
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class SigarBox
|
13
|
+
def self.create_sigar
|
14
|
+
sigar = nil
|
15
|
+
begin
|
16
|
+
GC.disable
|
17
|
+
sigar = Sigar.new
|
18
|
+
sigar.logger = NullLogger.new
|
19
|
+
ensure
|
20
|
+
GC.enable
|
21
|
+
end
|
22
|
+
sigar
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
|
5
|
+
class SmtpServer < EM::Protocols::SmtpServer
|
6
|
+
|
7
|
+
class Error < StandardError; end
|
8
|
+
|
9
|
+
MAX_MESSAGE_SIZE = 1024 * 1024 # 1M
|
10
|
+
INACTIVITY_TIMEOUT = 10 # seconds
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
|
15
|
+
options = args.last
|
16
|
+
|
17
|
+
@logger = Config.logger
|
18
|
+
@user = options[:user]
|
19
|
+
@password = options[:password]
|
20
|
+
@processor = options[:processor]
|
21
|
+
@authenticated = false
|
22
|
+
@chunks = [ ]
|
23
|
+
@msg_size = 0
|
24
|
+
|
25
|
+
# @parms come from EM:Protocols::SmtpServer
|
26
|
+
@parms[:auth] = true if @user && @password
|
27
|
+
|
28
|
+
self.comm_inactivity_timeout = INACTIVITY_TIMEOUT
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_server_greeting
|
32
|
+
"BOSH Agent SMTP Server"
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_server_domain
|
36
|
+
get_server_greeting
|
37
|
+
end
|
38
|
+
|
39
|
+
def authenticated?
|
40
|
+
@authenticated
|
41
|
+
end
|
42
|
+
|
43
|
+
# We don't really care about senders and recipients
|
44
|
+
# as we only use SMTP for data transport between
|
45
|
+
# Monit and Agent. However it's handy to use
|
46
|
+
# the SMTP sequence constraints to enforce that
|
47
|
+
# only authenticated user can actually send data.
|
48
|
+
|
49
|
+
def receive_plain_auth(user, password)
|
50
|
+
@authenticated = (user == @user && @password == password)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Only accept MAIL FROM if already authenticated
|
54
|
+
def receive_sender(sender)
|
55
|
+
authenticated?
|
56
|
+
end
|
57
|
+
|
58
|
+
# Only accept RCPT TO if already authenticated
|
59
|
+
def receive_recipient(rcpt)
|
60
|
+
authenticated?
|
61
|
+
end
|
62
|
+
|
63
|
+
def receive_data_chunk(c)
|
64
|
+
@msg_size += c.join.length
|
65
|
+
|
66
|
+
if @msg_size > MAX_MESSAGE_SIZE
|
67
|
+
send_data "552 Message too large\r\n"
|
68
|
+
close_connection_after_writing
|
69
|
+
else
|
70
|
+
@chunks += c
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def receive_message
|
75
|
+
unless @processor
|
76
|
+
@logger.error "Failed to process alert: no alert processor provided"
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
|
80
|
+
unless @processor.respond_to?(:process_email_alert)
|
81
|
+
@logger.error "Failed to process alert: alert processor should respond to :process_email_alert method"
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
|
85
|
+
message = @chunks.join("\n")
|
86
|
+
@chunks = [ ] # Support multiple data blocks in a single SMTP session
|
87
|
+
@processor.process_email_alert(message)
|
88
|
+
|
89
|
+
# WARNING: this MUST return true, otherwise Monit will try to send alerts over and over again
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|