-
Notifications
You must be signed in to change notification settings - Fork 897
/
Copy pathinter_region_api_method_relay.rb
116 lines (97 loc) · 3.67 KB
/
inter_region_api_method_relay.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
require 'manageiq-api-client'
module InterRegionApiMethodRelay
class InterRegionApiMethodRelayError < RuntimeError; end
INITIAL_INSTANCE_WAIT = 1.second
MAX_INSTANCE_WAIT = 1.minute
def self.extended(klass)
unless klass.const_defined?("InstanceMethodRelay")
instance_relay = klass.const_set("InstanceMethodRelay", Module.new)
klass.prepend(instance_relay)
end
unless klass.const_defined?("ClassMethodRelay")
class_relay = klass.const_set("ClassMethodRelay", Module.new)
klass.singleton_class.prepend(class_relay)
end
end
def api_relay_method(method, action = method)
relay = const_get("InstanceMethodRelay")
collection_name = collection_for_class
relay.class_eval do
define_method(method) do |*meth_args, &meth_block|
api_args = yield(*meth_args) if block_given?
if in_current_region?
super(*meth_args, &meth_block)
else
InterRegionApiMethodRelay.exec_api_call(region_number, collection_name, action, api_args) do
[{:id => id}]
end
end
end
end
end
def api_relay_class_method(method, action = method)
relay = const_get("ClassMethodRelay")
collection_name = collection_for_class
raise ArgumentError, "A block is required to determine target object region and API arguments" unless block_given?
relay.class_eval do
define_method(method) do |*meth_args, &meth_block|
record_id, api_args = yield(*meth_args)
if record_id.nil? || id_in_current_region?(record_id)
super(*meth_args, &meth_block)
else
InterRegionApiMethodRelay.exec_api_call(id_to_region(record_id), collection_name, action, api_args)
end
end
end
end
def self.api_client_connection_for_region(region_number, user = User.current_userid)
region = MiqRegion.find_by(:region => region_number)
url = region.remote_ws_url
if url.nil?
_log.error("The remote region [#{region_number}] does not have a web service address.")
raise "Failed to establish API connection to region #{region_number}"
end
ManageIQ::API::Client.new(
:url => url,
:miqtoken => region.api_system_auth_token(user),
:ssl => {:verify => false}
)
end
def self.exec_api_call(region, collection_name, action, api_args = nil, &resource_block)
api_args ||= {}
collection = api_client_connection_for_region(region).public_send(collection_name)
result = if resource_block
collection.public_send(action, api_args, &resource_block)
else
collection.public_send(action, api_args)
end
case result
when ManageIQ::API::Client::ActionResult
raise InterRegionApiMethodRelayError, result.message if result.failed?
when ManageIQ::API::Client::Resource
instance_for_resource(result)
else
raise InterRegionApiMethodRelayError, "Got unexpected API result object #{result.class}"
end
end
def self.instance_for_resource(resource)
klass = Api::CollectionConfig.new.klass(resource.collection.name)
wait = INITIAL_INSTANCE_WAIT
while wait < MAX_INSTANCE_WAIT
instance = klass.find_by(:id => resource.id)
return instance if instance
sleep(wait)
wait *= 2
end
raise InterRegionApiMethodRelayError, "Failed to retrieve #{klass} instance with id #{resource.id}"
end
private
def collection_for_class
collection_name = Api::CollectionConfig.new.name_for_klass(self)
unless collection_name
_log.error("No API endpoint found for class #{name}")
raise NotImplementedError, "No API endpoint found for class #{name}"
end
collection_name
end
end