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,45 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class HeartbeatProcessor
|
5
|
+
|
6
|
+
MAX_OUTSTANDING_HEARTBEATS = 2
|
7
|
+
|
8
|
+
def enable(interval)
|
9
|
+
unless EM.reactor_running?
|
10
|
+
raise Bosh::Agent::HeartbeatError, "Event loop must be running in order to enable heartbeats"
|
11
|
+
end
|
12
|
+
|
13
|
+
if @timer
|
14
|
+
Config.logger.warn("Heartbeat timer already running, canceling")
|
15
|
+
disable
|
16
|
+
end
|
17
|
+
|
18
|
+
@pending = 0
|
19
|
+
|
20
|
+
@timer = EM.add_periodic_timer(interval) do
|
21
|
+
beat
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def disable
|
26
|
+
Config.logger.info("Disabled heartbeats")
|
27
|
+
@timer.cancel if @timer
|
28
|
+
@timer = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def beat
|
32
|
+
raise HeartbeatError, "#{@pending} outstanding heartbeat(s)" if @pending > MAX_OUTSTANDING_HEARTBEATS
|
33
|
+
|
34
|
+
Heartbeat.new.send_via_mbus do
|
35
|
+
@pending -= 1
|
36
|
+
end
|
37
|
+
@pending += 1
|
38
|
+
rescue => e
|
39
|
+
Config.logger.warn("Error sending heartbeat: #{e}")
|
40
|
+
Config.logger.warn(e.backtrace.join("\n"))
|
41
|
+
raise e if @pending > MAX_OUTSTANDING_HEARTBEATS
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
require "thin"
|
4
|
+
require "sinatra"
|
5
|
+
require "monitor"
|
6
|
+
require "common/ssl"
|
7
|
+
|
8
|
+
module Bosh::Agent
|
9
|
+
|
10
|
+
class HTTPHandler < Handler
|
11
|
+
|
12
|
+
def self.start
|
13
|
+
new.start
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
handler = self
|
18
|
+
|
19
|
+
uri = URI.parse(Config.mbus)
|
20
|
+
|
21
|
+
@server = Thin::Server.new(uri.host, uri.port) do
|
22
|
+
use Rack::CommonLogger
|
23
|
+
|
24
|
+
if uri.userinfo
|
25
|
+
use Rack::Auth::Basic do |user, password|
|
26
|
+
"#{user}:#{password}" == uri.userinfo
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
map "/" do
|
31
|
+
run AgentController.new(handler)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
certificate = Bosh::Ssl::Certificate.new('agent.key',
|
36
|
+
'agent.cert',
|
37
|
+
uri.host
|
38
|
+
).load_or_create
|
39
|
+
|
40
|
+
@server.ssl = true
|
41
|
+
@server.ssl_options = {:ssl_verify => false, :ssl_key_file => certificate.key_path, :ssl_cert_file => certificate.certificate_path }
|
42
|
+
|
43
|
+
@server.start!
|
44
|
+
end
|
45
|
+
|
46
|
+
def shutdown
|
47
|
+
@logger.info("Exit")
|
48
|
+
@server.stop
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_message(json)
|
52
|
+
result = {}
|
53
|
+
result.extend(MonitorMixin)
|
54
|
+
|
55
|
+
cond = result.new_cond
|
56
|
+
timeout_time = Time.now.to_f + 30
|
57
|
+
|
58
|
+
@callback = Proc.new do |response|
|
59
|
+
result.synchronize do
|
60
|
+
result.merge!(response)
|
61
|
+
cond.signal
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
super(json)
|
66
|
+
|
67
|
+
result.synchronize do
|
68
|
+
while result.empty?
|
69
|
+
timeout = timeout_time - Time.now.to_f
|
70
|
+
unless timeout > 0
|
71
|
+
raise "Timed out"
|
72
|
+
end
|
73
|
+
cond.wait(timeout)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
def publish(reply_to, payload, &blk)
|
81
|
+
response = @callback.call(payload)
|
82
|
+
blk.call if blk
|
83
|
+
response
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
module Message
|
88
|
+
class ReleaseApplySpec < Base
|
89
|
+
|
90
|
+
def self.process(args)
|
91
|
+
self.new.apply_spec
|
92
|
+
end
|
93
|
+
|
94
|
+
def release_apply_spec
|
95
|
+
#generated from bosh-release and baked into the stemcell
|
96
|
+
"/var/vcap/micro/apply_spec.yml"
|
97
|
+
end
|
98
|
+
|
99
|
+
def apply_spec
|
100
|
+
Psych.load_file(release_apply_spec)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class AgentController < Sinatra::Base
|
106
|
+
|
107
|
+
def initialize(handler)
|
108
|
+
super()
|
109
|
+
@handler = handler
|
110
|
+
end
|
111
|
+
|
112
|
+
configure do
|
113
|
+
set(:show_exceptions, false)
|
114
|
+
set(:raise_errors, false)
|
115
|
+
set(:dump_errors, false)
|
116
|
+
end
|
117
|
+
|
118
|
+
post "/agent" do
|
119
|
+
body = request.env["rack.input"].read
|
120
|
+
response = handle_message(body)
|
121
|
+
content_type(:json)
|
122
|
+
response
|
123
|
+
end
|
124
|
+
|
125
|
+
def handle_message(json)
|
126
|
+
begin
|
127
|
+
payload = @handler.handle_message(json)
|
128
|
+
rescue => e
|
129
|
+
payload = RemoteException.from(e).to_hash
|
130
|
+
end
|
131
|
+
|
132
|
+
Yajl::Encoder.encode(payload, :terminator => "\n")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Infrastructure::Aws::Registry
|
5
|
+
class << self
|
6
|
+
|
7
|
+
API_TIMEOUT = 86400 * 3
|
8
|
+
CONNECT_TIMEOUT = 30
|
9
|
+
INSTANCE_DATA_URI = "http://169.254.169.254/latest"
|
10
|
+
|
11
|
+
def get_uri(uri)
|
12
|
+
client = HTTPClient.new
|
13
|
+
client.send_timeout = API_TIMEOUT
|
14
|
+
client.receive_timeout = API_TIMEOUT
|
15
|
+
client.connect_timeout = CONNECT_TIMEOUT
|
16
|
+
|
17
|
+
response = client.get(INSTANCE_DATA_URI + uri)
|
18
|
+
unless response.status == 200
|
19
|
+
raise(LoadSettingsError, "Instance metadata endpoint returned " \
|
20
|
+
"HTTP #{response.status}")
|
21
|
+
end
|
22
|
+
|
23
|
+
response.body
|
24
|
+
rescue HTTPClient::BadResponseError => e
|
25
|
+
raise(LoadSettingsError,
|
26
|
+
"Received bad HTTP response for #{uri}: #{e.inspect}")
|
27
|
+
rescue HTTPClient::TimeoutError
|
28
|
+
raise(LoadSettingsError,
|
29
|
+
"Timed out reading uri #{uri}, " \
|
30
|
+
"please make sure agent is running on EC2 instance")
|
31
|
+
rescue URI::Error, SocketError, Errno::ECONNREFUSED, SystemCallError => e
|
32
|
+
raise(LoadSettingsError,
|
33
|
+
"Error requesting current instance id from #{uri} #{e.inspect}")
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Reads current instance id from EC2 metadata. We are assuming
|
38
|
+
# instance id cannot change while current process is running
|
39
|
+
# and thus memoizing it.
|
40
|
+
def current_instance_id
|
41
|
+
return @current_instance_id if @current_instance_id
|
42
|
+
@current_instance_id = get_uri("/meta-data/instance-id/")
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_json_from_url(url)
|
46
|
+
client = HTTPClient.new
|
47
|
+
client.send_timeout = API_TIMEOUT
|
48
|
+
client.receive_timeout = API_TIMEOUT
|
49
|
+
client.connect_timeout = CONNECT_TIMEOUT
|
50
|
+
|
51
|
+
headers = {"Accept" => "application/json"}
|
52
|
+
response = client.get(url, {}, headers)
|
53
|
+
|
54
|
+
if response.status != 200
|
55
|
+
raise(LoadSettingsError,
|
56
|
+
"Cannot read settings for `#{url}' from registry, " \
|
57
|
+
"got HTTP #{response.status}")
|
58
|
+
end
|
59
|
+
|
60
|
+
body = Yajl::Parser.parse(response.body)
|
61
|
+
unless body.is_a?(Hash)
|
62
|
+
raise(LoadSettingsError,
|
63
|
+
"Invalid response from #{url} , Hash expected, " \
|
64
|
+
"got #{body.class}: #{body}")
|
65
|
+
end
|
66
|
+
|
67
|
+
body
|
68
|
+
|
69
|
+
rescue HTTPClient::BadResponseError => e
|
70
|
+
raise(LoadSettingsError,
|
71
|
+
"Received bad HTTP response from registry: #{e.inspect}")
|
72
|
+
rescue HTTPClient::TimeoutError
|
73
|
+
raise(LoadSettingsError,
|
74
|
+
"Timed out reading json from #{url}, " \
|
75
|
+
"please make sure agent is running on EC2 instance")
|
76
|
+
rescue URI::Error, SocketError, Errno::ECONNREFUSED, SystemCallError => e
|
77
|
+
raise(LoadSettingsError,
|
78
|
+
"Error requesting registry information #{e.inspect}")
|
79
|
+
rescue Yajl::ParseError => e
|
80
|
+
raise(LoadSettingsError,
|
81
|
+
"Cannot parse settings for from registry #{e.inspect}")
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_registry_endpoint
|
85
|
+
user_data = get_json_from_url(INSTANCE_DATA_URI + "/user-data")
|
86
|
+
unless user_data.has_key?("registry") &&
|
87
|
+
user_data["registry"].has_key?("endpoint")
|
88
|
+
raise(LoadSettingsError,
|
89
|
+
"Cannot parse user data for endpoint #{user_data.inspect}")
|
90
|
+
end
|
91
|
+
Bosh::Agent::Config.logger.info("got user_data: #{user_data}")
|
92
|
+
lookup_registry(user_data)
|
93
|
+
end
|
94
|
+
|
95
|
+
# If the registry endpoint is specified with a bosh dns name, e.g.
|
96
|
+
# 0.registry.default.aws.bosh, then the agent needs to lookup the
|
97
|
+
# name and insert the IP address, as the agent doesn't update
|
98
|
+
# resolv.conf until after the bootstrap is run.
|
99
|
+
def lookup_registry(user_data)
|
100
|
+
endpoint = user_data["registry"]["endpoint"]
|
101
|
+
|
102
|
+
# if we get data from an old director which doesn't set dns
|
103
|
+
# info, there is noting we can do, so just return the endpoint
|
104
|
+
if user_data["dns"].nil? || user_data["dns"]["nameserver"].nil?
|
105
|
+
return endpoint
|
106
|
+
end
|
107
|
+
|
108
|
+
hostname = extract_registry_hostname(endpoint)
|
109
|
+
|
110
|
+
# if the registry endpoint is an IP address, just return the endpoint
|
111
|
+
unless (IPAddr.new(hostname) rescue(nil)).nil?
|
112
|
+
return endpoint
|
113
|
+
end
|
114
|
+
|
115
|
+
nameservers = user_data["dns"]["nameserver"]
|
116
|
+
ip = bosh_lookup(hostname, nameservers)
|
117
|
+
inject_registry_ip(ip, endpoint)
|
118
|
+
rescue Resolv::ResolvError => e
|
119
|
+
raise(LoadSettingsError,
|
120
|
+
"Cannot lookup #{hostname} using #{nameservers.join(', ')}" +
|
121
|
+
"\n#{e.inspect}")
|
122
|
+
end
|
123
|
+
|
124
|
+
def bosh_lookup(hostname, nameservers)
|
125
|
+
resolver = Resolv.new([Resolv::Hosts.new, Resolv::DNS.new(nameserver: nameservers)])
|
126
|
+
resolver.each_address(hostname) do |address|
|
127
|
+
begin
|
128
|
+
return address if IPAddr.new(address).ipv4?
|
129
|
+
rescue ArgumentError
|
130
|
+
end
|
131
|
+
end
|
132
|
+
raise Resolv::ResolvError, "Could not resolve #{hostname}"
|
133
|
+
end
|
134
|
+
|
135
|
+
def extract_registry_hostname(endpoint)
|
136
|
+
uri = URI.parse(endpoint)
|
137
|
+
hostname = uri.hostname
|
138
|
+
|
139
|
+
if hostname.nil?
|
140
|
+
raise LoadSettingsError, "Could not extract registry hostname"
|
141
|
+
end
|
142
|
+
|
143
|
+
hostname
|
144
|
+
end
|
145
|
+
|
146
|
+
def inject_registry_ip(ip, endpoint)
|
147
|
+
uri = URI.parse(endpoint)
|
148
|
+
uri.hostname = ip
|
149
|
+
uri.to_s
|
150
|
+
end
|
151
|
+
|
152
|
+
def get_openssh_key
|
153
|
+
get_uri("/meta-data/public-keys/0/openssh-key")
|
154
|
+
end
|
155
|
+
|
156
|
+
def get_settings
|
157
|
+
@registry_endpoint ||= get_registry_endpoint
|
158
|
+
url = "#{@registry_endpoint}/instances/#{current_instance_id}/settings"
|
159
|
+
body = get_json_from_url(url)
|
160
|
+
|
161
|
+
settings = Yajl::Parser.parse(body["settings"])
|
162
|
+
unless settings.is_a?(Hash)
|
163
|
+
raise(LoadSettingsError, "Invalid settings format, " \
|
164
|
+
"Hash expected, got #{settings.class}: " \
|
165
|
+
"#{settings}")
|
166
|
+
end
|
167
|
+
|
168
|
+
settings
|
169
|
+
|
170
|
+
rescue Yajl::ParseError
|
171
|
+
raise(LoadSettingsError,
|
172
|
+
"Cannot parse settings from registry #{@registry_endpoint}")
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Infrastructure::Aws::Settings
|
5
|
+
|
6
|
+
VIP_NETWORK_TYPE = "vip"
|
7
|
+
DHCP_NETWORK_TYPE = "dynamic"
|
8
|
+
MANUAL_NETWORK_TYPE = "manual"
|
9
|
+
|
10
|
+
SUPPORTED_NETWORK_TYPES = [
|
11
|
+
VIP_NETWORK_TYPE, DHCP_NETWORK_TYPE, MANUAL_NETWORK_TYPE
|
12
|
+
]
|
13
|
+
|
14
|
+
AUTHORIZED_KEYS = File.join("/home/", BOSH_APP_USER, ".ssh/authorized_keys")
|
15
|
+
|
16
|
+
def logger
|
17
|
+
Bosh::Agent::Config.logger
|
18
|
+
end
|
19
|
+
|
20
|
+
def authorized_keys
|
21
|
+
AUTHORIZED_KEYS
|
22
|
+
end
|
23
|
+
|
24
|
+
def setup_openssh_key
|
25
|
+
public_key = Infrastructure::Aws::Registry.get_openssh_key
|
26
|
+
if public_key.nil? || public_key.empty?
|
27
|
+
return
|
28
|
+
end
|
29
|
+
FileUtils.mkdir_p(File.dirname(authorized_keys))
|
30
|
+
FileUtils.chmod(0700, File.dirname(authorized_keys))
|
31
|
+
FileUtils.chown(Bosh::Agent::BOSH_APP_USER, Bosh::Agent::BOSH_APP_GROUP,
|
32
|
+
File.dirname(authorized_keys))
|
33
|
+
File.open(authorized_keys, "w") { |f| f.write(public_key) }
|
34
|
+
FileUtils.chown(Bosh::Agent::BOSH_APP_USER, Bosh::Agent::BOSH_APP_GROUP,
|
35
|
+
authorized_keys)
|
36
|
+
FileUtils.chmod(0644, authorized_keys)
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_settings
|
40
|
+
setup_openssh_key
|
41
|
+
Infrastructure::Aws::Registry.get_settings
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_network_settings(network_name, properties)
|
45
|
+
type = properties["type"] || "manual"
|
46
|
+
unless type && SUPPORTED_NETWORK_TYPES.include?(type)
|
47
|
+
raise Bosh::Agent::StateError,
|
48
|
+
"Unsupported network type '%s', valid types are: %s" %
|
49
|
+
[type, SUPPORTED_NETWORK_TYPES.join(', ')]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Nothing to do for "vip" and "manual" networks
|
53
|
+
return nil if [VIP_NETWORK_TYPE, MANUAL_NETWORK_TYPE].include? type
|
54
|
+
|
55
|
+
Bosh::Agent::Util.get_network_info
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Infrastructure::Aws
|
5
|
+
require 'bosh_agent/infrastructure/aws/settings'
|
6
|
+
require 'bosh_agent/infrastructure/aws/registry'
|
7
|
+
|
8
|
+
def load_settings
|
9
|
+
Settings.new.load_settings
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_network_settings(network_name, properties)
|
13
|
+
Settings.new.get_network_settings(network_name, properties)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Infrastructure::Dummy
|
5
|
+
|
6
|
+
def load_settings
|
7
|
+
{
|
8
|
+
"blobstore" => {
|
9
|
+
"provider" => Bosh::Agent::Config.blobstore_provider,
|
10
|
+
"options" => Bosh::Agent::Config.blobstore_options,
|
11
|
+
},
|
12
|
+
"ntp" => [],
|
13
|
+
"disks" => {
|
14
|
+
"persistent" => {},
|
15
|
+
},
|
16
|
+
"mbus" => Bosh::Agent::Config.mbus,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_network_settings(network_name, properties)
|
21
|
+
# Nothing to do
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Agent
|
4
|
+
class Infrastructure::Openstack::Registry
|
5
|
+
class << self
|
6
|
+
|
7
|
+
attr_accessor :user_data
|
8
|
+
|
9
|
+
HTTP_API_TIMEOUT = 300
|
10
|
+
HTTP_CONNECT_TIMEOUT = 30
|
11
|
+
META_DATA_URI = "http://169.254.169.254/latest"
|
12
|
+
USER_DATA_FILE = File.join(File::SEPARATOR, "var", BOSH_APP_USER, "bosh", "user_data.json")
|
13
|
+
|
14
|
+
##
|
15
|
+
# Returns the logger.
|
16
|
+
#
|
17
|
+
# @return [Logger] Bosh Agent logger
|
18
|
+
def logger
|
19
|
+
Bosh::Agent::Config.logger
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Gets the OpenSSH public key. First we try to get it from the OpenStack meta data endpoint, if we fail,
|
24
|
+
# then we fallback to the injected user data file.
|
25
|
+
#
|
26
|
+
# @return [String] OpenSSH key
|
27
|
+
def get_openssh_key
|
28
|
+
get_uri(META_DATA_URI + "/meta-data/public-keys/0/openssh-key")
|
29
|
+
rescue LoadSettingsError => e
|
30
|
+
logger.info("Failed to get OpenSSH public key from OpenStack meta data endpoint: #{e.message}")
|
31
|
+
user_data = parse_user_data(get_user_data_from_file)
|
32
|
+
unless user_data.has_key?("openssh") && user_data["openssh"].has_key?("public_key")
|
33
|
+
raise LoadSettingsError, "Cannot get OpenSSH public key from injected user data file: #{user_data.inspect}"
|
34
|
+
end
|
35
|
+
user_data["openssh"]["public_key"]
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Gets the settings for this agent from the Bosh registry.
|
40
|
+
#
|
41
|
+
# @return [Hash] Agent Settings
|
42
|
+
def get_settings
|
43
|
+
@registry_endpoint ||= get_registry_endpoint
|
44
|
+
url = "#{@registry_endpoint}/instances/#{get_server_name}/settings"
|
45
|
+
raw_response = get_uri(url)
|
46
|
+
|
47
|
+
registry_data = Yajl::Parser.parse(raw_response)
|
48
|
+
unless registry_data.is_a?(Hash) && registry_data.has_key?("settings")
|
49
|
+
raise LoadSettingsError, "Invalid response received from Bosh registry, " \
|
50
|
+
"got #{registry_data.class}: #{registry_data}"
|
51
|
+
end
|
52
|
+
|
53
|
+
settings = Yajl::Parser.parse(registry_data["settings"])
|
54
|
+
unless settings.is_a?(Hash)
|
55
|
+
raise(LoadSettingsError, "Invalid settings received from Bosh registry, " \
|
56
|
+
"got #{settings.class}: #{settings}")
|
57
|
+
end
|
58
|
+
|
59
|
+
settings
|
60
|
+
rescue Yajl::ParseError => e
|
61
|
+
raise LoadSettingsError, "Cannot parse settings from Bosh registry, got #{raw_response} - #{e.message}"
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Gets the server name from OpenStack user data.
|
66
|
+
#
|
67
|
+
# @return [String] OpenStack server name
|
68
|
+
def get_server_name
|
69
|
+
user_data = get_user_data
|
70
|
+
unless user_data.has_key?("server") && user_data["server"].has_key?("name")
|
71
|
+
raise LoadSettingsError, "Cannot get OpenStack server name from user data #{user_data.inspect}"
|
72
|
+
end
|
73
|
+
user_data["server"]["name"]
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Gets the Bosh registry endpoint from OpenStack user data.
|
78
|
+
#
|
79
|
+
# @return [String] Bosh registry endpoint
|
80
|
+
def get_registry_endpoint
|
81
|
+
user_data = get_user_data
|
82
|
+
unless user_data.has_key?("registry") && user_data["registry"].has_key?("endpoint")
|
83
|
+
raise LoadSettingsError, "Cannot get Bosh registry endpoint from user data #{user_data.inspect}"
|
84
|
+
end
|
85
|
+
lookup_registry_endpoint(user_data)
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# If the Bosh registry endpoint is specified with a Bosh DNS name, i.e. 0.registry.default.openstack.bosh,
|
90
|
+
# then the agent needs to lookup the name and insert the IP address, as the agent doesn't update
|
91
|
+
# resolv.conf until after the bootstrap is run.
|
92
|
+
#
|
93
|
+
# @param [Hash] user_data OpenStack user data (generated by the CPI)
|
94
|
+
# @return [String] Bosh registry endpoint
|
95
|
+
def lookup_registry_endpoint(user_data)
|
96
|
+
registry_endpoint = user_data["registry"]["endpoint"]
|
97
|
+
|
98
|
+
# If user data doesn't contain dns info, there is noting we can do, so just return the endpoint
|
99
|
+
return registry_endpoint if user_data["dns"].nil? || user_data["dns"]["nameserver"].nil?
|
100
|
+
|
101
|
+
# If the endpoint is an IP address, just return the endpoint
|
102
|
+
registry_hostname = extract_registry_hostname(registry_endpoint)
|
103
|
+
return registry_endpoint unless (IPAddr.new(registry_hostname) rescue(nil)).nil?
|
104
|
+
|
105
|
+
nameservers = user_data["dns"]["nameserver"]
|
106
|
+
registry_ip = lookup_registry_ip_address(registry_hostname, nameservers)
|
107
|
+
inject_registry_ip_address(registry_ip, registry_endpoint)
|
108
|
+
rescue Resolv::ResolvError => e
|
109
|
+
raise LoadSettingsError, "Cannot lookup #{registry_hostname} using #{nameservers.join(", ")}: #{e.inspect}"
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Extracts the hostname from the Bosh registry endpoint.
|
114
|
+
#
|
115
|
+
# @param [String] endpoint Bosh registry endpoint
|
116
|
+
# @return [String] Bosh registry hostname
|
117
|
+
def extract_registry_hostname(endpoint)
|
118
|
+
match = endpoint.match(%r{https*://([^:]+):})
|
119
|
+
unless match && match.size == 2
|
120
|
+
raise LoadSettingsError, "Cannot extract Bosh registry hostname from #{endpoint}"
|
121
|
+
end
|
122
|
+
match[1]
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Lookups for the Bosh registry IP address.
|
127
|
+
#
|
128
|
+
# @param [String] hostname Bosh registry hostname
|
129
|
+
# @param [Array] nameservers Array containing nameserver address
|
130
|
+
# @return [Resolv::IPv4] Bosh registry IP address
|
131
|
+
def lookup_registry_ip_address(hostname, nameservers)
|
132
|
+
resolver = Resolv::DNS.new(:nameserver => nameservers)
|
133
|
+
resolver.getaddress(hostname)
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Injects an IP address in the Bosh registry endpoint.
|
138
|
+
#
|
139
|
+
# @param [Resolv::IPv4] ip Bosh registry IP address
|
140
|
+
# @param [String] endpoint Bosh registry endpoint
|
141
|
+
# @return [String] Bosh registry endpoint
|
142
|
+
def inject_registry_ip_address(ip, endpoint)
|
143
|
+
endpoint.sub(%r{//[^:]+:}, "//#{ip}:")
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Gets the OpenStack user data. First we try to get it from the OpenStack user data endpoint, if we fail,
|
148
|
+
# then we fallback to the injected user data file.
|
149
|
+
#
|
150
|
+
# @return [Hash] OpenStack user data
|
151
|
+
def get_user_data
|
152
|
+
return @user_data if @user_data
|
153
|
+
begin
|
154
|
+
raw_user_data = get_uri(META_DATA_URI + "/user-data")
|
155
|
+
rescue LoadSettingsError => e
|
156
|
+
logger.info("Failed to get user data from OpenStack user data endpoint: #{e.message}")
|
157
|
+
raw_user_data = get_user_data_from_file
|
158
|
+
end
|
159
|
+
|
160
|
+
logger.info("OpenStack user data: #{raw_user_data.inspect}")
|
161
|
+
@user_data = parse_user_data(raw_user_data)
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Gets the OpenStack user data from the injected user data file.
|
166
|
+
#
|
167
|
+
# @return [String] OpenStack user data
|
168
|
+
def get_user_data_from_file
|
169
|
+
File.read(USER_DATA_FILE)
|
170
|
+
rescue SystemCallError => e
|
171
|
+
raise LoadSettingsError, "Failed to get user data from OpenStack injected user data file: #{e.message}"
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Parses the OpenStack user data.
|
176
|
+
#
|
177
|
+
# @param [String] raw_user_data Raw OpenStack user data
|
178
|
+
# @return [Hash] OpenStack user data
|
179
|
+
def parse_user_data(raw_user_data)
|
180
|
+
begin
|
181
|
+
user_data = Yajl::Parser.parse(raw_user_data)
|
182
|
+
rescue Yajl::ParseError => e
|
183
|
+
raise LoadSettingsError, "Cannot parse user data #{raw_user_data.inspect}: #{e.message}"
|
184
|
+
end
|
185
|
+
|
186
|
+
unless user_data.is_a?(Hash)
|
187
|
+
raise LoadSettingsError, "Invalid user data format, Hash expected, got #{user_data.class}: #{user_data}"
|
188
|
+
end
|
189
|
+
|
190
|
+
user_data
|
191
|
+
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# Sends GET request to an specified URI.
|
195
|
+
#
|
196
|
+
# @param [String] uri URI to request
|
197
|
+
# @return [String] Response body
|
198
|
+
def get_uri(uri)
|
199
|
+
client = HTTPClient.new
|
200
|
+
client.send_timeout = HTTP_API_TIMEOUT
|
201
|
+
client.receive_timeout = HTTP_API_TIMEOUT
|
202
|
+
client.connect_timeout = HTTP_CONNECT_TIMEOUT
|
203
|
+
|
204
|
+
headers = {"Accept" => "application/json"}
|
205
|
+
response = client.get(uri, {}, headers)
|
206
|
+
unless response.status == 200
|
207
|
+
raise LoadSettingsError, "Endpoint #{uri} returned HTTP #{response.status}"
|
208
|
+
end
|
209
|
+
|
210
|
+
response.body
|
211
|
+
rescue HTTPClient::TimeoutError
|
212
|
+
raise LoadSettingsError, "Timed out reading endpoint #{uri}"
|
213
|
+
rescue HTTPClient::BadResponseError => e
|
214
|
+
raise LoadSettingsError, "Received bad HTTP response from endpoint #{uri}: #{e.inspect}"
|
215
|
+
rescue URI::Error, SocketError, Errno::ECONNREFUSED, SystemCallError => e
|
216
|
+
raise LoadSettingsError, "Error requesting endpoint #{uri}: #{e.inspect}"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|