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.
Files changed (76) hide show
  1. data/CHANGELOG +0 -0
  2. data/bin/bosh_agent +102 -0
  3. data/lib/bosh_agent/alert.rb +191 -0
  4. data/lib/bosh_agent/alert_processor.rb +96 -0
  5. data/lib/bosh_agent/apply_plan/helpers.rb +30 -0
  6. data/lib/bosh_agent/apply_plan/job.rb +235 -0
  7. data/lib/bosh_agent/apply_plan/package.rb +58 -0
  8. data/lib/bosh_agent/apply_plan/plan.rb +96 -0
  9. data/lib/bosh_agent/bootstrap.rb +341 -0
  10. data/lib/bosh_agent/config.rb +5 -0
  11. data/lib/bosh_agent/configuration.rb +102 -0
  12. data/lib/bosh_agent/disk_util.rb +103 -0
  13. data/lib/bosh_agent/errors.rb +25 -0
  14. data/lib/bosh_agent/ext.rb +48 -0
  15. data/lib/bosh_agent/file_aggregator.rb +78 -0
  16. data/lib/bosh_agent/file_matcher.rb +45 -0
  17. data/lib/bosh_agent/handler.rb +440 -0
  18. data/lib/bosh_agent/heartbeat.rb +74 -0
  19. data/lib/bosh_agent/heartbeat_processor.rb +45 -0
  20. data/lib/bosh_agent/http_handler.rb +135 -0
  21. data/lib/bosh_agent/infrastructure/aws/registry.rb +177 -0
  22. data/lib/bosh_agent/infrastructure/aws/settings.rb +59 -0
  23. data/lib/bosh_agent/infrastructure/aws.rb +17 -0
  24. data/lib/bosh_agent/infrastructure/dummy.rb +24 -0
  25. data/lib/bosh_agent/infrastructure/openstack/registry.rb +220 -0
  26. data/lib/bosh_agent/infrastructure/openstack/settings.rb +76 -0
  27. data/lib/bosh_agent/infrastructure/openstack.rb +17 -0
  28. data/lib/bosh_agent/infrastructure/vsphere/settings.rb +135 -0
  29. data/lib/bosh_agent/infrastructure/vsphere.rb +16 -0
  30. data/lib/bosh_agent/infrastructure.rb +25 -0
  31. data/lib/bosh_agent/message/apply.rb +184 -0
  32. data/lib/bosh_agent/message/base.rb +38 -0
  33. data/lib/bosh_agent/message/compile_package.rb +250 -0
  34. data/lib/bosh_agent/message/drain.rb +195 -0
  35. data/lib/bosh_agent/message/list_disk.rb +25 -0
  36. data/lib/bosh_agent/message/logs.rb +108 -0
  37. data/lib/bosh_agent/message/migrate_disk.rb +55 -0
  38. data/lib/bosh_agent/message/mount_disk.rb +102 -0
  39. data/lib/bosh_agent/message/ssh.rb +109 -0
  40. data/lib/bosh_agent/message/state.rb +47 -0
  41. data/lib/bosh_agent/message/unmount_disk.rb +29 -0
  42. data/lib/bosh_agent/monit.rb +354 -0
  43. data/lib/bosh_agent/monit_client.rb +158 -0
  44. data/lib/bosh_agent/mounter.rb +42 -0
  45. data/lib/bosh_agent/ntp.rb +32 -0
  46. data/lib/bosh_agent/platform/centos/disk.rb +27 -0
  47. data/lib/bosh_agent/platform/centos/network.rb +39 -0
  48. data/lib/bosh_agent/platform/centos/templates/centos-ifcfg.erb +9 -0
  49. data/lib/bosh_agent/platform/centos/templates/dhclient_conf.erb +56 -0
  50. data/lib/bosh_agent/platform/centos/templates/logrotate.erb +8 -0
  51. data/lib/bosh_agent/platform/centos.rb +4 -0
  52. data/lib/bosh_agent/platform/dummy/templates/dummy_template.erb +1 -0
  53. data/lib/bosh_agent/platform/linux/adapter.rb +36 -0
  54. data/lib/bosh_agent/platform/linux/disk.rb +121 -0
  55. data/lib/bosh_agent/platform/linux/logrotate.rb +32 -0
  56. data/lib/bosh_agent/platform/linux/network.rb +124 -0
  57. data/lib/bosh_agent/platform/linux/password.rb +22 -0
  58. data/lib/bosh_agent/platform/linux.rb +4 -0
  59. data/lib/bosh_agent/platform/ubuntu/network.rb +59 -0
  60. data/lib/bosh_agent/platform/ubuntu/templates/dhclient_conf.erb +56 -0
  61. data/lib/bosh_agent/platform/ubuntu/templates/interfaces.erb +14 -0
  62. data/lib/bosh_agent/platform/ubuntu/templates/logrotate.erb +8 -0
  63. data/lib/bosh_agent/platform/ubuntu.rb +4 -0
  64. data/lib/bosh_agent/platform.rb +26 -0
  65. data/lib/bosh_agent/remote_exception.rb +62 -0
  66. data/lib/bosh_agent/runner.rb +36 -0
  67. data/lib/bosh_agent/settings.rb +61 -0
  68. data/lib/bosh_agent/sigar_box.rb +26 -0
  69. data/lib/bosh_agent/smtp_server.rb +96 -0
  70. data/lib/bosh_agent/state.rb +100 -0
  71. data/lib/bosh_agent/syslog_monitor.rb +53 -0
  72. data/lib/bosh_agent/template.rb +50 -0
  73. data/lib/bosh_agent/util.rb +190 -0
  74. data/lib/bosh_agent/version.rb +8 -0
  75. data/lib/bosh_agent.rb +92 -0
  76. 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,4 @@
1
+ module Bosh::Agent
2
+ module Platform::Linux
3
+ end
4
+ 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,8 @@
1
+ <%= base_dir %>/data/sys/log/*.log <%= base_dir %>/data/sys/log/*/*.log <%= base_dir %>/data/sys/log/*/*/*.log {
2
+ missingok
3
+ rotate 7
4
+ compress
5
+ delaycompress
6
+ copytruncate
7
+ size=<%= size %>
8
+ }
@@ -0,0 +1,4 @@
1
+ module Bosh::Agent
2
+ module Platform::Ubuntu
3
+ end
4
+ 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
+