i2x 0.0.6 → 0.0.7
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/lib/i2x/agent.rb +13 -12
- data/lib/i2x/cashier.rb +2 -1
- data/lib/i2x/client.rb +3 -4
- data/lib/i2x/csvdetector.rb +12 -13
- data/lib/i2x/detector.rb +9 -10
- data/lib/i2x/jsondetector.rb +15 -12
- data/lib/i2x/sqldetector.rb +15 -6
- data/lib/i2x/version.rb +1 -1
- data/lib/i2x/xmldetector.rb +13 -8
- data/lib/i2x.rb +4 -12
- metadata +1 -2
- data/lib/i2x/checkup.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5ed362050b735157e6bfa215d3fbdeba96332df
|
4
|
+
data.tar.gz: 626512055732e79ff3bbf57b50cff7ab116d6361
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cf8ef528e1cb50fc7bb8bb0dcb71504c67458e1bfeae36a17b80ae31415a220c1cc9d548dffd02a7e6d459958ddfedbace57b8c9d13179135bcd03f8a382ab0
|
7
|
+
data.tar.gz: df2c439bf52eaedbb3ed67f270db3a57ba7ff76e799bef51aeec3fabe3e4fd688f42d4ca74681a6468b0279be0bc699e1f8d2a51e80b9791076416cf588e15e9
|
data/lib/i2x/agent.rb
CHANGED
@@ -12,8 +12,9 @@ module I2X
|
|
12
12
|
@cache = agent[:payload][:cache]
|
13
13
|
@seeds = agent[:seeds]
|
14
14
|
@selectors = agent[:payload][:selectors]
|
15
|
+
I2X::Config.log.debug(self.class.name) {"Agent #{@identifier} initialized"}
|
15
16
|
rescue Exception => e
|
16
|
-
|
17
|
+
I2X::Config.log.error(self.class.name) {"Unable to initialize agent. #{e}"}
|
17
18
|
end
|
18
19
|
|
19
20
|
end
|
@@ -31,28 +32,28 @@ module I2X
|
|
31
32
|
@d = I2X::SQLDetector.new(self)
|
32
33
|
rescue Exception => e
|
33
34
|
@response = {:status => 400, :error => e}
|
34
|
-
|
35
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
35
36
|
end
|
36
37
|
when 'csv'
|
37
38
|
begin
|
38
39
|
@d = I2X::CSVDetector.new(self)
|
39
40
|
rescue Exception => e
|
40
41
|
@response = {:status => 400, :error => e}
|
41
|
-
|
42
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
42
43
|
end
|
43
44
|
when 'xml'
|
44
45
|
begin
|
45
46
|
@d = I2X::XMLDetector.new(self)
|
46
47
|
rescue Exception => e
|
47
48
|
@response = {:status => 400, :error => e}
|
48
|
-
|
49
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
49
50
|
end
|
50
51
|
when 'json'
|
51
52
|
begin
|
52
53
|
@d = I2X::JSONDetector.new(self)
|
53
54
|
rescue Exception => e
|
54
55
|
@response = {:status => 400, :error => e}
|
55
|
-
|
56
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
@@ -64,7 +65,7 @@ module I2X
|
|
64
65
|
end
|
65
66
|
@checkup = @d.checkup
|
66
67
|
rescue Exception => e
|
67
|
-
|
68
|
+
I2X::Config.log.error(self.class.name) {"Checkup error: #{e}"}
|
68
69
|
end
|
69
70
|
|
70
71
|
# Start detection
|
@@ -75,7 +76,7 @@ module I2X
|
|
75
76
|
|
76
77
|
@checkup[:templates] = @d.templates.uniq
|
77
78
|
rescue Exception => e
|
78
|
-
|
79
|
+
I2X::Config.log.error(self.class.name) {"Detection error: #{e}"}
|
79
80
|
end
|
80
81
|
|
81
82
|
begin
|
@@ -83,7 +84,7 @@ module I2X
|
|
83
84
|
process @checkup
|
84
85
|
end
|
85
86
|
rescue Exception => e
|
86
|
-
|
87
|
+
I2X::Config.log.error(self.class.name) {"Process error: #{e}"}
|
87
88
|
end
|
88
89
|
response = {:status => @checkup[:status], :message => "[i2x][Checkup][execute] All OK."}
|
89
90
|
end
|
@@ -96,20 +97,20 @@ module I2X
|
|
96
97
|
def process checkup
|
97
98
|
begin
|
98
99
|
checkup[:templates].each do |template|
|
99
|
-
|
100
|
+
I2X::Config.log.info(self.class.name) {"Delivering to #{template} template."}
|
100
101
|
checkup[:payload].each do |payload|
|
101
|
-
|
102
|
+
I2X::Config.log.debug(self.class.name) {"Processing #{payload}."}
|
102
103
|
response = RestClient.post "#{I2X::Config.host}postman/deliver/#{template}.js", payload
|
103
104
|
case response.code
|
104
105
|
when 200
|
105
106
|
|
106
107
|
else
|
107
|
-
|
108
|
+
I2X::Config.log.warn(self.class.name) {"unable to deliver \"#{payload}\" to \"#{template}\""}
|
108
109
|
end
|
109
110
|
end
|
110
111
|
end
|
111
112
|
rescue Exception => e
|
112
|
-
|
113
|
+
I2X::Config.log.error(self.class.name) {"Processing error: #{e}"}
|
113
114
|
end
|
114
115
|
|
115
116
|
end
|
data/lib/i2x/cashier.rb
CHANGED
@@ -15,10 +15,11 @@ module I2X
|
|
15
15
|
# - *seed*: seed data (if available)
|
16
16
|
#
|
17
17
|
def self.verify cache, agent, payload, seed
|
18
|
-
|
18
|
+
I2X::Config.log.info(self.class.name) {"Verifying\n\taccess token: #{I2X::Config.access_token}\n\thost: #{I2X::Config.host}\n\tcache: #{cache}\n\tagent: #{agent}\n\tpayload: #{payload}\tseed: #{seed}"}
|
19
19
|
begin
|
20
20
|
response = RestClient.post "#{I2X::Config.host}fluxcapacitor/verify.json", {:access_token => I2X::Config.access_token, :agent => agent[:identifier], :cache => cache, :payload => payload, :seed => seed}
|
21
21
|
rescue Exception => e
|
22
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
22
23
|
response = {:status => 400, :error => e}
|
23
24
|
end
|
24
25
|
response
|
data/lib/i2x/client.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'rest_client'
|
2
|
-
require 'logger'
|
3
2
|
|
4
3
|
module I2X
|
5
4
|
class Client
|
@@ -10,9 +9,9 @@ module I2X
|
|
10
9
|
def initialize config, log
|
11
10
|
begin
|
12
11
|
@config = config
|
13
|
-
I2X::Config.
|
14
|
-
I2X::Config.
|
15
|
-
I2X::Config.
|
12
|
+
I2X::Config.token = config[:server][:api_key]
|
13
|
+
I2X::Config.host = (config[:server][:host].end_with?('/') ? config[:server][:host] : config[:server][:host] << '/')
|
14
|
+
I2X::Config.log = log
|
16
15
|
|
17
16
|
I2X::Config.log.info(self.class.name) {'Configuration loaded successfully.'}
|
18
17
|
rescue Exception => e
|
data/lib/i2x/csvdetector.rb
CHANGED
@@ -16,33 +16,32 @@ module I2X
|
|
16
16
|
#
|
17
17
|
def detect object
|
18
18
|
|
19
|
-
|
19
|
+
I2X::Config.log.debug(self.class.name) {"Monitoring #{object[:uri]}"}
|
20
20
|
CSV.new(open(object[:uri]), :headers => :first_row).each do |row|
|
21
21
|
begin
|
22
22
|
unless object[:cache].nil? then
|
23
|
-
|
24
|
-
@response = Cashier.verify row[object[:cache].to_i], object, row, object[:seed]
|
25
|
-
|
23
|
+
@response = Cashier.verify row[object[:cache].to_i], object, row, object[:seed]
|
26
24
|
else
|
27
|
-
|
28
|
-
@cache = Cashier.verify row[0], object, row, object[:seed]
|
29
|
-
|
25
|
+
@response = Cashier.verify row[0], object, row, object[:seed]
|
30
26
|
end
|
31
27
|
rescue Exception => e
|
32
|
-
|
28
|
+
I2X::Config.log.error(self.class.name) {"Loading error: #{e}"}
|
33
29
|
end
|
34
30
|
|
35
31
|
begin
|
36
32
|
|
33
|
+
# Process i2xcache response
|
37
34
|
@cache = JSON.parse(@response, {:symbolize_names => true})
|
38
|
-
@cache[:templates].
|
39
|
-
@templates.
|
35
|
+
unless @cache[:templates].nil? then
|
36
|
+
@cache[:templates].each do |t|
|
37
|
+
@templates.push t
|
38
|
+
end
|
40
39
|
end
|
41
40
|
# The actual processing
|
42
41
|
#
|
43
42
|
if @cache[:cache][:status] == 100 then
|
44
|
-
|
45
|
-
|
43
|
+
I2X::Config.log.info(self.class.name) {"Not on cache, generating payload"}
|
44
|
+
|
46
45
|
payload = Hash.new
|
47
46
|
|
48
47
|
object[:selectors].each do |selector|
|
@@ -55,7 +54,7 @@ module I2X
|
|
55
54
|
end
|
56
55
|
|
57
56
|
rescue Exception => e
|
58
|
-
|
57
|
+
I2X::Config.log.error(self.class.name) {"Processing error: #{e}"}
|
59
58
|
end
|
60
59
|
@cache[:templates]
|
61
60
|
end
|
data/lib/i2x/detector.rb
CHANGED
@@ -21,9 +21,9 @@ module I2X
|
|
21
21
|
@payloads = Array.new
|
22
22
|
@objects = Array.new
|
23
23
|
@help = I2X::Helper.new
|
24
|
-
|
24
|
+
I2X::Config.log.info(self.class.name) {"Started new #{agent.identifier} detector"}
|
25
25
|
rescue Exception => e
|
26
|
-
|
26
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -46,25 +46,25 @@ module I2X
|
|
46
46
|
begin
|
47
47
|
@sr = I2X::CSVSeedReader.new(@agent, seed)
|
48
48
|
rescue Exception => e
|
49
|
-
|
49
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
50
50
|
end
|
51
51
|
when 'sql'
|
52
52
|
begin
|
53
53
|
@sr = I2X::SQLSeedReader.new(@agent, seed)
|
54
54
|
rescue Exception => e
|
55
|
-
|
55
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
56
56
|
end
|
57
57
|
when 'xml'
|
58
58
|
begin
|
59
59
|
@sr = I2X::XMLSeedReader.new(@agent, seed)
|
60
60
|
rescue Exception => e
|
61
|
-
|
61
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
62
62
|
end
|
63
63
|
when 'json'
|
64
64
|
begin
|
65
65
|
@sr = I2X::JSONSeedReader.new(@agent, seed)
|
66
66
|
rescue Exception => e
|
67
|
-
|
67
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
68
68
|
end
|
69
69
|
end
|
70
70
|
begin
|
@@ -73,7 +73,7 @@ module I2X
|
|
73
73
|
@objects.push read
|
74
74
|
end
|
75
75
|
rescue Exception => e
|
76
|
-
|
76
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -85,7 +85,6 @@ module I2X
|
|
85
85
|
object[:cache] = @agent.cache
|
86
86
|
object[:seed] = object[:identifier]
|
87
87
|
object[:selectors] = @agent.selectors
|
88
|
-
p "\n\tSelectors: #{object[:selectors]}"
|
89
88
|
unless self.content.nil? then
|
90
89
|
object[:content] = self.content
|
91
90
|
end
|
@@ -93,7 +92,7 @@ module I2X
|
|
93
92
|
end
|
94
93
|
rescue Exception => e
|
95
94
|
@response = {:status => 404, :message => "[i2x][Detector] failed to load doc, #{e}"}
|
96
|
-
|
95
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
97
96
|
end
|
98
97
|
|
99
98
|
begin
|
@@ -104,7 +103,7 @@ module I2X
|
|
104
103
|
@response = { :payload => @payloads, :templates => @templates, :status => 100}
|
105
104
|
rescue Exception => e
|
106
105
|
@response = {:status => 404, :message => "[i2x][Detector] failed to process queries, #{e}"}
|
107
|
-
|
106
|
+
I2X::Config.log.error(self.class.name) {"#{e}"}
|
108
107
|
end
|
109
108
|
@response
|
110
109
|
end
|
data/lib/i2x/jsondetector.rb
CHANGED
@@ -1,14 +1,7 @@
|
|
1
|
-
#require 'helper'
|
2
1
|
require 'open-uri'
|
3
2
|
require 'jsonpath'
|
4
3
|
require 'rest_client'
|
5
|
-
require 'csv'
|
6
4
|
require 'json'
|
7
|
-
#require 'seedreader'
|
8
|
-
#require 'csvseedreader'
|
9
|
-
#require 'sqlseedreader'
|
10
|
-
#require 'xmlseedreader'
|
11
|
-
#require 'jsonseedreader'
|
12
5
|
|
13
6
|
module I2X
|
14
7
|
|
@@ -24,6 +17,8 @@ module I2X
|
|
24
17
|
# == Detect the changes
|
25
18
|
#
|
26
19
|
def detect object
|
20
|
+
I2X::Config.log.info(self.class.name) {"Monitoring #{object[:uri]}"} unless object[:uri].nil?
|
21
|
+
|
27
22
|
begin
|
28
23
|
if object[:uri] == '' then
|
29
24
|
@doc = object[:content]
|
@@ -33,17 +28,25 @@ module I2X
|
|
33
28
|
end
|
34
29
|
JsonPath.on(@doc,object[:query]).each do |element|
|
35
30
|
JsonPath.on(element, object[:cache]).each do |c|
|
36
|
-
@
|
31
|
+
@response = Cashier.verify c, object, c, object[:seed]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Process i2x cache response
|
35
|
+
@cache = JSON.parse(@response, {:symbolize_names => true})
|
36
|
+
unless @cache[:templates].nil? then
|
37
|
+
@cache[:templates].each do |t|
|
38
|
+
@templates.push t
|
39
|
+
end
|
37
40
|
end
|
38
41
|
|
39
42
|
##
|
40
43
|
# If not on cache, add to payload for processing
|
41
44
|
#
|
42
45
|
if @cache[:status] == 100 then
|
43
|
-
|
46
|
+
I2X::Config.log.info(self.class.name) {"Not on cache, generating payload"}
|
44
47
|
# add row data to payload from selectors (key => key, value => column name)
|
45
48
|
payload = Hash.new
|
46
|
-
|
49
|
+
object[:selectors].each do |selector|
|
47
50
|
selector.each do |k,v|
|
48
51
|
JsonPath.on(element, v).each do |el|
|
49
52
|
payload[k] = el
|
@@ -56,9 +59,9 @@ module I2X
|
|
56
59
|
|
57
60
|
end
|
58
61
|
rescue Exception => e
|
59
|
-
|
62
|
+
I2X::Config.log.error(self.class.name) {"Loading error: #{e}"}
|
60
63
|
end
|
61
|
-
|
64
|
+
@cache[:templates]
|
62
65
|
end
|
63
66
|
end
|
64
67
|
end
|
data/lib/i2x/sqldetector.rb
CHANGED
@@ -15,23 +15,31 @@ module I2X
|
|
15
15
|
# == Detect the changes
|
16
16
|
#
|
17
17
|
def detect object
|
18
|
-
|
18
|
+
I2X::Config.log.debug(self.class.name) {"Monitoring #{object[:host]}"}
|
19
19
|
begin
|
20
20
|
@client = Mysql2::Client.new(:host => object[:host], :username => object[:username] , :password => object[:password] , :database => object[:database])
|
21
21
|
@client.query(@agent[:payload][:query]).each(:symbolize_keys => false) do |row|
|
22
22
|
unless object[:cache].nil? then
|
23
|
-
@
|
23
|
+
@response = Cashier.verify row[object[:cache]], object, row, object[:seed]
|
24
24
|
else
|
25
|
-
@
|
25
|
+
@response = Cashier.verify row["id"], object, row, object[:seed]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Process i2x cache response
|
29
|
+
@cache = JSON.parse(@response, {:symbolize_names => true})
|
30
|
+
unless @cache[:templates].nil? then
|
31
|
+
@cache[:templates].each do |t|
|
32
|
+
@templates.push t
|
33
|
+
end
|
26
34
|
end
|
27
35
|
|
28
36
|
# The actual processing
|
29
37
|
#
|
30
38
|
if @cache[:status] == 100 then
|
31
|
-
|
39
|
+
I2X::Config.log.info(self.class.name) {"Not on cache, generating payload"}
|
32
40
|
# add row data to payload from selectors (key => key, value => column name)
|
33
41
|
payload = Hash.new
|
34
|
-
|
42
|
+
object[:selectors].each do |selector|
|
35
43
|
selector.each do |k,v|
|
36
44
|
payload[k] = row[v]
|
37
45
|
end
|
@@ -41,8 +49,9 @@ module I2X
|
|
41
49
|
end
|
42
50
|
end
|
43
51
|
rescue Exception => e
|
44
|
-
|
52
|
+
I2X::Config.log.error(self.class.name) {"Processing error: #{e}"}
|
45
53
|
end
|
54
|
+
@cache[:templates]
|
46
55
|
end
|
47
56
|
end
|
48
57
|
end
|
data/lib/i2x/version.rb
CHANGED
data/lib/i2x/xmldetector.rb
CHANGED
@@ -1,8 +1,4 @@
|
|
1
|
-
#require 'helper'
|
2
|
-
#require 'cashier'
|
3
1
|
require 'open-uri'
|
4
|
-
#require 'raven'
|
5
|
-
#require 'slog'
|
6
2
|
|
7
3
|
module I2X
|
8
4
|
|
@@ -17,6 +13,7 @@ module I2X
|
|
17
13
|
# == Detect the changes
|
18
14
|
#
|
19
15
|
def detect object
|
16
|
+
I2X::Config.log.info(self.class.name) {"Monitoring #{object[:uri]}"} unless object[:uri].nil?
|
20
17
|
begin
|
21
18
|
if object[:uri] == '' then
|
22
19
|
@doc = Nokogiri::XML(object[:content])
|
@@ -26,17 +23,25 @@ module I2X
|
|
26
23
|
@doc.remove_namespaces!
|
27
24
|
@doc.xpath(object[:query]).each do |element|
|
28
25
|
element.xpath(object[:cache]).each do |c|
|
29
|
-
@
|
26
|
+
@response = Cashier.verify c.content, object, c.content, object[:seed]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Process i2x cache response
|
30
|
+
@cache = JSON.parse(@response, {:symbolize_names => true})
|
31
|
+
unless @cache[:templates].nil? then
|
32
|
+
@cache[:templates].each do |t|
|
33
|
+
@templates.push t
|
34
|
+
end
|
30
35
|
end
|
31
36
|
|
32
37
|
##
|
33
38
|
# If not on cache, add to payload for processing
|
34
39
|
#
|
35
40
|
if @cache[:status] == 100 then
|
36
|
-
|
41
|
+
I2X::Config.log.info(self.class.name) {"Not on cache, generating payload"}
|
37
42
|
# add row data to payload from selectors (key => key, value => column name)
|
38
43
|
payload = Hash.new
|
39
|
-
|
44
|
+
object[:selectors].each do |selector|
|
40
45
|
|
41
46
|
selector.each do |k,v|
|
42
47
|
element.xpath(v).each do |el|
|
@@ -51,7 +56,7 @@ module I2X
|
|
51
56
|
end
|
52
57
|
end
|
53
58
|
rescue Exception => e
|
54
|
-
|
59
|
+
I2X::Config.log.error(self.class.name) {"Processing error: #{e}"}
|
55
60
|
end
|
56
61
|
end
|
57
62
|
end
|
data/lib/i2x.rb
CHANGED
@@ -13,6 +13,10 @@ require 'i2x/client'
|
|
13
13
|
module I2X
|
14
14
|
class Config
|
15
15
|
|
16
|
+
class << self
|
17
|
+
attr_accessor :log, :host, :access_token
|
18
|
+
end
|
19
|
+
|
16
20
|
def self.set_log log
|
17
21
|
@@log = log
|
18
22
|
end
|
@@ -25,17 +29,5 @@ module I2X
|
|
25
29
|
def self.set_access_token api_key
|
26
30
|
@@access_token = api_key
|
27
31
|
end
|
28
|
-
|
29
|
-
def self.log
|
30
|
-
@@log
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.host
|
34
|
-
@@host
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.access_token
|
38
|
-
@@access_token
|
39
|
-
end
|
40
32
|
end
|
41
33
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i2x
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pedro Lopes
|
@@ -97,7 +97,6 @@ files:
|
|
97
97
|
- lib/i2x.rb
|
98
98
|
- lib/i2x/agent.rb
|
99
99
|
- lib/i2x/cashier.rb
|
100
|
-
- lib/i2x/checkup.rb
|
101
100
|
- lib/i2x/client.rb
|
102
101
|
- lib/i2x/csvdetector.rb
|
103
102
|
- lib/i2x/csvseedreader.rb
|
data/lib/i2x/checkup.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
module I2X
|
2
|
-
class Checkup
|
3
|
-
|
4
|
-
##
|
5
|
-
# = Perform the actual check execution
|
6
|
-
#
|
7
|
-
# + *agent*: the agent to verify
|
8
|
-
#
|
9
|
-
def execute agent
|
10
|
-
begin
|
11
|
-
@response = agent.execute
|
12
|
-
rescue Exception => e
|
13
|
-
|
14
|
-
@response = {:status => 400, :error => e}
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
##
|
20
|
-
# = Initiate real-time (poll) check
|
21
|
-
#
|
22
|
-
# + *schedule*: the scheduling being checked
|
23
|
-
def check schedule
|
24
|
-
Integration.all.each do |integration|
|
25
|
-
@agents = integration.agents.where( :schedule => schedule).where("last_check_at < CURRENT_TIMESTAMP - INTERVAL 5 MINUTE")
|
26
|
-
@agents.each do |agent|
|
27
|
-
begin
|
28
|
-
self.execute agent
|
29
|
-
rescue Exception => e
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|