topological_inventory-providers-common 1.0.8 → 2.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -2
- data/lib/topological_inventory/providers/common.rb +1 -2
- data/lib/topological_inventory/providers/common/collector.rb +12 -4
- data/lib/topological_inventory/providers/common/collectors_pool.rb +2 -1
- data/lib/topological_inventory/providers/common/logging.rb +10 -4
- data/lib/topological_inventory/providers/common/mixins/sources_api.rb +58 -0
- data/lib/topological_inventory/providers/common/mixins/topology_api.rb +26 -0
- data/lib/topological_inventory/providers/common/mixins/x_rh_headers.rb +24 -0
- data/lib/topological_inventory/providers/common/operations/async_worker.rb +56 -0
- data/lib/topological_inventory/providers/common/operations/health_check.rb +15 -0
- data/lib/topological_inventory/providers/common/operations/source.rb +119 -144
- data/lib/topological_inventory/providers/common/sources_api_client.rb +92 -0
- data/lib/topological_inventory/providers/common/topology_api_client.rb +43 -0
- data/lib/topological_inventory/providers/common/version.rb +1 -1
- data/spec/support/shared/availability_check.rb +1 -1
- data/spec/topological_inventory/providers/common/operations/async_worker_spec.rb +36 -0
- data/topological_inventory-providers-common.gemspec +1 -1
- metadata +16 -7
- data/lib/topological_inventory/providers/common/operations/endpoint_client.rb +0 -65
- data/lib/topological_inventory/providers/common/operations/processor.rb +0 -135
- data/lib/topological_inventory/providers/common/operations/sources_api_client.rb +0 -94
- data/lib/topological_inventory/providers/common/operations/topology_api_client.rb +0 -28
- data/spec/topological_inventory/providers/common/operations/processor_spec.rb +0 -102
@@ -0,0 +1,92 @@
|
|
1
|
+
require "sources-api-client"
|
2
|
+
|
3
|
+
module TopologicalInventory
|
4
|
+
module Providers
|
5
|
+
module Common
|
6
|
+
class SourcesApiClient < ::SourcesApiClient::ApiClient
|
7
|
+
delegate :update_source, :update_endpoint, :update_application, :to => :api
|
8
|
+
|
9
|
+
INTERNAL_API_PATH = '//internal/v1.0'.freeze
|
10
|
+
|
11
|
+
def initialize(identity = nil)
|
12
|
+
super(::SourcesApiClient::Configuration.default)
|
13
|
+
self.identity = identity
|
14
|
+
self.api = init_default_api
|
15
|
+
end
|
16
|
+
|
17
|
+
def init_default_api
|
18
|
+
default_headers.merge!(identity) if identity.present?
|
19
|
+
::SourcesApiClient::DefaultApi.new(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def fetch_default_endpoint(source_id)
|
23
|
+
endpoints = api.list_source_endpoints(source_id)&.data || []
|
24
|
+
endpoints.find(&:default)
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_application(source_id)
|
28
|
+
applications = api.list_source_applications(source_id)&.data || []
|
29
|
+
applications.first
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch_authentication(source_id, default_endpoint = nil, authtype = nil)
|
33
|
+
endpoint = default_endpoint || fetch_default_endpoint(source_id)
|
34
|
+
return if endpoint.nil?
|
35
|
+
|
36
|
+
endpoint_authentications = api.list_endpoint_authentications(endpoint.id.to_s).data || []
|
37
|
+
return if endpoint_authentications.empty?
|
38
|
+
|
39
|
+
auth_id = if authtype.nil?
|
40
|
+
endpoint_authentications.first&.id
|
41
|
+
else
|
42
|
+
endpoint_authentications.detect { |a| a.authtype = authtype }&.id
|
43
|
+
end
|
44
|
+
return if auth_id.nil?
|
45
|
+
|
46
|
+
fetch_authentication_with_password(auth_id)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_accessor :identity, :api, :custom_base_path
|
52
|
+
|
53
|
+
def fetch_authentication_with_password(auth_id)
|
54
|
+
on_internal_api do
|
55
|
+
local_var_path = "/authentications/#{auth_id}"
|
56
|
+
|
57
|
+
query_params = "expose_encrypted_attribute[]=password"
|
58
|
+
|
59
|
+
header_params = {'Accept' => select_header_accept(['application/json'])}
|
60
|
+
return_type = 'Authentication'
|
61
|
+
data, _, _ = call_api(:GET, local_var_path,
|
62
|
+
:header_params => header_params,
|
63
|
+
:query_params => query_params,
|
64
|
+
:auth_names => ['UserSecurity'],
|
65
|
+
:return_type => return_type)
|
66
|
+
data
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_request_url(path)
|
71
|
+
# Add leading and trailing slashes to path
|
72
|
+
path = "/#{path}".gsub(/\/+/, '/')
|
73
|
+
URI.encode((custom_base_url || @config.base_url) + path)
|
74
|
+
end
|
75
|
+
|
76
|
+
def custom_base_url
|
77
|
+
return nil if custom_base_path.nil?
|
78
|
+
|
79
|
+
url = "#{@config.scheme}://#{[@config.host, custom_base_path].join('/').gsub(/\/+/, '/')}".sub(/\/+\z/, '')
|
80
|
+
URI.encode(url)
|
81
|
+
end
|
82
|
+
|
83
|
+
def on_internal_api
|
84
|
+
self.custom_base_path = INTERNAL_API_PATH
|
85
|
+
yield
|
86
|
+
ensure
|
87
|
+
self.custom_base_path = nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "topological_inventory-api-client"
|
2
|
+
|
3
|
+
module TopologicalInventory
|
4
|
+
module Providers
|
5
|
+
module Common
|
6
|
+
class TopologyApiClient < ::TopologicalInventoryApiClient::ApiClient
|
7
|
+
attr_accessor :api
|
8
|
+
|
9
|
+
def initialize(identity = nil)
|
10
|
+
super(::TopologicalInventoryApiClient::Configuration.default)
|
11
|
+
|
12
|
+
self.identity = identity
|
13
|
+
self.api = init_default_api
|
14
|
+
end
|
15
|
+
|
16
|
+
def init_default_api
|
17
|
+
default_headers.merge!(identity) if identity.present?
|
18
|
+
::TopologicalInventoryApiClient::DefaultApi.new(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_task(task_id, source_id: nil, state:, status:, target_type: nil, target_source_ref: nil, context: nil)
|
22
|
+
params = {'state' => state,
|
23
|
+
'status' => status}
|
24
|
+
params['context'] = context if context
|
25
|
+
params['source_id'] = source_id if source_id
|
26
|
+
params['target_type'] = target_type if target_type
|
27
|
+
params['target_source_ref'] = target_source_ref if target_source_ref
|
28
|
+
task = TopologicalInventoryApiClient::Task.new(params)
|
29
|
+
api.update_task(task_id, task)
|
30
|
+
end
|
31
|
+
|
32
|
+
def svc_instance_url(service_instance)
|
33
|
+
rest_api_path = '/service_instances/{id}'.sub('{' + 'id' + '}', service_instance&.id.to_s)
|
34
|
+
build_request(:GET, rest_api_path).url
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_accessor :identity
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -94,7 +94,7 @@ RSpec.shared_examples "availability_check" do
|
|
94
94
|
stub_patch(:source, source_patch_body)
|
95
95
|
|
96
96
|
# Check
|
97
|
-
api_client = subject.send(:
|
97
|
+
api_client = subject.send(:sources_api)
|
98
98
|
expect(api_client).not_to receive(:update_endpoint)
|
99
99
|
|
100
100
|
subject.availability_check
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "topological_inventory/providers/common/operations/async_worker"
|
2
|
+
|
3
|
+
describe TopologicalInventory::Providers::Common::Operations::AsyncWorker do
|
4
|
+
let(:queue) { double }
|
5
|
+
let(:impl) { double }
|
6
|
+
let(:msg) { double }
|
7
|
+
subject { described_class.new(impl, queue) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
allow(queue).to receive(:length).and_return(0)
|
11
|
+
allow(msg).to receive(:message).and_return("Source.availability_check")
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when the message is able to be processed" do
|
15
|
+
before do
|
16
|
+
allow(impl).to receive(:process!).with(msg)
|
17
|
+
allow(msg).to receive(:ack)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "drains messages that are added to the queue" do
|
21
|
+
expect(impl).to receive(:process!).with(msg).once
|
22
|
+
subject.send(:process_message, msg)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when the message results in an error" do
|
27
|
+
before do
|
28
|
+
allow(impl).to receive(:process!).with(msg).and_raise(StandardError.new("boom!"))
|
29
|
+
end
|
30
|
+
|
31
|
+
it "ack's the message on failure" do
|
32
|
+
expect(msg).to receive(:ack).once
|
33
|
+
subject.send(:process_message, msg)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_runtime_dependency "manageiq-loggers", ">= 0.4.2"
|
30
30
|
spec.add_runtime_dependency "sources-api-client", "~> 3.0"
|
31
31
|
spec.add_runtime_dependency "topological_inventory-api-client", "~> 3.0", ">= 3.0.1"
|
32
|
-
spec.add_runtime_dependency "topological_inventory-ingress_api-client", "~> 1.0"
|
32
|
+
spec.add_runtime_dependency "topological_inventory-ingress_api-client", "~> 1.0", ">= 1.0.3"
|
33
33
|
|
34
34
|
spec.add_development_dependency "bundler", "~> 2.0"
|
35
35
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: topological_inventory-providers-common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Slemr
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -113,6 +113,9 @@ dependencies:
|
|
113
113
|
- - "~>"
|
114
114
|
- !ruby/object:Gem::Version
|
115
115
|
version: '1.0'
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 1.0.3
|
116
119
|
type: :runtime
|
117
120
|
prerelease: false
|
118
121
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -120,6 +123,9 @@ dependencies:
|
|
120
123
|
- - "~>"
|
121
124
|
- !ruby/object:Gem::Version
|
122
125
|
version: '1.0'
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: 1.0.3
|
123
129
|
- !ruby/object:Gem::Dependency
|
124
130
|
name: bundler
|
125
131
|
requirement: !ruby/object:Gem::Requirement
|
@@ -246,13 +252,16 @@ files:
|
|
246
252
|
- lib/topological_inventory/providers/common/collector/parser.rb
|
247
253
|
- lib/topological_inventory/providers/common/collectors_pool.rb
|
248
254
|
- lib/topological_inventory/providers/common/logging.rb
|
249
|
-
- lib/topological_inventory/providers/common/
|
250
|
-
- lib/topological_inventory/providers/common/
|
255
|
+
- lib/topological_inventory/providers/common/mixins/sources_api.rb
|
256
|
+
- lib/topological_inventory/providers/common/mixins/topology_api.rb
|
257
|
+
- lib/topological_inventory/providers/common/mixins/x_rh_headers.rb
|
258
|
+
- lib/topological_inventory/providers/common/operations/async_worker.rb
|
259
|
+
- lib/topological_inventory/providers/common/operations/health_check.rb
|
251
260
|
- lib/topological_inventory/providers/common/operations/source.rb
|
252
|
-
- lib/topological_inventory/providers/common/operations/sources_api_client.rb
|
253
|
-
- lib/topological_inventory/providers/common/operations/topology_api_client.rb
|
254
261
|
- lib/topological_inventory/providers/common/save_inventory/exception.rb
|
255
262
|
- lib/topological_inventory/providers/common/save_inventory/saver.rb
|
263
|
+
- lib/topological_inventory/providers/common/sources_api_client.rb
|
264
|
+
- lib/topological_inventory/providers/common/topology_api_client.rb
|
256
265
|
- lib/topological_inventory/providers/common/version.rb
|
257
266
|
- spec/spec_helper.rb
|
258
267
|
- spec/support/inventory_helper.rb
|
@@ -262,7 +271,7 @@ files:
|
|
262
271
|
- spec/topological_inventory/providers/common/collectors/inventory_collection_wrapper_spec.rb
|
263
272
|
- spec/topological_inventory/providers/common/collectors_pool_spec.rb
|
264
273
|
- spec/topological_inventory/providers/common/logger_spec.rb
|
265
|
-
- spec/topological_inventory/providers/common/operations/
|
274
|
+
- spec/topological_inventory/providers/common/operations/async_worker_spec.rb
|
266
275
|
- spec/topological_inventory/providers/common/operations/source_spec.rb
|
267
276
|
- spec/topological_inventory/providers/common/save_inventory/saver_spec.rb
|
268
277
|
- spec/topological_inventory/providers/common_spec.rb
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require "topological_inventory/providers/common/operations/topology_api_client"
|
2
|
-
require "topological_inventory/providers/common/operations/sources_api_client"
|
3
|
-
|
4
|
-
module TopologicalInventory
|
5
|
-
module Providers
|
6
|
-
module Common
|
7
|
-
module Operations
|
8
|
-
class EndpointClient
|
9
|
-
include TopologyApiClient
|
10
|
-
|
11
|
-
def initialize(source_id, task_id, identity = nil)
|
12
|
-
self.identity = identity
|
13
|
-
self.source_id = source_id
|
14
|
-
self.task_id = task_id
|
15
|
-
end
|
16
|
-
|
17
|
-
def order_service(service_offering, service_plan, order_params)
|
18
|
-
raise NotImplementedError, "#{__method__} must be implemented in a subclass"
|
19
|
-
end
|
20
|
-
|
21
|
-
def source_ref_of(endpoint_svc_instance)
|
22
|
-
raise NotImplementedError, "#{__method__} must be implemented in a subclass"
|
23
|
-
end
|
24
|
-
|
25
|
-
def wait_for_provision_complete(source_id, endpoint_svc_instance, context = {})
|
26
|
-
raise NotImplementedError, "#{__method__} must be implemented in a subclass"
|
27
|
-
end
|
28
|
-
|
29
|
-
def provisioned_successfully?(endpoint_svc_instance)
|
30
|
-
raise NotImplementedError, "#{__method__} must be implemented in a subclass"
|
31
|
-
end
|
32
|
-
|
33
|
-
# Endpoint for conversion of provisioned service's status to
|
34
|
-
# TopologicalInventory Task's status
|
35
|
-
def task_status_for(endpoint_svc_instance)
|
36
|
-
raise NotImplementedError, "#{__method__} must be implemented in a subclass"
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
attr_accessor :identity, :task_id, :source_id
|
42
|
-
|
43
|
-
def sources_api
|
44
|
-
@sources_api ||= SourcesApiClient.new(identity)
|
45
|
-
end
|
46
|
-
|
47
|
-
def default_endpoint
|
48
|
-
@default_endpoint ||= sources_api.fetch_default_endpoint(source_id)
|
49
|
-
raise "Sources API: Endpoint not found! (source id: #{source_id})" if @default_endpoint.nil?
|
50
|
-
|
51
|
-
@default_endpoint
|
52
|
-
end
|
53
|
-
|
54
|
-
def authentication
|
55
|
-
@authentication ||= sources_api.fetch_authentication(source_id, default_endpoint)
|
56
|
-
end
|
57
|
-
|
58
|
-
def verify_ssl_mode
|
59
|
-
default_endpoint.verify_ssl ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,135 +0,0 @@
|
|
1
|
-
require "topological_inventory/providers/common/operations/topology_api_client"
|
2
|
-
|
3
|
-
module TopologicalInventory
|
4
|
-
module Providers
|
5
|
-
module Common
|
6
|
-
module Operations
|
7
|
-
class Processor
|
8
|
-
include Logging
|
9
|
-
include TopologyApiClient
|
10
|
-
|
11
|
-
SLEEP_POLL = 10
|
12
|
-
POLL_TIMEOUT = 1800
|
13
|
-
|
14
|
-
def self.process!(message)
|
15
|
-
model, method = message.headers['message_type'].to_s.split(".")
|
16
|
-
new(model, method, message.payload).process
|
17
|
-
end
|
18
|
-
|
19
|
-
# @param payload [Hash] https://github.com/ManageIQ/topological_inventory-api/blob/master/app/controllers/api/v0/service_plans_controller.rb#L32-L41
|
20
|
-
def initialize(model, method, payload, metrics = nil)
|
21
|
-
self.model = model
|
22
|
-
self.method = method
|
23
|
-
self.params = payload["params"]
|
24
|
-
self.identity = payload["request_context"]
|
25
|
-
self.metrics = metrics
|
26
|
-
end
|
27
|
-
|
28
|
-
def process
|
29
|
-
logger.info("Processing #{model}##{method} [#{params}]...")
|
30
|
-
result = order_service(params)
|
31
|
-
logger.info("Processing #{model}##{method} [#{params}]...Complete")
|
32
|
-
|
33
|
-
result
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
attr_accessor :identity, :model, :method, :metrics, :params
|
39
|
-
|
40
|
-
def endpoint_client(source_id, task_id, identity)
|
41
|
-
raise NotImplementedError, "#{__method__} must be implemented in a subclass as kind of TopologicalInventory::Providers::Common::EndpointClient class"
|
42
|
-
end
|
43
|
-
|
44
|
-
def order_service(params)
|
45
|
-
task_id, service_offering_id, service_plan_id, order_params = params.values_at("task_id", "service_offering_id", "service_plan_id", "order_params")
|
46
|
-
|
47
|
-
service_plan = topology_api_client.show_service_plan(service_plan_id) if service_plan_id.present?
|
48
|
-
service_offering_id = service_plan.service_offering_id if service_offering_id.nil? && service_plan.present?
|
49
|
-
service_offering = topology_api_client.show_service_offering(service_offering_id)
|
50
|
-
|
51
|
-
source_id = service_offering.source_id
|
52
|
-
client = endpoint_client(source_id, task_id, identity)
|
53
|
-
|
54
|
-
logger.info("Ordering #{service_offering.name}...")
|
55
|
-
remote_service_instance = client.order_service(service_offering, service_plan.presence, order_params)
|
56
|
-
logger.info("Ordering #{service_offering.name}...Complete")
|
57
|
-
|
58
|
-
poll_order_complete_thread(task_id, source_id, remote_service_instance)
|
59
|
-
rescue StandardError => err
|
60
|
-
metrics&.record_error
|
61
|
-
logger.error("[Task #{task_id}] Ordering error: #{err}\n#{err.backtrace.join("\n")}")
|
62
|
-
update_task(task_id, :state => "completed", :status => "error", :context => {:error => err.to_s})
|
63
|
-
end
|
64
|
-
|
65
|
-
def poll_order_complete_thread(task_id, source_id, remote_svc_instance)
|
66
|
-
Thread.new do
|
67
|
-
begin
|
68
|
-
poll_order_complete(task_id, source_id, remote_svc_instance)
|
69
|
-
rescue StandardError => err
|
70
|
-
logger.error("[Task #{task_id}] Waiting for complete: #{err}\n#{err.backtrace.join("\n")}")
|
71
|
-
update_task(task_id, :state => "completed", :status => "warn", :context => {:error => err.to_s})
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def poll_order_complete(task_id, source_id, remote_svc_instance)
|
77
|
-
client = endpoint_client(source_id, task_id, identity)
|
78
|
-
|
79
|
-
context = {
|
80
|
-
:service_instance => {
|
81
|
-
:source_id => source_id,
|
82
|
-
:source_ref => client.source_ref_of(remote_svc_instance)
|
83
|
-
}
|
84
|
-
}
|
85
|
-
|
86
|
-
remote_svc_instance = client.wait_for_provision_complete(task_id, remote_svc_instance, context)
|
87
|
-
|
88
|
-
if client.provisioned_successfully?(remote_svc_instance)
|
89
|
-
if (service_instance = load_topological_svc_instance(source_id, client.source_ref_of(remote_svc_instance))).present?
|
90
|
-
context[:service_instance][:id] = service_instance.id
|
91
|
-
context[:service_instance][:url] = svc_instance_url(service_instance)
|
92
|
-
else
|
93
|
-
logger.warn("Failed to get service_instance API URL (endpoint's service instance: #{remote_svc_instance.inspect})")
|
94
|
-
end
|
95
|
-
end
|
96
|
-
update_task(task_id, :state => "completed", :status => client.task_status_for(remote_svc_instance), :context => context)
|
97
|
-
end
|
98
|
-
|
99
|
-
def load_topological_svc_instance(source_id, source_ref)
|
100
|
-
api = topology_api_client.api_client
|
101
|
-
|
102
|
-
count = 0
|
103
|
-
timeout_count = POLL_TIMEOUT / SLEEP_POLL
|
104
|
-
|
105
|
-
header_params = { 'Accept' => api.select_header_accept(['application/json']) }
|
106
|
-
query_params = { :'source_id' => source_id, :'source_ref' => source_ref }
|
107
|
-
return_type = 'ServiceInstancesCollection'
|
108
|
-
|
109
|
-
service_instance = nil
|
110
|
-
loop do
|
111
|
-
data, _status_code, _headers = api.call_api(:GET, "/service_instances",
|
112
|
-
:header_params => header_params,
|
113
|
-
:query_params => query_params,
|
114
|
-
:auth_names => ['UserSecurity'],
|
115
|
-
:return_type => return_type)
|
116
|
-
|
117
|
-
service_instance = data.data&.first if data.meta.count > 0
|
118
|
-
break if service_instance.present?
|
119
|
-
|
120
|
-
break if (count += 1) >= timeout_count
|
121
|
-
|
122
|
-
sleep(SLEEP_POLL) # seconds
|
123
|
-
end
|
124
|
-
|
125
|
-
if service_instance.nil?
|
126
|
-
logger.error("Failed to find service_instance by source_id [#{source_id}] source_ref [#{source_ref}]")
|
127
|
-
end
|
128
|
-
|
129
|
-
service_instance
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|