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,100 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ # This is a thin abstraction on top of a state.yml file that is managed by agent.
4
+ # It can be used for getting the whole state as a hash or for querying
5
+ # particular keys. Flushing the state to a file only happens on write call.
6
+
7
+ # It aims to be threadsafe but doesn' use file locking so as long as everyone agrees on using the same
8
+ # singleton of this class to read and write state it should be fine. However two agent processes using the same state file
9
+ # could still be stepping on each other as well as two classes using different instances of this.
10
+
11
+ module Bosh::Agent
12
+ class State
13
+
14
+ def initialize(state_file)
15
+ @state_file = state_file
16
+ @lock = Mutex.new
17
+ @data = nil
18
+ read
19
+ end
20
+
21
+ # Fetches the state from file (unless it's been already fetched)
22
+ # and returns the value of a given key.
23
+ # @param key Key that will be looked up in state hash
24
+ def [](key)
25
+ @lock.synchronize { @data[key] }
26
+ end
27
+
28
+ def to_hash
29
+ @lock.synchronize { @data.dup }
30
+ end
31
+
32
+ def ips
33
+ result = []
34
+ networks = self["networks"] || {}
35
+ return [] unless networks.kind_of?(Hash)
36
+
37
+ networks.each_pair do |name, network |
38
+ result << network["ip"] if network["ip"]
39
+ end
40
+
41
+ result
42
+ end
43
+
44
+ # Reads the current agent state from the state file and saves it internally.
45
+ # Empty file is fine but malformed file raises an exception.
46
+ def read
47
+ @lock.synchronize do
48
+ if File.exists?(@state_file)
49
+ state = Psych.load_file(@state_file) || default_state
50
+ unless state.kind_of?(Hash)
51
+ raise_format_error(state)
52
+ end
53
+ @data = state
54
+ else
55
+ @data = default_state
56
+ end
57
+ end
58
+
59
+ self
60
+ rescue SystemCallError => e
61
+ raise StateError, "Cannot read agent state file `#{@state_file}': #{e}"
62
+ rescue Psych::SyntaxError
63
+ raise StateError, "Malformed agent state: #{e}"
64
+ end
65
+
66
+ # Writes a new agent state into the state file.
67
+ # @param new_state Hash New state
68
+ def write(new_state)
69
+ unless new_state.is_a?(Hash)
70
+ raise_format_error(new_state)
71
+ end
72
+
73
+ @lock.synchronize do
74
+ File.open(@state_file, "w") do |f|
75
+ f.puts(Psych.dump(new_state))
76
+ end
77
+ @data = new_state
78
+ end
79
+
80
+ true
81
+ rescue SystemCallError, Psych::SyntaxError => e
82
+ raise StateError, "Cannot write agent state file `#{@state_file}': #{e}"
83
+ end
84
+
85
+ private
86
+
87
+ def default_state
88
+ {
89
+ "deployment" => "",
90
+ "networks" => { },
91
+ "resource_pool" => { }
92
+ }
93
+ end
94
+
95
+ def raise_format_error(state)
96
+ raise StateError, "Unexpected agent state format: expected Hash, got #{state.class}"
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,53 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require 'eventmachine'
4
+ require 'syslog_protocol'
5
+ require 'uuidtools'
6
+
7
+ module Bosh::Agent::SyslogMonitor
8
+
9
+ PORT = 33331
10
+
11
+ # severity level we publish in the nats alert
12
+ SEVERITY = 4
13
+
14
+ class Server < EventMachine::Connection
15
+ include EM::Protocols::LineText2
16
+
17
+ def initialize(nats, agent_id)
18
+ @nats = nats
19
+ @agent_id = agent_id
20
+ end
21
+
22
+ def receive_line(data)
23
+ parsed = SyslogProtocol.parse(data)
24
+
25
+ if parsed.content.end_with?('disconnected by user')
26
+ title = 'SSH Logout'
27
+ elsif parsed.content.include?('Accepted publickey for')
28
+ title = 'SSH Login'
29
+ else
30
+ return
31
+ end
32
+
33
+ json = Yajl::Encoder.encode(
34
+ {
35
+ 'id' => UUIDTools::UUID.random_create,
36
+ 'severity' => SEVERITY,
37
+ 'title' => title,
38
+ 'summary' => parsed.content,
39
+ 'created_at' => Time.now.to_i
40
+ }
41
+ )
42
+ @nats.publish("hm.agent.alert.#{@agent_id}", json)
43
+ end
44
+ end
45
+
46
+ def self.start(nats, agent_id)
47
+ unless EM.reactor_running?
48
+ raise Error, 'Cannot start syslog monitor as event loop is not running'
49
+ end
50
+
51
+ EventMachine::start_server '127.0.0.1', PORT, Server, nats, agent_id
52
+ end
53
+ end
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Agent
4
+
5
+ class Template
6
+
7
+ class TemplateDataError < StandardError; end
8
+
9
+ def self.write(&block)
10
+ self.new(block).write
11
+ end
12
+
13
+ def initialize(block)
14
+ raise ArgumentError unless block.arity == 1
15
+ @block = block
16
+ @block.call(self)
17
+ raise TemplateDataError if @template_data.nil?
18
+ end
19
+
20
+ def src(template_src)
21
+ case template_src
22
+ when String
23
+ if template_src.match(%r{\A/})
24
+ template_path = template_src
25
+ else
26
+ template_base_dir = File.dirname(__FILE__)
27
+ template_path = File.join(template_base_dir, template_src)
28
+ end
29
+ fh = File.new(template_path)
30
+ when IO, StringIO
31
+ fh = template_src
32
+ end
33
+ @template_data = fh.read
34
+ end
35
+
36
+ def dst(path)
37
+ @dst = path
38
+ end
39
+
40
+ def render
41
+ template = ERB.new(@template_data, 0, '%<>-')
42
+ template.result(@block.binding)
43
+ end
44
+
45
+ def write
46
+ File.open(@dst, 'w') { |fh| fh.write(self.render) }
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,190 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Agent
4
+ class Util
5
+ include Bosh::Exec
6
+
7
+ class << self
8
+ def base_dir
9
+ Bosh::Agent::Config.base_dir
10
+ end
11
+
12
+ def logger
13
+ Bosh::Agent::Config.logger
14
+ end
15
+
16
+ def unpack_blob(blobstore_id, sha1, install_path)
17
+ bsc_options = Bosh::Agent::Config.blobstore_options
18
+ bsc_provider = Bosh::Agent::Config.blobstore_provider
19
+ blobstore_client = Bosh::Blobstore::Client.create(bsc_provider, bsc_options)
20
+
21
+ data_tmp = File.join(base_dir, 'data', 'tmp')
22
+ FileUtils.mkdir_p(data_tmp)
23
+
24
+ begin
25
+ tf = Tempfile.open(blobstore_id, data_tmp)
26
+ logger.info("Retrieving blob: #{blobstore_id}")
27
+
28
+ blobstore_client.get(blobstore_id, tf)
29
+ logger.info("Done retrieving blob")
30
+
31
+ tf.flush
32
+ blob_data_file = tf.path
33
+
34
+ logger.info("creating #{install_path}")
35
+ FileUtils.mkdir_p(install_path)
36
+
37
+ blob_sha1 = Digest::SHA1.file(blob_data_file).hexdigest
38
+ logger.info("hexdigest of #{blob_data_file}")
39
+
40
+ unless blob_sha1 == sha1
41
+ raise Bosh::Agent::MessageHandlerError,
42
+ "Expected sha1: #{sha1}, Downloaded sha1: #{blob_sha1}, Blobstore ID: #{blobstore_id}, Install Path: #{install_path}"
43
+ end
44
+
45
+ logger.info("Installing to: #{install_path}")
46
+ Dir.chdir(install_path) do
47
+ output = `tar --no-same-owner -zxvf #{blob_data_file}`
48
+ raise Bosh::Agent::MessageHandlerError.new(
49
+ "Failed to unpack blob", output) unless $?.exitstatus == 0
50
+ end
51
+ rescue Exception => e
52
+ logger.info("Failure unpacking blob: #{e.inspect} #{e.backtrace}")
53
+ raise e
54
+ ensure
55
+ if tf
56
+ tf.close
57
+ tf.unlink
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ # @param [Hash] Instance spec
64
+ # @return [Binding] Template evaluation binding
65
+ def config_binding(spec)
66
+ Bosh::Common::TemplateEvaluationContext.new(spec).get_binding
67
+ end
68
+
69
+ def partition_disk(dev, sfdisk_input)
70
+ if File.blockdev?(dev)
71
+ sfdisk_cmd = "echo \"#{sfdisk_input}\" | sfdisk -uM #{dev}"
72
+ output = %x[#{sfdisk_cmd}]
73
+ unless $? == 0
74
+ logger.info("failed to parition #{dev}")
75
+ logger.info(output)
76
+ end
77
+ end
78
+ end
79
+
80
+ def lazy_itable_init_enabled?
81
+ File.exists?("/sys/fs/ext4/features/lazy_itable_init")
82
+ end
83
+
84
+ def block_device_size(block_device)
85
+ unless File.blockdev?(block_device)
86
+ raise Bosh::Agent::MessageHandlerError, "Not a blockdevice"
87
+ end
88
+
89
+ result = sh("/sbin/sfdisk -s #{block_device} 2>&1")
90
+ unless result.output.match(/\A\d+\Z/)
91
+ raise Bosh::Agent::MessageHandlerError,
92
+ "Unable to determine disk size"
93
+ end
94
+ result.output.to_i
95
+ end
96
+
97
+ def run_hook(hook, job_template_name)
98
+ hook_file = File.join(base_dir, 'jobs', job_template_name, 'bin', hook)
99
+
100
+ unless File.exists?(hook_file)
101
+ return nil
102
+ end
103
+
104
+ unless File.executable?(hook_file)
105
+ raise Bosh::Agent::MessageHandlerError, "`#{hook}' hook for `#{job_template_name}' job is not an executable file"
106
+ end
107
+
108
+ env = {
109
+ 'PATH' => '/usr/sbin:/usr/bin:/sbin:/bin',
110
+ }
111
+
112
+ stdout_rd, stdout_wr = IO.pipe
113
+ stderr_rd, stderr_wr = IO.pipe
114
+ Process.spawn(env, hook_file, :out => stdout_wr, :err => stderr_wr, :unsetenv_others => true)
115
+ Process.wait
116
+ exit_status = $?.exitstatus
117
+ stdout_wr.close
118
+ stderr_wr.close
119
+ result = stdout_rd.read
120
+ error_output = stderr_rd.read
121
+
122
+ logger.info("Hook #{hook} for job #{job_template_name}: #{result}")
123
+
124
+ unless exit_status == 0
125
+ exception_message = "Hook #{hook} for #{job_template_name} failed "
126
+ exception_message += "(exit: #{exit_status}) "
127
+ exception_message += " stderr: #{error_output}, stdout: #{result}"
128
+ logger.info(exception_message)
129
+
130
+ raise Bosh::Agent::MessageHandlerError, exception_message
131
+ end
132
+ result
133
+ end
134
+
135
+ def create_symlink(src, dst)
136
+ # FileUtils doesn have 'no-deference' for links - causing ln_sf to
137
+ # attempt to create target link in dst rather than to overwrite it.
138
+ # BROKEN: FileUtils.ln_sf(src, dst)
139
+ out = %x(ln -nsf #{src} #{dst} 2>&1)
140
+ unless $?.exitstatus == 0
141
+ raise Bosh::Agent::MessageHandlerError,
142
+ "Failed to link '#{src}' to '#{dst}': #{out}"
143
+ end
144
+ end
145
+
146
+ # Poor mans idempotency
147
+ def update_file(data, path)
148
+ name = File.basename(path)
149
+ dir = File.dirname(path)
150
+
151
+ if_tmp_file = Tempfile.new(name, dir)
152
+ if_tmp_file.write(data)
153
+ if_tmp_file.flush
154
+
155
+ old = nil
156
+ begin
157
+ old = Digest::SHA1.file(path).hexdigest
158
+ rescue Errno::ENOENT
159
+ logger.debug("missing file: #{path}")
160
+ end
161
+ new = Digest::SHA1.file(if_tmp_file.path).hexdigest
162
+
163
+ updated = false
164
+ unless old == new
165
+ FileUtils.cp(if_tmp_file.path, path)
166
+ updated = true
167
+ end
168
+ updated
169
+ ensure
170
+ if if_tmp_file
171
+ if_tmp_file.close
172
+ FileUtils.rm_rf(if_tmp_file.path)
173
+ end
174
+ end
175
+
176
+ def get_network_info
177
+ sigar = SigarBox.create_sigar
178
+ net_info = sigar.net_info
179
+ ifconfig = sigar.net_interface_config(net_info.default_gateway_interface)
180
+
181
+ properties = {}
182
+ properties["ip"] = ifconfig.address
183
+ properties["netmask"] = ifconfig.netmask
184
+ properties["gateway"] = net_info.default_gateway
185
+ properties
186
+ end
187
+
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,8 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh
4
+ module Agent
5
+ VERSION = '1.5.0.pre.1113'
6
+ BOSH_PROTOCOL = "1"
7
+ end
8
+ end
data/lib/bosh_agent.rb ADDED
@@ -0,0 +1,92 @@
1
+ module Bosh
2
+ end
3
+
4
+ require 'logger'
5
+ require 'time'
6
+ require 'yaml'
7
+ require 'set'
8
+
9
+ require 'nats/client'
10
+ require 'yajl'
11
+ require 'securerandom'
12
+ require 'ostruct'
13
+ require 'fileutils'
14
+ require 'resolv'
15
+ require 'ipaddr'
16
+ require 'httpclient'
17
+ require 'sigar'
18
+
19
+ require 'common/exec'
20
+ require 'common/properties'
21
+ require 'bosh/core/encryption_handler'
22
+
23
+ module Bosh::Agent
24
+ BOSH_APP = BOSH_APP_USER = BOSH_APP_GROUP = 'vcap'
25
+ end
26
+
27
+ require 'bosh_agent/ext'
28
+ require 'bosh_agent/version'
29
+
30
+ require 'bosh_agent/template'
31
+ require 'bosh_agent/errors'
32
+ require 'bosh_agent/remote_exception'
33
+
34
+ require 'bosh_agent/sigar_box'
35
+ require 'bosh_agent/config'
36
+ require 'bosh_agent/util'
37
+ require 'bosh_agent/monit'
38
+
39
+ require 'bosh_agent/infrastructure'
40
+ require 'bosh_agent/platform'
41
+
42
+ require 'bosh_agent/platform/linux'
43
+ require 'bosh_agent/platform/linux/adapter'
44
+ require 'bosh_agent/platform/linux/disk'
45
+ require 'bosh_agent/platform/linux/logrotate'
46
+ require 'bosh_agent/platform/linux/password'
47
+ require 'bosh_agent/platform/linux/network'
48
+
49
+ require 'bosh_agent/platform/ubuntu'
50
+ require 'bosh_agent/platform/ubuntu/network'
51
+
52
+ require 'bosh_agent/platform/centos'
53
+ require 'bosh_agent/platform/centos/disk'
54
+ require 'bosh_agent/platform/centos/network'
55
+
56
+ require 'bosh_agent/bootstrap'
57
+
58
+ require 'bosh_agent/alert'
59
+ require 'bosh_agent/alert_processor'
60
+ require 'bosh_agent/smtp_server'
61
+ require 'bosh_agent/heartbeat'
62
+ require 'bosh_agent/heartbeat_processor'
63
+ require 'bosh_agent/state'
64
+ require 'bosh_agent/settings'
65
+ require 'bosh_agent/file_matcher'
66
+ require 'bosh_agent/file_aggregator'
67
+ require 'bosh_agent/ntp'
68
+ require 'bosh_agent/syslog_monitor'
69
+
70
+ require 'bosh_agent/apply_plan/helpers'
71
+ require 'bosh_agent/apply_plan/job'
72
+ require 'bosh_agent/apply_plan/package'
73
+ require 'bosh_agent/apply_plan/plan'
74
+
75
+ require 'bosh_agent/disk_util'
76
+
77
+ require 'bosh_agent/message/base'
78
+
79
+ require 'bosh_agent/message/list_disk'
80
+ require 'bosh_agent/message/migrate_disk'
81
+ require 'bosh_agent/message/mount_disk'
82
+ require 'bosh_agent/message/unmount_disk'
83
+
84
+ require 'bosh_agent/message/state'
85
+ require 'bosh_agent/message/drain'
86
+ require 'bosh_agent/message/apply'
87
+ require 'bosh_agent/message/compile_package'
88
+ require 'bosh_agent/message/logs'
89
+ require 'bosh_agent/message/ssh'
90
+
91
+ require 'bosh_agent/handler'
92
+ require 'bosh_agent/runner'