miasma-aws 0.3.10 → 0.3.12
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 +5 -5
- data/CHANGELOG.md +4 -0
- data/lib/miasma-aws.rb +2 -2
- data/lib/miasma-aws/api.rb +3 -3
- data/lib/miasma-aws/api/iam.rb +16 -17
- data/lib/miasma-aws/api/sts.rb +29 -30
- data/lib/miasma-aws/version.rb +1 -1
- data/lib/miasma/contrib/aws.rb +170 -185
- data/lib/miasma/contrib/aws/auto_scale.rb +23 -25
- data/lib/miasma/contrib/aws/compute.rb +51 -56
- data/lib/miasma/contrib/aws/load_balancer.rb +64 -70
- data/lib/miasma/contrib/aws/orchestration.rb +153 -159
- data/lib/miasma/contrib/aws/storage.rb +109 -113
- data/miasma-aws.gemspec +3 -2
- metadata +33 -19
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "miasma"
|
2
2
|
|
3
3
|
module Miasma
|
4
4
|
module Models
|
@@ -7,9 +7,9 @@ module Miasma
|
|
7
7
|
class Aws < AutoScale
|
8
8
|
|
9
9
|
# Service name of the API
|
10
|
-
API_SERVICE =
|
10
|
+
API_SERVICE = "autoscaling".freeze
|
11
11
|
# Supported version of the AutoScaling API
|
12
|
-
API_VERSION =
|
12
|
+
API_VERSION = "2011-01-01".freeze
|
13
13
|
|
14
14
|
include Contrib::AwsApiCore::ApiCommon
|
15
15
|
include Contrib::AwsApiCore::RequestUtils
|
@@ -27,7 +27,7 @@ module Miasma
|
|
27
27
|
# @param group [Models::AutoScale::Group]
|
28
28
|
# @return [Models::AutoScale::Group]
|
29
29
|
def group_reload(group)
|
30
|
-
if
|
30
|
+
if group.id || group.name
|
31
31
|
load_group_data(group)
|
32
32
|
end
|
33
33
|
group
|
@@ -45,35 +45,34 @@ module Miasma
|
|
45
45
|
#
|
46
46
|
# @param group [Models::AutoScale::Group]
|
47
47
|
# @return [Array<Models::AutoScale::Group>]
|
48
|
-
def load_group_data(group=nil)
|
49
|
-
params = Smash.new(
|
50
|
-
if
|
51
|
-
params.merge(
|
48
|
+
def load_group_data(group = nil)
|
49
|
+
params = Smash.new("Action" => "DescribeAutoScalingGroups")
|
50
|
+
if group
|
51
|
+
params.merge("AutoScalingGroupNames.member.1" => group.id || group.name)
|
52
52
|
end
|
53
53
|
result = all_result_pages(nil, :body,
|
54
|
-
|
55
|
-
|
56
|
-
) do |options|
|
54
|
+
"DescribeAutoScalingGroupsResponse", "DescribeAutoScalingGroupsResult",
|
55
|
+
"AutoScalingGroups", "member") do |options|
|
57
56
|
request(
|
58
57
|
:method => :post,
|
59
|
-
:path =>
|
60
|
-
:form => options.merge(params)
|
58
|
+
:path => "/",
|
59
|
+
:form => options.merge(params),
|
61
60
|
)
|
62
61
|
end.map do |grp|
|
63
62
|
(group || Group.new(self)).load_data(
|
64
|
-
:id => grp[
|
65
|
-
:name => grp[
|
66
|
-
:servers => [grp.get(
|
67
|
-
Group::Server.new(self, :id => i[
|
63
|
+
:id => grp["AutoScalingGroupName"],
|
64
|
+
:name => grp["AutoScalingGroupName"],
|
65
|
+
:servers => [grp.get("Instances", "member")].flatten(1).compact.map { |i|
|
66
|
+
Group::Server.new(self, :id => i["InstanceId"])
|
68
67
|
},
|
69
|
-
:minimum_size => grp[
|
70
|
-
:maximum_size => grp[
|
71
|
-
:status => grp[
|
68
|
+
:minimum_size => grp["MinSize"],
|
69
|
+
:maximum_size => grp["MaxSize"],
|
70
|
+
:status => grp["Status"],
|
72
71
|
:load_balancers => [
|
73
|
-
grp.get(
|
74
|
-
].flatten(1).compact.map{|i|
|
72
|
+
grp.get("LoadBalancerNames", "member"),
|
73
|
+
].flatten(1).compact.map { |i|
|
75
74
|
Group::Balancer.new(self, :id => i, :name => i)
|
76
|
-
}
|
75
|
+
},
|
77
76
|
).valid_state
|
78
77
|
end
|
79
78
|
end
|
@@ -82,10 +81,9 @@ module Miasma
|
|
82
81
|
#
|
83
82
|
# @param options [Hash] filter
|
84
83
|
# @return [Array<Models::AutoScale::Group>]
|
85
|
-
def group_all(options={})
|
84
|
+
def group_all(options = {})
|
86
85
|
load_group_data
|
87
86
|
end
|
88
|
-
|
89
87
|
end
|
90
88
|
end
|
91
89
|
end
|
@@ -1,73 +1,71 @@
|
|
1
|
-
require
|
1
|
+
require "miasma"
|
2
2
|
|
3
3
|
module Miasma
|
4
|
-
|
5
4
|
module Models
|
6
5
|
class Compute
|
7
6
|
# Compute interface for AWS
|
8
7
|
class Aws < Compute
|
9
8
|
|
10
9
|
# Service name of the API
|
11
|
-
API_SERVICE =
|
10
|
+
API_SERVICE = "ec2".freeze
|
12
11
|
# Supported version of the EC2 API
|
13
|
-
API_VERSION =
|
12
|
+
API_VERSION = "2014-06-15".freeze
|
14
13
|
|
15
14
|
include Contrib::AwsApiCore::ApiCommon
|
16
15
|
include Contrib::AwsApiCore::RequestUtils
|
17
16
|
|
18
17
|
# @return [Smash] map state to valid internal values
|
19
18
|
SERVER_STATE_MAP = Smash.new(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
"running" => :running,
|
20
|
+
"pending" => :pending,
|
21
|
+
"shutting-down" => :pending,
|
22
|
+
"terminated" => :terminated,
|
23
|
+
"stopping" => :pending,
|
24
|
+
"stopped" => :stopped,
|
26
25
|
).to_smash(:freeze)
|
27
26
|
|
28
27
|
# @todo catch bad lookup and clear model
|
29
28
|
def server_reload(server)
|
30
29
|
result = request(
|
31
30
|
:method => :post,
|
32
|
-
:path =>
|
31
|
+
:path => "/",
|
33
32
|
:form => {
|
34
|
-
|
35
|
-
|
36
|
-
}
|
33
|
+
"Action" => "DescribeInstances",
|
34
|
+
"InstanceId.1" => server.id,
|
35
|
+
},
|
37
36
|
)
|
38
37
|
srv = result.get(:body,
|
39
|
-
|
40
|
-
|
41
|
-
)
|
38
|
+
"DescribeInstancesResponse", "reservationSet",
|
39
|
+
"item", "instancesSet", "item")
|
42
40
|
server.load_data(
|
43
41
|
:id => srv[:instanceId],
|
44
|
-
:name => [srv.fetch(:tagSet, :item, [])].flatten.map{|tag|
|
45
|
-
tag[:value] if tag.is_a?(Hash) && tag[:key] ==
|
42
|
+
:name => [srv.fetch(:tagSet, :item, [])].flatten.map { |tag|
|
43
|
+
tag[:value] if tag.is_a?(Hash) && tag[:key] == "Name"
|
46
44
|
}.compact.first,
|
47
45
|
:image_id => srv[:imageId],
|
48
46
|
:flavor_id => srv[:instanceType],
|
49
47
|
:state => SERVER_STATE_MAP.fetch(srv.get(:instanceState, :name), :pending),
|
50
48
|
:addresses_private => [
|
51
|
-
Server::Address.new(:version => 4, :address => srv[:privateIpAddress])
|
49
|
+
Server::Address.new(:version => 4, :address => srv[:privateIpAddress]),
|
52
50
|
],
|
53
51
|
:addresses_public => [
|
54
|
-
Server::Address.new(:version => 4, :address => srv[:ipAddress])
|
52
|
+
Server::Address.new(:version => 4, :address => srv[:ipAddress]),
|
55
53
|
],
|
56
54
|
:status => srv.get(:instanceState, :name),
|
57
|
-
:key_name => srv[:keyName]
|
55
|
+
:key_name => srv[:keyName],
|
58
56
|
)
|
59
57
|
server.valid_state
|
60
58
|
end
|
61
59
|
|
62
60
|
def server_destroy(server)
|
63
|
-
if
|
61
|
+
if server.persisted?
|
64
62
|
result = request(
|
65
63
|
:method => :post,
|
66
|
-
:path =>
|
64
|
+
:path => "/",
|
67
65
|
:form => {
|
68
|
-
|
69
|
-
|
70
|
-
}
|
66
|
+
"Action" => "TerminateInstances",
|
67
|
+
"InstanceId.1" => server.id,
|
68
|
+
},
|
71
69
|
)
|
72
70
|
else
|
73
71
|
raise "this doesn't even exist"
|
@@ -75,50 +73,48 @@ module Miasma
|
|
75
73
|
end
|
76
74
|
|
77
75
|
def server_save(server)
|
78
|
-
unless
|
76
|
+
unless server.persisted?
|
79
77
|
server.load_data(server.attributes)
|
80
78
|
result = request(
|
81
79
|
:method => :post,
|
82
|
-
:path =>
|
80
|
+
:path => "/",
|
83
81
|
:form => {
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
}
|
82
|
+
"Action" => "RunInstances",
|
83
|
+
"ImageId" => server.image_id,
|
84
|
+
"InstanceType" => server.flavor_id,
|
85
|
+
"KeyName" => server.key_name,
|
86
|
+
"MinCount" => 1,
|
87
|
+
"MaxCount" => 1,
|
88
|
+
},
|
91
89
|
)
|
92
90
|
server.id = result.get(:body,
|
93
|
-
|
94
|
-
)
|
91
|
+
"RunInstancesResponse", "instancesSet", "item", "instanceId")
|
95
92
|
server.valid_state
|
96
93
|
request(
|
97
94
|
:method => :post,
|
98
|
-
:path =>
|
95
|
+
:path => "/",
|
99
96
|
:form => {
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
}
|
97
|
+
"Action" => "CreateTags",
|
98
|
+
"ResourceId.1" => server.id,
|
99
|
+
"Tag.1.Key" => "Name",
|
100
|
+
"Tag.1.Value" => server.name,
|
101
|
+
},
|
105
102
|
)
|
106
103
|
else
|
107
|
-
raise
|
104
|
+
raise "WAT DO I DO!?"
|
108
105
|
end
|
109
106
|
end
|
110
107
|
|
111
108
|
# @todo need to add auto pagination helper (as common util)
|
112
109
|
def server_all
|
113
110
|
results = all_result_pages(nil, :body,
|
114
|
-
|
115
|
-
) do |options|
|
111
|
+
"DescribeInstancesResponse", "reservationSet", "item") do |options|
|
116
112
|
request(
|
117
113
|
:method => :post,
|
118
|
-
:path =>
|
114
|
+
:path => "/",
|
119
115
|
:form => options.merge(
|
120
|
-
|
121
|
-
)
|
116
|
+
"Action" => "DescribeInstances",
|
117
|
+
),
|
122
118
|
)
|
123
119
|
end
|
124
120
|
results.map do |server|
|
@@ -126,25 +122,24 @@ module Miasma
|
|
126
122
|
Server.new(
|
127
123
|
self,
|
128
124
|
:id => srv[:instanceId],
|
129
|
-
:name => srv.fetch(:tagSet, :item, []).map{|tag|
|
130
|
-
tag[:value] if tag.is_a?(Hash) && tag[:key] ==
|
125
|
+
:name => srv.fetch(:tagSet, :item, []).map { |tag|
|
126
|
+
tag[:value] if tag.is_a?(Hash) && tag[:key] == "Name"
|
131
127
|
}.compact.first,
|
132
128
|
:image_id => srv[:imageId],
|
133
129
|
:flavor_id => srv[:instanceType],
|
134
130
|
:state => SERVER_STATE_MAP.fetch(srv.get(:instanceState, :name), :pending),
|
135
131
|
:addresses_private => [
|
136
|
-
Server::Address.new(:version => 4, :address => srv[:privateIpAddress])
|
132
|
+
Server::Address.new(:version => 4, :address => srv[:privateIpAddress]),
|
137
133
|
],
|
138
134
|
:addresses_public => [
|
139
|
-
Server::Address.new(:version => 4, :address => srv[:ipAddress])
|
135
|
+
Server::Address.new(:version => 4, :address => srv[:ipAddress]),
|
140
136
|
],
|
141
137
|
:status => srv.get(:instanceState, :name),
|
142
|
-
:key_name => srv[:keyName]
|
138
|
+
:key_name => srv[:keyName],
|
143
139
|
).valid_state
|
144
140
|
end
|
145
141
|
end.flatten
|
146
142
|
end
|
147
|
-
|
148
143
|
end
|
149
144
|
end
|
150
145
|
end
|
@@ -1,70 +1,68 @@
|
|
1
|
-
require
|
1
|
+
require "miasma"
|
2
2
|
|
3
3
|
module Miasma
|
4
4
|
module Models
|
5
5
|
class LoadBalancer
|
6
6
|
# AWS load balancer API
|
7
7
|
class Aws < LoadBalancer
|
8
|
-
|
9
8
|
include Contrib::AwsApiCore::ApiCommon
|
10
9
|
include Contrib::AwsApiCore::RequestUtils
|
11
10
|
|
12
11
|
# Service name of API
|
13
|
-
API_SERVICE =
|
12
|
+
API_SERVICE = "elasticloadbalancing".freeze
|
14
13
|
# Supported version of the ELB API
|
15
|
-
API_VERSION =
|
14
|
+
API_VERSION = "2012-06-01".freeze
|
16
15
|
|
17
16
|
# Save load balancer
|
18
17
|
#
|
19
18
|
# @param balancer [Models::LoadBalancer::Balancer]
|
20
19
|
# @return [Models::LoadBalancer::Balancer]
|
21
20
|
def balancer_save(balancer)
|
22
|
-
unless
|
21
|
+
unless balancer.persisted?
|
23
22
|
params = Smash.new(
|
24
|
-
|
23
|
+
"LoadBalancerName" => balancer.name,
|
25
24
|
)
|
26
25
|
availability_zones.each_with_index do |az, i|
|
27
26
|
params["AvailabilityZones.member.#{i + 1}"] = az
|
28
27
|
end
|
29
|
-
if
|
28
|
+
if balancer.listeners
|
30
29
|
balancer.listeners.each_with_index do |listener, i|
|
31
30
|
key = "Listeners.member.#{i + 1}"
|
32
31
|
params["#{key}.Protocol"] = listener.protocol
|
33
32
|
params["#{key}.InstanceProtocol"] = listener.instance_protocol
|
34
33
|
params["#{key}.LoadBalancerPort"] = listener.load_balancer_port
|
35
34
|
params["#{key}.InstancePort"] = listener.instance_port
|
36
|
-
if
|
35
|
+
if listener.ssl_certificate_id
|
37
36
|
params["#{key}.SSLCertificateId"] = listener.ssl_certificate_id
|
38
37
|
end
|
39
38
|
end
|
40
39
|
end
|
41
40
|
result = request(
|
42
41
|
:method => :post,
|
43
|
-
:path =>
|
42
|
+
:path => "/",
|
44
43
|
:form => params.merge(
|
45
44
|
Smash.new(
|
46
|
-
|
45
|
+
"Action" => "CreateLoadBalancer",
|
47
46
|
)
|
48
|
-
)
|
47
|
+
),
|
49
48
|
)
|
50
49
|
balancer.public_addresses = [
|
51
50
|
:address => result.get(:body,
|
52
|
-
|
53
|
-
)
|
51
|
+
"CreateLoadBalancerResponse", "CreateLoadBalancerResult", "DNSName"),
|
54
52
|
]
|
55
53
|
balancer.load_data(:id => balancer.name).valid_state
|
56
|
-
if
|
54
|
+
if balancer.health_check
|
57
55
|
balancer_health_check(balancer)
|
58
56
|
end
|
59
|
-
if
|
57
|
+
if balancer.servers && !balancer.servers.empty?
|
60
58
|
balancer_set_instances(balancer)
|
61
59
|
end
|
62
60
|
else
|
63
|
-
if
|
64
|
-
if
|
61
|
+
if balancer.dirty?
|
62
|
+
if balancer.dirty?(:health_check)
|
65
63
|
balancer_health_check(balancer)
|
66
64
|
end
|
67
|
-
if
|
65
|
+
if balancer.dirty?(:servers)
|
68
66
|
balancer_set_instances(balancer)
|
69
67
|
end
|
70
68
|
balancer.reload
|
@@ -94,13 +92,13 @@ module Miasma
|
|
94
92
|
# @param balancer [Models::LoadBalancer::Balancer]
|
95
93
|
# @return [Models::LoadBalancer::Balancer]
|
96
94
|
def balancer_reload(balancer)
|
97
|
-
if
|
95
|
+
if balancer.persisted?
|
98
96
|
begin
|
99
97
|
load_balancer_data(balancer)
|
100
98
|
rescue Miasma::Error::ApiError::RequestError => e
|
101
|
-
if
|
99
|
+
if e.response_error_msg.include?("LoadBalancerNotFound")
|
102
100
|
balancer.state = :terminated
|
103
|
-
balancer.status =
|
101
|
+
balancer.status = "terminated"
|
104
102
|
balancer.valid_state
|
105
103
|
else
|
106
104
|
raise
|
@@ -114,65 +112,63 @@ module Miasma
|
|
114
112
|
#
|
115
113
|
# @param balancer [Models::LoadBalancer::Balancer]
|
116
114
|
# @return [Array<Models::LoadBalancer::Balancer>]
|
117
|
-
def load_balancer_data(balancer=nil)
|
118
|
-
params = Smash.new(
|
119
|
-
if
|
120
|
-
params[
|
115
|
+
def load_balancer_data(balancer = nil)
|
116
|
+
params = Smash.new("Action" => "DescribeLoadBalancers")
|
117
|
+
if balancer
|
118
|
+
params["LoadBalancerNames.member.1"] = balancer.id || balancer.name
|
121
119
|
end
|
122
120
|
result = all_result_pages(nil, :body,
|
123
|
-
|
124
|
-
|
125
|
-
) do |options|
|
121
|
+
"DescribeLoadBalancersResponse", "DescribeLoadBalancersResult",
|
122
|
+
"LoadBalancerDescriptions", "member") do |options|
|
126
123
|
request(
|
127
124
|
:method => :post,
|
128
|
-
:path =>
|
129
|
-
:form => options.merge(params)
|
125
|
+
:path => "/",
|
126
|
+
:form => options.merge(params),
|
130
127
|
)
|
131
128
|
end
|
132
|
-
if
|
129
|
+
if balancer
|
133
130
|
health_result = all_result_pages(nil, :body,
|
134
|
-
|
135
|
-
|
136
|
-
) do |options|
|
131
|
+
"DescribeInstanceHealthResponse", "DescribeInstanceHealthResult",
|
132
|
+
"InstanceStates", "member") do |options|
|
137
133
|
request(
|
138
134
|
:method => :post,
|
139
|
-
:path =>
|
135
|
+
:path => "/",
|
140
136
|
:form => options.merge(
|
141
|
-
|
142
|
-
|
143
|
-
)
|
137
|
+
"LoadBalancerName" => balancer.id || balancer.name,
|
138
|
+
"Action" => "DescribeInstanceHealth",
|
139
|
+
),
|
144
140
|
)
|
145
141
|
end
|
146
142
|
end
|
147
143
|
result.map do |blr|
|
148
144
|
(balancer || Balancer.new(self)).load_data(
|
149
145
|
Smash.new(
|
150
|
-
:id => blr[
|
151
|
-
:name => blr[
|
146
|
+
:id => blr["LoadBalancerName"],
|
147
|
+
:name => blr["LoadBalancerName"],
|
152
148
|
:state => :active,
|
153
|
-
:status =>
|
154
|
-
:created => blr[
|
155
|
-
:updated => blr[
|
149
|
+
:status => "ACTIVE",
|
150
|
+
:created => blr["CreatedTime"],
|
151
|
+
:updated => blr["CreatedTime"],
|
156
152
|
:public_addresses => [
|
157
153
|
Balancer::Address.new(
|
158
|
-
:address => blr[
|
159
|
-
:version => 4
|
160
|
-
)
|
154
|
+
:address => blr["DNSName"],
|
155
|
+
:version => 4,
|
156
|
+
),
|
161
157
|
],
|
162
|
-
:servers => [blr.get(
|
163
|
-
Balancer::Server.new(self.api_for(:compute), :id => i[
|
164
|
-
}
|
158
|
+
:servers => [blr.get("Instances", "member")].flatten(1).compact.map { |i|
|
159
|
+
Balancer::Server.new(self.api_for(:compute), :id => i["InstanceId"])
|
160
|
+
},
|
165
161
|
).merge(
|
166
162
|
health_result.nil? ? {} : Smash.new(
|
167
|
-
:server_states => health_result.nil? ? nil : health_result.map{|i|
|
163
|
+
:server_states => health_result.nil? ? nil : health_result.map { |i|
|
168
164
|
Balancer::ServerState.new(
|
169
165
|
self.api_for(:compute),
|
170
|
-
:id => i[
|
171
|
-
:status => i[
|
172
|
-
:reason => i[
|
173
|
-
:state => i[
|
166
|
+
:id => i["InstanceId"],
|
167
|
+
:status => i["State"],
|
168
|
+
:reason => i["ReasonCode"],
|
169
|
+
:state => i["State"] == "InService" ? :up : :down,
|
174
170
|
)
|
175
|
-
}
|
171
|
+
},
|
176
172
|
)
|
177
173
|
)
|
178
174
|
).valid_state
|
@@ -184,17 +180,17 @@ module Miasma
|
|
184
180
|
# @param balancer [Models::LoadBalancer::Balancer]
|
185
181
|
# @return [TrueClass, FalseClass]
|
186
182
|
def balancer_destroy(balancer)
|
187
|
-
if
|
183
|
+
if balancer.persisted?
|
188
184
|
request(
|
189
185
|
:method => :post,
|
190
|
-
:path =>
|
186
|
+
:path => "/",
|
191
187
|
:form => Smash.new(
|
192
|
-
|
193
|
-
|
194
|
-
)
|
188
|
+
"Action" => "DeleteLoadBalancer",
|
189
|
+
"LoadBalancerName" => balancer.name,
|
190
|
+
),
|
195
191
|
)
|
196
192
|
balancer.state = :pending
|
197
|
-
balancer.status =
|
193
|
+
balancer.status = "DELETE_IN_PROGRESS"
|
198
194
|
balancer.valid_state
|
199
195
|
true
|
200
196
|
else
|
@@ -206,7 +202,7 @@ module Miasma
|
|
206
202
|
#
|
207
203
|
# @param options [Hash] filter
|
208
204
|
# @return [Array<Models::LoadBalancer::Balancer>]
|
209
|
-
def balancer_all(options={})
|
205
|
+
def balancer_all(options = {})
|
210
206
|
load_balancer_data
|
211
207
|
end
|
212
208
|
|
@@ -217,21 +213,19 @@ module Miasma
|
|
217
213
|
memoize(:availability_zones) do
|
218
214
|
res = api_for(:compute).request(
|
219
215
|
:method => :post,
|
220
|
-
:path =>
|
216
|
+
:path => "/",
|
221
217
|
:form => Smash.new(
|
222
|
-
|
223
|
-
)
|
218
|
+
"Action" => "DescribeAvailabilityZones",
|
219
|
+
),
|
224
220
|
).fetch(:body,
|
225
|
-
|
226
|
-
)
|
221
|
+
"DescribeAvailabilityZonesResponse", "availabilityZoneInfo", "item", [])
|
227
222
|
[res].flatten.compact.map do |item|
|
228
|
-
if
|
229
|
-
item[
|
223
|
+
if item["zoneState"] == "available"
|
224
|
+
item["zoneName"]
|
230
225
|
end
|
231
226
|
end.compact
|
232
227
|
end
|
233
228
|
end
|
234
|
-
|
235
229
|
end
|
236
230
|
end
|
237
231
|
end
|