sensu-plugins-aws 3.2.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,98 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # s3-billing
4
+ #
5
+ # DESCRIPTION:
6
+ # Gets Billing metrics from CloudWatch and puts them in Graphite for longer term storage
7
+ #
8
+ # OUTPUT:
9
+ # metric-data
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: aws-sdk
16
+ # gem: sensu-plugin
17
+ # gem: sensu-plugins-aws
18
+ #
19
+ # USAGE:
20
+ # metrics-billing.rb
21
+ #
22
+ # NOTES:
23
+ #
24
+ # LICENSE:
25
+ # Copyright (c) 2015, Olivier Bazoud, olivier.bazoud@gmail.com
26
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
27
+ # for details.
28
+ #
29
+
30
+ require 'sensu-plugin/metric/cli'
31
+ require 'aws-sdk'
32
+ require 'sensu-plugins-aws'
33
+
34
+ class BillingMetrics < Sensu::Plugin::Metric::CLI::Graphite
35
+ include Common
36
+
37
+ option :services_name,
38
+ short: '-s SERVICES_NAME',
39
+ long: '--services-name SERVICES_NAME',
40
+ description: 'The name of the AWS service (http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/billing-metricscollected.html)',
41
+ default: 'AmazonEC2,AWSDataTransfer'
42
+
43
+ option :scheme,
44
+ description: 'Metric naming scheme, text to prepend to metric',
45
+ short: '-s SCHEME',
46
+ long: '--scheme SCHEME',
47
+ default: 'sensu.aws.billing'
48
+
49
+ option :aws_region,
50
+ short: '-r AWS_REGION',
51
+ long: '--aws-region REGION',
52
+ description: 'AWS Region (defaults to us-east-1).',
53
+ default: 'us-east-1'
54
+
55
+ def run
56
+ begin
57
+ cw = Aws::CloudWatch::Client.new(aws_config)
58
+ now = Time.now
59
+ r = cw.get_metric_statistics(
60
+ namespace: 'AWS/Billing',
61
+ metric_name: 'EstimatedCharges',
62
+ dimensions: [
63
+ {
64
+ name: 'Currency',
65
+ value: 'USD'
66
+ }
67
+ ],
68
+ start_time: (now.utc - 6 * 60 * 60).iso8601,
69
+ end_time: now.utc.iso8601,
70
+ period: 6 * 60 * 60,
71
+ statistics: ['Maximum'],
72
+ unit: 'None'
73
+ )
74
+ output "#{config[:scheme]}.total.estimated_charges", r[:datapoints][0].maximum, r[:datapoints][0][:timestamp].to_i unless r[:datapoints][0].nil?
75
+
76
+ config[:services_name].split(',').each do |service_name|
77
+ r = cw.get_metric_statistics(
78
+ namespace: 'AWS/Billing',
79
+ metric_name: 'EstimatedCharges',
80
+ dimensions: [
81
+ { name: 'Currency', value: 'USD' },
82
+ { name: 'ServiceName', value: service_name }
83
+ ],
84
+ start_time: (now.utc - 6 * 60 * 60).iso8601,
85
+ end_time: now.utc.iso8601,
86
+ period: 6 * 60 * 60,
87
+ statistics: ['Maximum'],
88
+ unit: 'None'
89
+ )
90
+ output "#{config[:scheme]}.total.#{service_name}", r[:datapoints][0].maximum, r[:datapoints][0][:timestamp].to_i unless r[:datapoints][0].nil?
91
+ end
92
+
93
+ rescue => e
94
+ critical "Error: exception: #{e}"
95
+ end
96
+ ok
97
+ end
98
+ end
@@ -3,7 +3,7 @@
3
3
  # elasticache-metrics
4
4
  #
5
5
  # DESCRIPTION:
6
- # Fetch Elasticache metrics from CloudWatch
6
+ # Gets latency metrics from CloudWatch and puts them in Graphite for longer term storage
7
7
  #
8
8
  # OUTPUT:
9
9
  # metric-data
@@ -12,203 +12,154 @@
12
12
  # Linux
13
13
  #
14
14
  # DEPENDENCIES:
15
- # gem: aws-sdk-v1
15
+ # gem: aws-sdk
16
16
  # gem: sensu-plugin
17
+ # gem: sensu-plugin-aws
18
+ # gem: time
17
19
  #
18
20
  # USAGE:
19
- # elasticache-metrics.rb -n rediscluster -c redis -a key -k secret
20
- # elasticache-metrics.rb -n memcachedcluster -c memcached -a key -k secret
21
21
  #
22
- # NOTES:
23
- # Redis: http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CacheMetrics.Redis.html
24
- # Memcached: http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CacheMetrics.Memcached.html
25
22
  #
26
- # By default fetches all available statistics from one minute ago. You may need to fetch further back than this;
23
+ # NOTES:
24
+ # Returns latency statistics by default. You can specify any valid ASG metric type, see
25
+ # http://http://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/as-metricscollected.html
27
26
  #
28
27
  # LICENSE:
29
- # Copyright 2014 Yann Verry
28
+ # Peter Hoppe <peter.hoppe.extern@bertelsmann.de>
30
29
  # Released under the same terms as Sensu (the MIT license); see LICENSE
31
30
  # for details.
32
31
  #
33
32
 
34
33
  require 'sensu-plugin/metric/cli'
35
- require 'aws-sdk-v1'
36
-
37
- class ElastiCacheMetrics < Sensu::Plugin::Metric::CLI::Graphite
38
- option :cacheclusterid,
39
- description: 'Name of the Cache Cluster',
40
- short: '-n ELASTICACHE_NAME',
41
- long: '--name ELASTICACHE_NAME',
42
- required: true
43
-
44
- option :cachenodeid,
45
- description: 'Cache Node ID',
46
- short: '-i CACHE_NODE_ID',
47
- long: '--cache-node-id CACHE_NODE_ID',
48
- default: '0001'
49
-
50
- option :elasticachetype,
51
- description: 'Elasticache type redis or memcached',
52
- short: '-c TYPE',
53
- long: '--cachetype TYPE',
54
- required: true
34
+ require 'aws-sdk'
35
+ require 'sensu-plugins-aws'
36
+ require 'time'
55
37
 
38
+ class ElasticMetrics < Sensu::Plugin::Metric::CLI::Graphite
39
+ include Common
56
40
  option :scheme,
57
41
  description: 'Metric naming scheme, text to prepend to metric',
58
42
  short: '-s SCHEME',
59
43
  long: '--scheme SCHEME',
60
44
  default: ''
61
45
 
62
- option :fetch_age,
63
- description: 'How long ago to fetch metrics for',
64
- short: '-f AGE',
65
- long: '--fetch_age',
66
- default: 60,
67
- proc: proc(&:to_i)
68
-
69
- option :aws_access_key,
70
- short: '-a AWS_ACCESS_KEY',
71
- long: '--aws-access-key AWS_ACCESS_KEY',
72
- description: "AWS Access Key. Either set ENV['AWS_ACCESS_KEY'] or provide it as an option",
73
- default: ENV['AWS_ACCESS_KEY']
74
-
75
- option :aws_secret_access_key,
76
- short: '-k AWS_SECRET_KEY',
77
- long: '--aws-secret-access-key AWS_SECRET_KEY',
78
- description: "AWS Secret Access Key. Either set ENV['AWS_SECRET_KEY'] or provide it as an option",
79
- default: ENV['AWS_SECRET_KEY']
46
+ option :statistic,
47
+ description: 'Statistics type',
48
+ short: '-t STATISTIC',
49
+ long: '--statistic',
50
+ default: ''
80
51
 
81
52
  option :aws_region,
82
53
  short: '-r AWS_REGION',
83
54
  long: '--aws-region REGION',
84
55
  description: 'AWS Region (defaults to us-east-1).',
85
- default: 'us-east-1'
86
-
87
- def aws_config
88
- { access_key_id: config[:aws_access_key],
89
- secret_access_key: config[:aws_secret_access_key],
90
- region: config[:aws_region] }
56
+ default: ENV['AWS_REGION']
57
+
58
+ option :end_time,
59
+ short: '-t T',
60
+ long: '--end-time TIME',
61
+ default: Time.now,
62
+ proc: proc { |a| Time.parse a },
63
+ description: 'CloudWatch metric statistics end time'
64
+
65
+ option :period,
66
+ short: '-p N',
67
+ long: '--period SECONDS',
68
+ default: 60,
69
+ proc: proc(&:to_i),
70
+ description: 'CloudWatch metric statistics period'
71
+
72
+ def cloud_watch
73
+ @cloud_watch = Aws::CloudWatch::Client.new
91
74
  end
92
75
 
93
- def run
94
- graphitepath = if config[:scheme] == ''
95
- "elasticache.#{config[:cacheclusterid]}"
96
- else
97
- config[:scheme]
98
- end
99
-
100
- dimensions = if config[:cachenodeid]
101
- [{
102
- 'name' => 'CacheClusterId',
103
- 'value' => config[:cacheclusterid]
104
- }, {
105
- 'name' => 'CacheNodeId',
106
- 'value' => config[:cachenodeid]
107
- }]
108
- else
109
- [{
110
- 'name' => 'CacheClusterId',
111
- 'value' => config[:cacheclusterid]
112
- }]
113
- end
114
-
115
- statistic_type = {
116
- 'redis' => {
117
- 'CPUUtilization' => 'Percent',
118
- 'BytesUsedForCache' => 'Bytes',
119
- 'SwapUsage' => 'Bytes',
120
- 'FreeableMemory' => 'Bytes',
121
- 'NetworkBytesIn' => 'Bytes',
122
- 'NetworkBytesOut' => 'Bytes',
123
- 'GetTypeCmds' => 'Count',
124
- 'SetTypeCmds' => 'Count',
125
- 'KeyBasedCmds' => 'Count',
126
- 'StringBasedCmds' => 'Count',
127
- 'HashBasedCmds' => 'Count',
128
- 'ListBasedCmds' => 'Count',
129
- 'SetBasedCmds' => 'Count',
130
- 'SortedSetBasedCmds' => 'Count',
131
- 'CurrItems' => 'Count'
132
- },
133
- 'memcached' => {
134
- 'CPUUtilization' => 'Percent',
135
- 'SwapUsage' => 'Bytes',
136
- 'FreeableMemory' => 'Bytes',
137
- 'NetworkBytesIn' => 'Bytes',
138
- 'NetworkBytesOut' => 'Bytes',
139
- 'BytesUsedForCacheItems' => 'Bytes',
140
- 'BytesReadIntoMemcached' => 'Bytes',
141
- 'BytesWrittenOutFromMemcached' => 'Bytes',
142
- 'CasBadval' => 'Count',
143
- 'CasHits' => 'Count',
144
- 'CasMisses' => 'Count',
145
- 'CmdFlush' => 'Count',
146
- 'CmdGet' => 'Count',
147
- 'CmdSet' => 'Count',
148
- 'CurrConnections' => 'Count',
149
- 'CurrItems' => 'Count',
150
- 'DecrHits' => 'Count',
151
- 'DecrMisses' => 'Count',
152
- 'DeleteHits' => 'Count',
153
- 'DeleteMisses' => 'Count',
154
- 'Evictions' => 'Count',
155
- 'GetHits' => 'Count',
156
- 'GetMisses' => 'Count',
157
- 'IncrHits' => 'Count',
158
- 'IncrMisses' => 'Count',
159
- 'Reclaimed' => 'Count',
160
- 'BytesUsedForHash' => 'Bytes',
161
- 'CmdConfigGet' => 'Count',
162
- 'CmdConfigSet' => 'Count',
163
- 'CmdTouch' => 'Count',
164
- 'CurrConfig' => 'Count',
165
- 'EvictedUnfetched' => 'Count',
166
- 'ExpiredUnfetched' => 'Count',
167
- 'SlabsMoved' => 'Count',
168
- 'TouchHits' => 'Count',
169
- 'TouchMisses' => 'Count',
170
- 'NewConnections' => 'Count',
171
- 'NewItems' => 'Count',
172
- 'UnusedMemory' => 'Bytes'
173
- }
174
- }
175
-
176
- begin
177
- et = Time.now - config[:fetch_age]
178
- st = et - 60
179
-
180
- cw = AWS::CloudWatch::Client.new aws_config
181
-
182
- # define all options
183
- options = {
184
- 'namespace' => 'AWS/ElastiCache',
185
- 'metric_name' => config[:metric],
186
- 'dimensions' => dimensions,
187
- 'start_time' => st.iso8601,
188
- 'end_time' => et.iso8601,
189
- 'period' => 60,
190
- 'statistics' => ['Average']
191
- }
76
+ def elasticaches
77
+ @elasticaches = Aws::ElastiCache::Client.new
78
+ end
192
79
 
193
- result = {}
80
+ def cloud_watch_metric(metric_name, value, cache_cluster_id)
81
+ cloud_watch.get_metric_statistics(
82
+ namespace: 'AWS/ElastiCache',
83
+ metric_name: metric_name,
84
+ dimensions: [
85
+ {
86
+ name: 'CacheClusterId',
87
+ value: cache_cluster_id
88
+ }
89
+ ],
90
+ statistics: [value],
91
+ start_time: config[:end_time] - config[:period],
92
+ end_time: config[:end_time],
93
+ period: config[:period]
94
+ )
95
+ end
194
96
 
195
- # Fetch all metrics by elasticachetype (redis or memcached).
196
- statistic_type[config[:elasticachetype]].each do |m|
197
- options['metric_name'] = m[0] # override metric
198
- r = cw.get_metric_statistics(options)
199
- result[m[0]] = r[:datapoints][0] unless r[:datapoints][0].nil?
200
- end
97
+ def print_statistics(cache_cluster_id, statistics)
98
+ result = {}
99
+ statistics.each do |key, static|
100
+ r = cloud_watch_metric(key, static, cache_cluster_id)
101
+ result['elasticache.' + cache_cluster_id + '.' + key] = r[:datapoints][0] unless r[:datapoints][0].nil?
102
+ end
103
+ return unless result.nil?
104
+ result.each do |key, value|
105
+ output key.downcase.to_s, value.average, value[:timestamp].to_i
106
+ end
107
+ end
201
108
 
202
- unless result.nil?
203
- result.each do |name, d|
204
- # We only return data when we have some to return
205
- output graphitepath + '.' + name.downcase, d[:average], d[:timestamp].to_i
109
+ def run
110
+ elasticaches.describe_cache_clusters.cache_clusters.each do |elasticache|
111
+ if elasticache.engine.include? 'redis'
112
+ if config[:statistic] == ''
113
+ default_statistic_per_metric = {
114
+ 'BytesUsedForCache' => 'Average',
115
+ 'CacheHits' => 'Average',
116
+ 'CacheMisses' => 'Average',
117
+ 'CurrConnections' => 'Average',
118
+ 'Evictions' => 'Average',
119
+ 'HyperLogLogBasedCmds' => 'Average',
120
+ 'NewConnections' => 'Average',
121
+ 'Reclaimed' => 'Average',
122
+ 'ReplicationBytes' => 'Average',
123
+ 'ReplicationLag' => 'Average',
124
+ 'SaveInProgress' => 'Average'
125
+ }
126
+ statistic = default_statistic_per_metric
127
+ else
128
+ statistic = config[:statistic]
129
+ end
130
+ print_statistics(elasticache.cache_cluster_id, statistic)
131
+ elsif elasticache.engine.include? 'memcached'
132
+ if config[:statistic] == ''
133
+ default_statistic_per_metric = {
134
+ 'BytesReadIntoMemcached' => 'Average',
135
+ 'BytesUsedForCacheItems' => 'Average',
136
+ 'BytesWrittenOutFromMemcached' => 'Average',
137
+ 'CasBadval' => 'Average',
138
+ 'CasHits' => 'Average',
139
+ 'CasMisses' => 'Average',
140
+ 'CmdFlush' => 'Average',
141
+ 'CmdGet' => 'Average',
142
+ 'CmdSet' => 'Average',
143
+ 'CurrConnections' => 'Average',
144
+ 'CurrItems' => 'Average',
145
+ 'DecrHits' => 'Average',
146
+ 'DecrMisses' => 'Average',
147
+ 'DeleteHits' => 'Average',
148
+ 'DeleteMisses' => 'Average',
149
+ 'Evictions' => 'Average',
150
+ 'GetHits' => 'Average',
151
+ 'GetMisses' => 'Average',
152
+ 'IncrHits' => 'Average',
153
+ 'IncrMisses' => 'Average',
154
+ 'Reclaimed' => 'Average'
155
+ }
156
+ statistic = default_statistic_per_metric
157
+ else
158
+ statistic = config[:statistic]
206
159
  end
160
+ print_statistics(elasticache.cache_cluster_id, statistic)
207
161
  end
208
- rescue => e
209
- puts "Error: exception: #{e}"
210
- critical
211
162
  end
212
- ok
163
+ exit
213
164
  end
214
165
  end
@@ -134,7 +134,7 @@ class ELBMetrics < Sensu::Plugin::Metric::CLI::Graphite
134
134
  next if result.nil?
135
135
  result.each do |key, value|
136
136
  puts key, value
137
- output graphitepath + ".#{key}", value.to_a.last[1], value[:timestamp].to_i
137
+ output graphitepath + ".#{key}", value[:sum] || value[:average], value[:timestamp].to_i
138
138
  end
139
139
  end
140
140
  rescue => e
data/bin/metrics-elb.rb CHANGED
@@ -12,8 +12,10 @@
12
12
  # Linux
13
13
  #
14
14
  # DEPENDENCIES:
15
- # gem: aws-sdk-v1
15
+ # gem: aws-sdk
16
16
  # gem: sensu-plugin
17
+ # gem: sensu-plugin-aws
18
+ # gem: time
17
19
  #
18
20
  # USAGE:
19
21
  # #YELLOW
@@ -31,17 +33,20 @@
31
33
  # Copyright 2013 Bashton Ltd http://www.bashton.com/
32
34
  # Released under the same terms as Sensu (the MIT license); see LICENSE
33
35
  # for details.
34
- #
36
+ # Updated by Peter Hoppe <peter.hoppe.extern@bertelsmann.de> 09.11.2016
37
+ # Using aws sdk version 2
35
38
 
36
39
  require 'sensu-plugin/metric/cli'
37
- require 'aws-sdk-v1'
40
+ require 'aws-sdk'
41
+ require 'sensu-plugins-aws'
42
+ require 'time'
38
43
 
39
44
  class ELBMetrics < Sensu::Plugin::Metric::CLI::Graphite
45
+ include Common
40
46
  option :elbname,
41
47
  description: 'Name of the Elastic Load Balancer',
42
48
  short: '-n ELB_NAME',
43
- long: '--name ELB_NAME',
44
- required: true
49
+ long: '--name ELB_NAME'
45
50
 
46
51
  option :scheme,
47
52
  description: 'Metric naming scheme, text to prepend to metric',
@@ -56,40 +61,68 @@ class ELBMetrics < Sensu::Plugin::Metric::CLI::Graphite
56
61
  default: 60,
57
62
  proc: proc(&:to_i)
58
63
 
59
- option :metric,
60
- description: 'Metric to fetch',
61
- short: '-m METRIC',
62
- long: '--metric',
63
- default: 'Latency'
64
-
65
64
  option :statistic,
66
65
  description: 'Statistics type',
67
66
  short: '-t STATISTIC',
68
67
  long: '--statistic',
69
68
  default: ''
70
69
 
71
- option :aws_access_key,
72
- short: '-a AWS_ACCESS_KEY',
73
- long: '--aws-access-key AWS_ACCESS_KEY',
74
- description: "AWS Access Key. Either set ENV['AWS_ACCESS_KEY'] or provide it as an option",
75
- default: ENV['AWS_ACCESS_KEY']
76
-
77
- option :aws_secret_access_key,
78
- short: '-k AWS_SECRET_KEY',
79
- long: '--aws-secret-access-key AWS_SECRET_KEY',
80
- description: "AWS Secret Access Key. Either set ENV['AWS_SECRET_KEY'] or provide it as an option",
81
- default: ENV['AWS_SECRET_KEY']
82
-
83
70
  option :aws_region,
84
71
  short: '-r AWS_REGION',
85
72
  long: '--aws-region REGION',
86
73
  description: 'AWS Region (defaults to us-east-1).',
87
- default: 'us-east-1'
74
+ default: ENV['AWS_REGION']
75
+
76
+ option :end_time,
77
+ short: '-t T',
78
+ long: '--end-time TIME',
79
+ default: Time.now,
80
+ proc: proc { |a| Time.parse a },
81
+ description: 'CloudWatch metric statistics end time'
88
82
 
89
- def aws_config
90
- { access_key_id: config[:aws_access_key],
91
- secret_access_key: config[:aws_secret_access_key],
92
- region: config[:aws_region] }
83
+ option :period,
84
+ short: '-p N',
85
+ long: '--period SECONDS',
86
+ default: 60,
87
+ proc: proc(&:to_i),
88
+ description: 'CloudWatch metric statistics period'
89
+
90
+ def cloud_watch
91
+ @cloud_watch = Aws::CloudWatch::Client.new
92
+ end
93
+
94
+ def loadbalancer
95
+ @loadbalancer = Aws::ElasticLoadBalancing::Client.new
96
+ end
97
+
98
+ def cloud_watch_metric(metric_name, value, load_balancer_name)
99
+ cloud_watch.get_metric_statistics(
100
+ namespace: 'AWS/ELB',
101
+ metric_name: metric_name,
102
+ dimensions: [
103
+ {
104
+ name: 'LoadBalancerName',
105
+ value: load_balancer_name
106
+ }
107
+ ],
108
+ statistics: [value],
109
+ start_time: config[:end_time] - config[:period],
110
+ end_time: config[:end_time],
111
+ period: config[:period]
112
+ )
113
+ end
114
+
115
+ def print_statistics(load_balancer_name, statistics)
116
+ result = {}
117
+ static_value = {}
118
+ statistics.each do |key, static|
119
+ r = cloud_watch_metric(key, static, load_balancer_name)
120
+ static_value['loadbalancer.' + load_balancer_name + '.' + key + '.' + static] = static
121
+ result['loadbalancer.' + load_balancer_name + '.' + key + '.' + static] = r[:datapoints][0] unless r[:datapoints][0].nil?
122
+ end
123
+ result.each do |key, value|
124
+ output key.downcase.to_s, value[static_value[key].downcase], value[:timestamp].to_i
125
+ end
93
126
  end
94
127
 
95
128
  def run
@@ -109,44 +142,20 @@ class ELBMetrics < Sensu::Plugin::Metric::CLI::Graphite
109
142
  'SurgeQueueLength' => 'Maximum',
110
143
  'SpilloverCount' => 'Sum'
111
144
  }
112
- statistic = default_statistic_per_metric[config[:metric]]
145
+ statistic = default_statistic_per_metric
113
146
  else
114
147
  statistic = config[:statistic]
115
148
  end
116
149
 
117
150
  begin
118
- et = Time.now - config[:fetch_age]
119
- st = et - 60
120
-
121
- cw = AWS::CloudWatch::Client.new aws_config
122
-
123
- options = {
124
- 'namespace' => 'AWS/ELB',
125
- 'metric_name' => config[:metric],
126
- 'dimensions' => [
127
- {
128
- 'name' => 'LoadBalancerName',
129
- 'value' => config[:elbname]
130
- }
131
- ],
132
- 'statistics' => [statistic],
133
- 'start_time' => st.iso8601,
134
- 'end_time' => et.iso8601,
135
- 'period' => 60
136
- }
137
- result = cw.get_metric_statistics(options)
138
- data = result[:datapoints][0]
139
- unless data.nil?
140
- # We only return data when we have some to return
141
- graphitepath = config[:scheme]
142
- if config[:scheme] == ''
143
- graphitepath = "#{config[:elbname]}.#{config[:metric].downcase}"
151
+ if config[:elbname].nil?
152
+ loadbalancer.describe_load_balancers.load_balancer_descriptions.each do |elb|
153
+ print_statistics(elb.load_balancer_name, statistic)
144
154
  end
145
- output graphitepath, data[statistic.downcase.to_sym], data[:timestamp].to_i
155
+ else
156
+ print_statistics(config[:elbname], statistic)
146
157
  end
147
- rescue => e
148
- critical "Error: exception: #{e}"
158
+ ok
149
159
  end
150
- ok
151
160
  end
152
161
  end