elastic_manager 0.1.7 → 0.1.8
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/README.md +13 -4
- data/elastic_manager.gemspec +3 -1
- data/lib/elastic_manager/chill.rb +56 -0
- data/lib/elastic_manager/close.rb +56 -0
- data/lib/elastic_manager/config.rb +21 -17
- data/lib/elastic_manager/delete.rb +54 -0
- data/lib/elastic_manager/logger.rb +1 -2
- data/lib/elastic_manager/open.rb +14 -81
- data/lib/elastic_manager/request.rb +177 -16
- data/lib/elastic_manager/snapdelete.rb +55 -0
- data/lib/elastic_manager/snapshot.rb +55 -0
- data/lib/elastic_manager/utils.rb +93 -0
- data/lib/elastic_manager.rb +22 -0
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c798157f6af433e26a76c9935307a298d7d6fa4d1e164cb4234f0a144438586
|
4
|
+
data.tar.gz: a26879da569ed538f6320dc56cb9e2944aedcf6bf30a4850bfee97ab40c13ce6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f6f5cf3017e6bc33acd2ecf5b2eec443e17f37aa08681cefb30861fb5c95eaba00d229ed862f4ecc8549387f0de6bd9a47ebc79085ae85b3d1901d851b41dd1
|
7
|
+
data.tar.gz: c05c07cdaad9135cc47a5c2b25696d4ebb135f824ed42251d3c156dcd275e552cf135b98e85cba62d5a7f34029433233963a1a8b0b3e58e66bab8f18a91494b0
|
data/README.md
CHANGED
@@ -4,6 +4,12 @@ Manager for logstash indices in elasticsearch. Why? Because qurator sucks!
|
|
4
4
|
|
5
5
|
[](https://travis-ci.org/onetwotrip/elastic_manager)
|
6
6
|
|
7
|
+
Use ENV variables for run. Required variables:
|
8
|
+
|
9
|
+
- TASK
|
10
|
+
- INDICES
|
11
|
+
- FROM-TO or DAYSAGO (FROM-TO pair have precedence over DAYSAGO)
|
12
|
+
|
7
13
|
Progress:
|
8
14
|
|
9
15
|
- [x] Open closed indices
|
@@ -11,7 +17,10 @@ Progress:
|
|
11
17
|
- [x] Open by date from and to
|
12
18
|
- [x] Open by daysago
|
13
19
|
- [x] Open all indices
|
14
|
-
- [
|
15
|
-
- [
|
16
|
-
- [
|
17
|
-
- [
|
20
|
+
- [x] Close indices
|
21
|
+
- [x] Chill indices
|
22
|
+
- [x] Delete indices
|
23
|
+
- [x] Snapshot indices
|
24
|
+
- [x] Delete snapshots
|
25
|
+
- [x] Override daysago for concrete index
|
26
|
+
- [x] Skip any task for specific index
|
data/elastic_manager.gemspec
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Gem::Specification.new do |s|
|
2
4
|
s.name = 'elastic_manager'
|
3
5
|
s.executables = ['elastic_manager']
|
4
|
-
s.version = '0.1.
|
6
|
+
s.version = '0.1.8'
|
5
7
|
s.date = '2018-10-15'
|
6
8
|
s.summary = 'Because qurator sucks'
|
7
9
|
s.description = 'Manager for logstash indices in elastic'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elastic_manager/logger'
|
4
|
+
|
5
|
+
# Index chilling operations
|
6
|
+
module Chill
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
def chill_populate_indices(indices, date_from, date_to, daysago)
|
10
|
+
result = []
|
11
|
+
|
12
|
+
if indices.length == 1 && indices.first == '_all'
|
13
|
+
result = @elastic.all_indices(date_from, date_to, daysago, 'open', @config['settings']['box_types']['ingest'], @config['settings']['indices'])
|
14
|
+
else
|
15
|
+
if date_from.nil?
|
16
|
+
result = @elastic.all_indices(date_from, date_to, daysago, 'open', @config['settings']['box_types']['ingest'], @config['settings']['indices']).select { |r| r.start_with?(*indices) }
|
17
|
+
else
|
18
|
+
date_from.upto(date_to) do |date|
|
19
|
+
indices.each do |index|
|
20
|
+
date_formatted = date.to_s.tr('-', '.')
|
21
|
+
result << "#{index}-#{date_formatted}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return result unless result.empty?
|
28
|
+
|
29
|
+
log.fatal 'no indices for work'
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def do_chill(indices)
|
34
|
+
indices.each do |index|
|
35
|
+
next if skip_index?(index, 'chill')
|
36
|
+
|
37
|
+
response = @elastic.request(:get, "/_cat/indices/#{index}")
|
38
|
+
|
39
|
+
if index_exist?(response)
|
40
|
+
next if already?(response, @config['settings']['box_types']['store'])
|
41
|
+
|
42
|
+
elastic_action_with_log('chill_index', index, @config['settings']['box_types']['store'])
|
43
|
+
else
|
44
|
+
log.warn "#{index} index not found"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def chill
|
50
|
+
indices, date_from, date_to, daysago = prepare_vars
|
51
|
+
prechecks(date_from, date_to)
|
52
|
+
indices = chill_populate_indices(indices, date_from, date_to, daysago)
|
53
|
+
log.debug indices.inspect
|
54
|
+
do_chill(indices)
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elastic_manager/logger'
|
4
|
+
|
5
|
+
# Index closing operations
|
6
|
+
module Close
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
def close_populate_indices(indices, date_from, date_to, daysago)
|
10
|
+
result = []
|
11
|
+
|
12
|
+
if indices.length == 1 && indices.first == '_all'
|
13
|
+
result = @elastic.all_indices(date_from, date_to, daysago, 'open', nil, @config['settings']['indices'])
|
14
|
+
else
|
15
|
+
if date_from.nil?
|
16
|
+
result = @elastic.all_indices(date_from, date_to, daysago, 'open', nil, @config['settings']['indices']).select { |r| r.start_with?(*indices) }
|
17
|
+
else
|
18
|
+
date_from.upto(date_to) do |date|
|
19
|
+
indices.each do |index|
|
20
|
+
date_formatted = date.to_s.tr('-', '.')
|
21
|
+
result << "#{index}-#{date_formatted}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return result unless result.empty?
|
28
|
+
|
29
|
+
log.fatal 'no indices for work'
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def do_close(indices)
|
34
|
+
indices.each do |index|
|
35
|
+
next if skip_index?(index, 'close')
|
36
|
+
|
37
|
+
response = @elastic.request(:get, "/_cat/indices/#{index}")
|
38
|
+
|
39
|
+
if index_exist?(response)
|
40
|
+
next if already?(response, 'close')
|
41
|
+
|
42
|
+
elastic_action_with_log('close_index', index, @config['settings']['box_types']['ingest'])
|
43
|
+
else
|
44
|
+
log.warn "#{index} index not found"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def close
|
50
|
+
indices, date_from, date_to, daysago = prepare_vars
|
51
|
+
prechecks(date_from, date_to)
|
52
|
+
indices = close_populate_indices(indices, date_from, date_to, daysago)
|
53
|
+
log.debug indices.inspect
|
54
|
+
do_close(indices)
|
55
|
+
end
|
56
|
+
end
|
@@ -35,15 +35,23 @@ module Config
|
|
35
35
|
default['timeout']['connect'] = '3'
|
36
36
|
default['timeout']['read'] = '120'
|
37
37
|
default['daysago'] = ''
|
38
|
-
default['settings'] = {
|
38
|
+
default['settings'] = {
|
39
|
+
'box_types' => {
|
40
|
+
'ingest' => 'hot',
|
41
|
+
'store' => 'warm'
|
42
|
+
},
|
43
|
+
'indices' => {}
|
44
|
+
}
|
39
45
|
|
40
46
|
log.debug "default config: #{default.inspect}"
|
41
47
|
default
|
42
48
|
end
|
43
49
|
|
44
|
-
def check_settings(var)
|
50
|
+
def check_settings(var, config)
|
45
51
|
if var.casecmp('settings').zero?
|
46
|
-
json_parse(ENV[var])
|
52
|
+
env_settings = json_parse(ENV[var])
|
53
|
+
log.debug "env settings: #{env_settings}"
|
54
|
+
config['settings'].merge(env_settings)
|
47
55
|
else
|
48
56
|
ENV[var]
|
49
57
|
end
|
@@ -58,20 +66,16 @@ module Config
|
|
58
66
|
if vars.length == 2
|
59
67
|
config[vars[0].downcase][vars[1].downcase] = ENV[var]
|
60
68
|
elsif vars.length == 1
|
61
|
-
config[vars[0].downcase] = check_settings(vars[0])
|
69
|
+
config[vars[0].downcase] = check_settings(vars[0], config)
|
62
70
|
end
|
63
71
|
end
|
64
72
|
|
65
73
|
config
|
66
74
|
end
|
67
75
|
|
68
|
-
|
69
|
-
# !blank?
|
70
|
-
# end
|
71
|
-
|
72
|
-
def exit_if_invalid(config)
|
76
|
+
def validate_config(config)
|
73
77
|
if config['task'].empty? || config['indices'].empty?
|
74
|
-
fail_and_exit('not enough env variables
|
78
|
+
fail_and_exit('not enough env variables: TASK, INDICES')
|
75
79
|
end
|
76
80
|
|
77
81
|
if !config['from'].empty? && !config['to'].empty?
|
@@ -81,19 +85,19 @@ module Config
|
|
81
85
|
rescue ArgumentError => e
|
82
86
|
fail_and_exit("can't parse date #{key}: #{e.message}")
|
83
87
|
end
|
84
|
-
elsif config['from'].empty?
|
85
|
-
fail_and_exit('not enough env variables
|
88
|
+
elsif config['from'].empty? && !config['to'].empty?
|
89
|
+
fail_and_exit('not enough env variables: FROM')
|
90
|
+
elsif !config['from'].empty? && config['to'].empty?
|
91
|
+
fail_and_exit('not enough env variables: TO')
|
86
92
|
elsif !config['daysago'].empty?
|
87
93
|
log.debug 'will use daysago'
|
88
94
|
config['from'], config['to'] = nil
|
89
95
|
config['daysago'] = config['daysago'].to_i
|
90
96
|
else
|
91
|
-
fail_and_exit('not enough env variables
|
97
|
+
fail_and_exit('not enough env variables: FROM-TO or DAYSAGO')
|
92
98
|
end
|
93
99
|
|
94
|
-
|
95
|
-
# fail_and_exit('not enough env variables. FROM/TO or DAYSAGO')
|
96
|
-
# end
|
100
|
+
config
|
97
101
|
end
|
98
102
|
|
99
103
|
def load_from_env
|
@@ -101,7 +105,7 @@ module Config
|
|
101
105
|
|
102
106
|
config = make_default_config
|
103
107
|
config = env_parser(config)
|
104
|
-
|
108
|
+
config = validate_config(config)
|
105
109
|
|
106
110
|
log.debug "env config: #{config.inspect}"
|
107
111
|
config
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elastic_manager/logger'
|
4
|
+
|
5
|
+
# Index deleting operations
|
6
|
+
module Delete
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
def delete_populate_indices(indices, date_from, date_to, daysago)
|
10
|
+
result = []
|
11
|
+
|
12
|
+
if indices.length == 1 && indices.first == '_all'
|
13
|
+
result = @elastic.all_indices(date_from, date_to, daysago, nil, nil, @config['settings']['indices'])
|
14
|
+
else
|
15
|
+
if date_from.nil?
|
16
|
+
result = @elastic.all_indices(date_from, date_to, daysago, nil, nil, @config['settings']['indices']).select { |r| r.start_with?(*indices) }
|
17
|
+
else
|
18
|
+
date_from.upto(date_to) do |date|
|
19
|
+
indices.each do |index|
|
20
|
+
date_formatted = date.to_s.tr('-', '.')
|
21
|
+
result << "#{index}-#{date_formatted}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return result unless result.empty?
|
28
|
+
|
29
|
+
log.fatal 'no indices for work'
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def do_delete(indices)
|
34
|
+
indices.each do |index|
|
35
|
+
next if skip_index?(index, 'delete')
|
36
|
+
|
37
|
+
response = @elastic.request(:get, "/_cat/indices/#{index}")
|
38
|
+
|
39
|
+
if index_exist?(response)
|
40
|
+
elastic_action_with_log('delete_index', index)
|
41
|
+
else
|
42
|
+
log.warn "#{index} index not found"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete
|
48
|
+
indices, date_from, date_to, daysago = prepare_vars
|
49
|
+
prechecks(date_from, date_to)
|
50
|
+
indices = delete_populate_indices(indices, date_from, date_to, daysago)
|
51
|
+
log.debug indices.inspect
|
52
|
+
do_delete(indices)
|
53
|
+
end
|
54
|
+
end
|
data/lib/elastic_manager/open.rb
CHANGED
@@ -6,89 +6,24 @@ require 'elastic_manager/logger'
|
|
6
6
|
module Open
|
7
7
|
include Logging
|
8
8
|
|
9
|
-
def
|
10
|
-
unless date_from.nil?
|
11
|
-
if date_from > date_to
|
12
|
-
log.fatal "wrong dates: date to is behind date from. from: #{date_from}, to: #{date_to}"
|
13
|
-
exit 1
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
unless true?(@config['force'])
|
18
|
-
unless @elastic.green?
|
19
|
-
fail_and_exit("elasticsearch on #{@config['es']['url']} is not green")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def skip_open?(index)
|
25
|
-
index_name = index.split('-')[0..-2].join('-')
|
26
|
-
|
27
|
-
if @config['settings'][index_name] && @config['settings'][index_name]['skip_open']
|
28
|
-
if true?(@config['settings'][index_name]['skip_open'])
|
29
|
-
log.warn "#{index_name} index open skiped"
|
30
|
-
return true
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
false
|
35
|
-
end
|
36
|
-
|
37
|
-
def index_exist?(response)
|
38
|
-
if response.code == 200
|
39
|
-
true
|
40
|
-
elsif response.code == 404
|
41
|
-
false
|
42
|
-
else
|
43
|
-
log.fatal "wtf in index_exist? response was: #{response.code} - #{response}"
|
44
|
-
exit 1
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def already_open?(response)
|
49
|
-
index = json_parse(response).first
|
50
|
-
if index['status'] == 'open'
|
51
|
-
log.warn "#{index['index']} index status already open"
|
52
|
-
return true
|
53
|
-
end
|
54
|
-
|
55
|
-
false
|
56
|
-
end
|
57
|
-
|
58
|
-
def open_prepare_vars
|
59
|
-
indices = @config['indices'].split(',')
|
60
|
-
daysago = @config['daysago']
|
61
|
-
date_from = @config['from']
|
62
|
-
date_to = @config['to']
|
63
|
-
|
64
|
-
[indices, date_from, date_to, daysago]
|
65
|
-
end
|
66
|
-
|
67
|
-
def action_with_log(action, index)
|
68
|
-
if @elastic.send(action, index)
|
69
|
-
log.info "#{index} #{action} succes"
|
70
|
-
else
|
71
|
-
log.error "#{index} #{action} fail"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def populate_indices(indices, date_from, date_to, daysago)
|
9
|
+
def open_populate_indices(indices, date_from, date_to, daysago)
|
76
10
|
result = []
|
77
11
|
|
78
12
|
if indices.length == 1 && indices.first == '_all'
|
79
|
-
result = @elastic.all_indices(date_from, date_to, daysago, 'close')
|
80
|
-
result += @elastic.all_indices_in_snapshots(date_from, date_to, daysago)
|
13
|
+
result = @elastic.all_indices(date_from, date_to, daysago, 'close', nil, @config['settings']['indices'])
|
14
|
+
result += @elastic.all_indices_in_snapshots(date_from, date_to, daysago, @config['settings']['indices'])
|
81
15
|
return result
|
82
16
|
end
|
83
17
|
|
84
18
|
if date_from.nil?
|
85
|
-
result = @elastic.all_indices(date_from, date_to, daysago, 'close')
|
86
|
-
result += @elastic.all_indices_in_snapshots(date_from, date_to, daysago)
|
19
|
+
result = @elastic.all_indices(date_from, date_to, daysago, 'close', nil, @config['settings']['indices'])
|
20
|
+
result += @elastic.all_indices_in_snapshots(date_from, date_to, daysago, @config['settings']['indices'])
|
87
21
|
return result.select { |r| r.start_with?(*indices) }
|
88
22
|
else
|
89
23
|
date_from.upto(date_to) do |date|
|
90
24
|
indices.each do |index|
|
91
|
-
|
25
|
+
date_formatted = date.to_s.tr('-', '.')
|
26
|
+
result << "#{index}-#{date_formatted}"
|
92
27
|
end
|
93
28
|
end
|
94
29
|
end
|
@@ -101,30 +36,28 @@ module Open
|
|
101
36
|
|
102
37
|
def do_open(indices)
|
103
38
|
indices.each do |index|
|
104
|
-
next if
|
39
|
+
next if skip_index?(index, 'open')
|
105
40
|
|
106
41
|
response = @elastic.request(:get, "/_cat/indices/#{index}")
|
107
42
|
|
108
43
|
if index_exist?(response)
|
109
|
-
next if
|
44
|
+
next if already?(response, 'open')
|
110
45
|
|
111
|
-
|
46
|
+
elastic_action_with_log('open_index', index)
|
112
47
|
else
|
113
48
|
log.warn "#{index} index not found"
|
114
49
|
log.info "#{index} trying snapshot restore"
|
115
50
|
|
116
|
-
|
51
|
+
elastic_action_with_log('restore_snapshot', index, @config['settings']['box_types']['store'])
|
117
52
|
end
|
118
53
|
end
|
119
54
|
end
|
120
55
|
|
121
56
|
def open
|
122
|
-
indices, date_from, date_to, daysago =
|
123
|
-
|
124
|
-
indices =
|
125
|
-
|
57
|
+
indices, date_from, date_to, daysago = prepare_vars
|
58
|
+
prechecks(date_from, date_to)
|
59
|
+
indices = open_populate_indices(indices, date_from, date_to, daysago)
|
126
60
|
log.debug indices.inspect
|
127
|
-
|
128
61
|
do_open(indices)
|
129
62
|
end
|
130
63
|
end
|
@@ -37,7 +37,6 @@ module Request
|
|
37
37
|
tries ||= @retry
|
38
38
|
|
39
39
|
yield
|
40
|
-
|
41
40
|
rescue *RETRY_ERRORS => e
|
42
41
|
log.warn "tries left #{tries + 1} '''#{e.message}''' sleeping #{@sleep} sec..."
|
43
42
|
sleep @sleep
|
@@ -71,23 +70,36 @@ module Request
|
|
71
70
|
false
|
72
71
|
end
|
73
72
|
|
74
|
-
def
|
73
|
+
def override_daysago(index_name, indices_config, daysago)
|
74
|
+
if indices_config[index_name] &&
|
75
|
+
indices_config[index_name]['daysago'] &&
|
76
|
+
!indices_config[index_name]['daysago'].to_s.empty?
|
77
|
+
indices_config[index_name]['daysago']
|
78
|
+
else
|
79
|
+
daysago
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def all_indices_in_snapshots(from=nil, to=nil, daysago=nil, indices_config)
|
75
84
|
all_snapshots = get_all_snapshots
|
76
85
|
all_snapshots.select! { |snap| snap['status'] == 'SUCCESS' }
|
77
86
|
|
78
87
|
result = []
|
79
88
|
all_snapshots.each do |snap|
|
80
89
|
begin
|
81
|
-
snap_date = Date.parse(snap['id'].
|
90
|
+
snap_date = Date.parse(snap['id'].delete('-'))
|
82
91
|
rescue ArgumentError => e
|
83
92
|
log.error "#{e.message} for #{index}"
|
84
93
|
next
|
85
94
|
end
|
86
95
|
|
87
|
-
|
88
|
-
|
96
|
+
index = snap['id'].gsub('snapshot_', '')
|
97
|
+
daysago_local = override_daysago(make_index_name(index), indices_config, daysago)
|
98
|
+
|
99
|
+
if from.nil? && snap_date < (Date.today - daysago_local)
|
100
|
+
result << CGI.escape(index)
|
89
101
|
elsif (from..to).cover? snap_date
|
90
|
-
result << CGI.escape(
|
102
|
+
result << CGI.escape(index)
|
91
103
|
end
|
92
104
|
end
|
93
105
|
|
@@ -106,7 +118,7 @@ module Request
|
|
106
118
|
end
|
107
119
|
end
|
108
120
|
|
109
|
-
def all_indices(from=nil, to=nil, daysago=nil, state=nil, type=nil)
|
121
|
+
def all_indices(from=nil, to=nil, daysago=nil, state=nil, type=nil, indices_config)
|
110
122
|
indices = get_all_indices
|
111
123
|
|
112
124
|
# TODO: (anton.ryabov) next line just for debug purpose, need better handling
|
@@ -118,13 +130,15 @@ module Request
|
|
118
130
|
result = []
|
119
131
|
indices.each do |index, _|
|
120
132
|
begin
|
121
|
-
index_date = Date.parse(index.
|
133
|
+
index_date = Date.parse(index.delete('-'))
|
122
134
|
rescue ArgumentError => e
|
123
135
|
log.error "#{e.message} for #{index}"
|
124
136
|
next
|
125
137
|
end
|
126
138
|
|
127
|
-
|
139
|
+
daysago_local = override_daysago(make_index_name(index), indices_config, daysago)
|
140
|
+
|
141
|
+
if from.nil? && index_date < (Date.today - daysago_local)
|
128
142
|
result << CGI.escape(index)
|
129
143
|
elsif (from..to).cover? index_date
|
130
144
|
result << CGI.escape(index)
|
@@ -149,6 +163,19 @@ module Request
|
|
149
163
|
end
|
150
164
|
end
|
151
165
|
|
166
|
+
def snapshot_exist?(snapshot_name, repo)
|
167
|
+
response = request(:get, "/_snapshot/#{repo}/#{snapshot_name}/")
|
168
|
+
|
169
|
+
if response.code == 200
|
170
|
+
true
|
171
|
+
elsif response.code == 404
|
172
|
+
false
|
173
|
+
else
|
174
|
+
log.fatal "can't check snapshot existing, response was: #{response.code} - #{response}"
|
175
|
+
exit 1
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
152
179
|
def find_snapshot_repo
|
153
180
|
# TODO: we need improve this if several snapshot repos used in elastic
|
154
181
|
response = request(:get, '/_snapshot/')
|
@@ -165,12 +192,12 @@ module Request
|
|
165
192
|
response = request(:get, "/_snapshot/#{repo}/#{snapshot_name}/")
|
166
193
|
|
167
194
|
if response.code == 200
|
168
|
-
snapshot = json_parse(response)['snapshots']
|
195
|
+
snapshot = json_parse(response)['snapshots'][0]
|
169
196
|
|
170
|
-
if snapshot
|
171
|
-
snapshot
|
197
|
+
if snapshot['state'] == 'SUCCESS'
|
198
|
+
snapshot['snapshot']
|
172
199
|
else
|
173
|
-
log.fatal
|
200
|
+
log.fatal 'wrong snapshot state'
|
174
201
|
exit 1
|
175
202
|
end
|
176
203
|
else
|
@@ -179,7 +206,7 @@ module Request
|
|
179
206
|
end
|
180
207
|
end
|
181
208
|
|
182
|
-
def restore_snapshot(index)
|
209
|
+
def restore_snapshot(index, box_type)
|
183
210
|
snapshot_name = "snapshot_#{index}"
|
184
211
|
snapshot_repo = find_snapshot_repo
|
185
212
|
snapshot = find_snapshot(snapshot_repo, snapshot_name)
|
@@ -188,7 +215,7 @@ module Request
|
|
188
215
|
index_settings: {
|
189
216
|
'index.number_of_replicas' => 0,
|
190
217
|
'index.refresh_interval' => -1,
|
191
|
-
'index.routing.allocation.require.box_type' =>
|
218
|
+
'index.routing.allocation.require.box_type' => box_type
|
192
219
|
}
|
193
220
|
}
|
194
221
|
response = request(:post, "/_snapshot/#{snapshot_repo}/#{snapshot}/_restore", body)
|
@@ -227,7 +254,141 @@ module Request
|
|
227
254
|
response = json_parse(response)
|
228
255
|
else
|
229
256
|
log.fatal "wrong response code for #{index} open"
|
230
|
-
|
257
|
+
return false
|
258
|
+
end
|
259
|
+
|
260
|
+
response['acknowledged'].is_a?(TrueClass)
|
261
|
+
end
|
262
|
+
|
263
|
+
def close_index(index, tag)
|
264
|
+
box_type = index_box_type(index)
|
265
|
+
|
266
|
+
return false if box_type.nil?
|
267
|
+
|
268
|
+
if box_type == tag
|
269
|
+
log.fatal "i will not close index #{index} in box_type #{tag}"
|
270
|
+
false
|
271
|
+
else
|
272
|
+
response = request(:post, "/#{index}/_close?master_timeout=1m")
|
273
|
+
|
274
|
+
if response.code == 200
|
275
|
+
response = json_parse(response)
|
276
|
+
else
|
277
|
+
log.fatal "wrong response code for #{index} close"
|
278
|
+
return false
|
279
|
+
end
|
280
|
+
|
281
|
+
response['acknowledged'].is_a?(TrueClass)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def index_box_type(index)
|
286
|
+
response = request(:get, "/#{index}/_settings/index.routing.allocation.require.box_type")
|
287
|
+
|
288
|
+
if response.code == 200
|
289
|
+
response = json_parse(response)
|
290
|
+
box_type = response[index]['settings']['index']['routing']['allocation']['require']['box_type']
|
291
|
+
log.debug "for #{index} box_type is #{box_type}"
|
292
|
+
box_type
|
293
|
+
else
|
294
|
+
log.fatal "can't check box_type for #{index}, response was: #{response.code} - #{response}"
|
295
|
+
nil
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def chill_index(index, box_type)
|
300
|
+
body = {
|
301
|
+
'index.routing.allocation.require.box_type' => box_type
|
302
|
+
}
|
303
|
+
response = request(:put, "/#{index}/_settings?master_timeout=1m", body)
|
304
|
+
|
305
|
+
if response.code == 200
|
306
|
+
response = json_parse(response)
|
307
|
+
else
|
308
|
+
log.fatal "can't chill #{index}, response was: #{response.code} - #{response}"
|
309
|
+
return false
|
310
|
+
end
|
311
|
+
|
312
|
+
response['acknowledged'].is_a?(TrueClass)
|
313
|
+
end
|
314
|
+
|
315
|
+
def delete_index(index)
|
316
|
+
snapshot_name = "snapshot_#{index}"
|
317
|
+
snapshot_repo = find_snapshot_repo
|
318
|
+
|
319
|
+
return false unless find_snapshot(snapshot_repo, snapshot_name)
|
320
|
+
|
321
|
+
response = request(:delete, "/#{index}")
|
322
|
+
|
323
|
+
if response.code == 200
|
324
|
+
response = json_parse(response)
|
325
|
+
else
|
326
|
+
log.fatal "can't delete index #{index}, response was: #{response.code} - #{response}"
|
327
|
+
return false
|
328
|
+
end
|
329
|
+
|
330
|
+
response['acknowledged'].is_a?(TrueClass)
|
331
|
+
end
|
332
|
+
|
333
|
+
def wait_snapshot(snapshot, repo)
|
334
|
+
snapshot_ok = false
|
335
|
+
|
336
|
+
until snapshot_ok
|
337
|
+
sleep @sleep
|
338
|
+
response = request(:get, "/_snapshot/#{repo}/#{snapshot}/_status")
|
339
|
+
|
340
|
+
if response.code == 200
|
341
|
+
# TODO: (anton.ryabov) add logging of percent and time ?
|
342
|
+
# stats = status['snapshots'][0]['stats']
|
343
|
+
# msg = "(#{stats['total_size_in_bytes']/1024/1024/1024}Gb / #{stats['processed_size_in_bytes']/1024/1024/1024}Gb)"
|
344
|
+
# puts "Get backup status #{msg}: retry attempt #{attempt_number}; #{total_delay.round} seconds have passed."
|
345
|
+
state = json_parse(response)['snapshots'][0]['state']
|
346
|
+
log.debug "snapshot check response: #{response.code} - #{response}"
|
347
|
+
|
348
|
+
if state == 'SUCCESS'
|
349
|
+
snapshot_ok = true
|
350
|
+
elsif %w[FAILED PARTIAL INCOMPATIBLE].include?(state)
|
351
|
+
log.fatal "can't snapshot #{snapshot} in #{repo}: #{response.code} - #{response}"
|
352
|
+
exit 1
|
353
|
+
end
|
354
|
+
else
|
355
|
+
log.error "can't check snapshot: #{response.code} - #{response}"
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
true
|
360
|
+
end
|
361
|
+
|
362
|
+
def snapshot_index(index)
|
363
|
+
snapshot_name = "snapshot_#{index}"
|
364
|
+
snapshot_repo = find_snapshot_repo
|
365
|
+
|
366
|
+
body = {
|
367
|
+
'indices' => index,
|
368
|
+
'ignore_unavailable' => false,
|
369
|
+
'include_global_state' => false,
|
370
|
+
'partial' => false
|
371
|
+
}
|
372
|
+
|
373
|
+
response = request(:put, "/_snapshot/#{snapshot_repo}/#{snapshot_name}/", body)
|
374
|
+
|
375
|
+
if response.code == 200
|
376
|
+
sleep 5
|
377
|
+
wait_snapshot(snapshot_name, snapshot_repo)
|
378
|
+
else
|
379
|
+
log.error "can't snapshot #{index}, response was: #{response.code} - #{response}"
|
380
|
+
false
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def delete_snapshot(snapshot, repo)
|
385
|
+
response = request(:delete, "/_snapshot/#{repo}/#{snapshot}")
|
386
|
+
|
387
|
+
if response.code == 200
|
388
|
+
response = json_parse(response)
|
389
|
+
else
|
390
|
+
log.fatal "can't delete snapshot #{snapshot}, response was: #{response.code} - #{response}"
|
391
|
+
return false
|
231
392
|
end
|
232
393
|
|
233
394
|
response['acknowledged'].is_a?(TrueClass)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elastic_manager/logger'
|
4
|
+
|
5
|
+
# Index snapshots deleting operations
|
6
|
+
module SnapDelete
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
def snapdelete_populate_indices(indices, date_from, date_to, daysago)
|
10
|
+
result = []
|
11
|
+
|
12
|
+
if indices.length == 1 && indices.first == '_all'
|
13
|
+
result = @elastic.all_indices_in_snapshots(date_from, date_to, daysago, @config['settings']['indices'])
|
14
|
+
else
|
15
|
+
if date_from.nil?
|
16
|
+
result = @elastic.all_indices_in_snapshots(date_from, date_to, daysago, @config['settings']['indices']).select { |r| r.start_with?(*indices) }
|
17
|
+
else
|
18
|
+
date_from.upto(date_to) do |date|
|
19
|
+
indices.each do |index|
|
20
|
+
date_formatted = date.to_s.tr('-', '.')
|
21
|
+
result << "#{index}-#{date_formatted}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return result unless result.empty?
|
28
|
+
|
29
|
+
log.fatal 'no indices for work'
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def do_snapdelete(indices)
|
34
|
+
indices.each do |index|
|
35
|
+
next if skip_index?(index, 'snapdelete')
|
36
|
+
|
37
|
+
snapshot_name = "snapshot_#{index}"
|
38
|
+
snapshot_repo = @elastic.find_snapshot_repo
|
39
|
+
|
40
|
+
if @elastic.snapshot_exist?(snapshot_name, snapshot_repo)
|
41
|
+
elastic_action_with_log('delete_snapshot', snapshot_name, snapshot_repo)
|
42
|
+
else
|
43
|
+
log.warn "#{index} snapshot #{snapshot_name} not found"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def snapdelete
|
49
|
+
indices, date_from, date_to, daysago = prepare_vars
|
50
|
+
prechecks(date_from, date_to)
|
51
|
+
indices = snapdelete_populate_indices(indices, date_from, date_to, daysago)
|
52
|
+
log.debug indices.inspect
|
53
|
+
do_snapdelete(indices)
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elastic_manager/logger'
|
4
|
+
|
5
|
+
# Index snapshoting operations
|
6
|
+
module Snapshot
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
def snapshot_populate_indices(indices, date_from, date_to, daysago)
|
10
|
+
result = []
|
11
|
+
|
12
|
+
if indices.length == 1 && indices.first == '_all'
|
13
|
+
result = @elastic.all_indices(date_from, date_to, daysago, nil, @config['settings']['box_types']['store'], @config['settings']['indices'])
|
14
|
+
else
|
15
|
+
if date_from.nil?
|
16
|
+
result = @elastic.all_indices(date_from, date_to, daysago, nil, @config['settings']['box_types']['store'], @config['settings']['indices']).select { |r| r.start_with?(*indices) }
|
17
|
+
else
|
18
|
+
date_from.upto(date_to) do |date|
|
19
|
+
indices.each do |index|
|
20
|
+
date_formatted = date.to_s.tr('-', '.')
|
21
|
+
result << "#{index}-#{date_formatted}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return result unless result.empty?
|
28
|
+
|
29
|
+
log.fatal 'no indices for work'
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def do_snapshot(indices)
|
34
|
+
indices.each do |index|
|
35
|
+
next if skip_index?(index, 'snapshot')
|
36
|
+
|
37
|
+
response = @elastic.request(:get, "/_cat/indices/#{index}")
|
38
|
+
|
39
|
+
if index_exist?(response)
|
40
|
+
elastic_action_with_log('open_index', index) unless already?(response, 'open')
|
41
|
+
elastic_action_with_log('delete_index', index) if elastic_action_with_log('snapshot_index', index)
|
42
|
+
else
|
43
|
+
log.warn "#{index} index not found"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def snapshot
|
49
|
+
indices, date_from, date_to, daysago = prepare_vars
|
50
|
+
prechecks(date_from, date_to)
|
51
|
+
indices = snapshot_populate_indices(indices, date_from, date_to, daysago)
|
52
|
+
log.debug indices.inspect
|
53
|
+
do_snapshot(indices)
|
54
|
+
end
|
55
|
+
end
|
@@ -22,4 +22,97 @@ module Utils
|
|
22
22
|
log.fatal text
|
23
23
|
exit 1
|
24
24
|
end
|
25
|
+
|
26
|
+
def make_index_name(index)
|
27
|
+
index.split('-')[0..-2].join('-')
|
28
|
+
end
|
29
|
+
|
30
|
+
def skip_index?(index, state)
|
31
|
+
index_name = make_index_name(index)
|
32
|
+
|
33
|
+
if @config['settings']['indices'][index_name] &&
|
34
|
+
@config['settings']['indices'][index_name]['skip'] &&
|
35
|
+
@config['settings']['indices'][index_name]['skip'][state]
|
36
|
+
|
37
|
+
if true?(@config['settings']['indices'][index_name]['skip'][state])
|
38
|
+
log.warn "#{index_name} index #{state} skiped due settings"
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def prepare_vars
|
47
|
+
indices = @config['indices'].split(',')
|
48
|
+
daysago = @config['daysago']
|
49
|
+
date_from = @config['from']
|
50
|
+
date_to = @config['to']
|
51
|
+
|
52
|
+
[indices, date_from, date_to, daysago]
|
53
|
+
end
|
54
|
+
|
55
|
+
def prechecks(date_from, date_to)
|
56
|
+
unless date_from.nil?
|
57
|
+
if date_from > date_to
|
58
|
+
log.fatal "wrong dates: date to is behind date from. from: #{date_from}, to: #{date_to}"
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
return if true?(@config['force'])
|
64
|
+
return if @elastic.green?
|
65
|
+
|
66
|
+
fail_and_exit("elasticsearch on #{@config['es']['url']} is not green")
|
67
|
+
end
|
68
|
+
|
69
|
+
def already?(response, state)
|
70
|
+
if %w[open close].include?(state)
|
71
|
+
index = json_parse(response).first
|
72
|
+
if index['status'] == state
|
73
|
+
log.warn "#{index['index']} index status already #{state}"
|
74
|
+
return true
|
75
|
+
end
|
76
|
+
elsif %w[hot warm].include?(state)
|
77
|
+
index = json_parse(response).first['index']
|
78
|
+
box_type = @elastic.index_box_type(index)
|
79
|
+
|
80
|
+
if box_type.nil?
|
81
|
+
log.info "can't get box_type, look at the previous error for info, will skip #{index}"
|
82
|
+
return true
|
83
|
+
end
|
84
|
+
|
85
|
+
if box_type == state
|
86
|
+
log.warn "#{index['index']} index already #{state}"
|
87
|
+
return true
|
88
|
+
end
|
89
|
+
else
|
90
|
+
log.fatal "wrong state for check #{state}, bad coder"
|
91
|
+
exit 1
|
92
|
+
end
|
93
|
+
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
def elastic_action_with_log(action, index, *params)
|
98
|
+
if @elastic.send(action, index, *params)
|
99
|
+
log.info "#{index} #{action} succes"
|
100
|
+
else
|
101
|
+
log.error "#{index} #{action} fail"
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
def index_exist?(response)
|
109
|
+
if response.code == 200
|
110
|
+
true
|
111
|
+
elsif response.code == 404
|
112
|
+
false
|
113
|
+
else
|
114
|
+
log.fatal "wtf in index_exist? response was: #{response.code} - #{response}"
|
115
|
+
exit 1
|
116
|
+
end
|
117
|
+
end
|
25
118
|
end
|
data/lib/elastic_manager.rb
CHANGED
@@ -7,6 +7,11 @@ require 'elastic_manager/logger'
|
|
7
7
|
require 'elastic_manager/request'
|
8
8
|
require 'elastic_manager/utils'
|
9
9
|
require 'elastic_manager/open'
|
10
|
+
require 'elastic_manager/close'
|
11
|
+
require 'elastic_manager/chill'
|
12
|
+
require 'elastic_manager/snapshot'
|
13
|
+
require 'elastic_manager/delete'
|
14
|
+
require 'elastic_manager/snapdelete'
|
10
15
|
|
11
16
|
# Main
|
12
17
|
class ElasticManager
|
@@ -15,6 +20,11 @@ class ElasticManager
|
|
15
20
|
include Request
|
16
21
|
include Utils
|
17
22
|
include Open
|
23
|
+
include Close
|
24
|
+
include Chill
|
25
|
+
include Snapshot
|
26
|
+
include Delete
|
27
|
+
include SnapDelete
|
18
28
|
|
19
29
|
def initialize
|
20
30
|
@config = load_from_env
|
@@ -25,6 +35,18 @@ class ElasticManager
|
|
25
35
|
def run
|
26
36
|
if @config['task'].casecmp('open').zero?
|
27
37
|
open
|
38
|
+
elsif @config['task'].casecmp('close').zero?
|
39
|
+
close
|
40
|
+
elsif @config['task'].casecmp('chill').zero?
|
41
|
+
chill
|
42
|
+
elsif @config['task'].casecmp('snapshot').zero?
|
43
|
+
snapshot
|
44
|
+
elsif @config['task'].casecmp('delete').zero?
|
45
|
+
delete
|
46
|
+
elsif @config['task'].casecmp('snapdelete').zero?
|
47
|
+
snapdelete
|
48
|
+
else
|
49
|
+
fail_and_exit('wrong task')
|
28
50
|
end
|
29
51
|
end
|
30
52
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastic_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Antony Ryabov
|
@@ -94,10 +94,15 @@ files:
|
|
94
94
|
- bin/elastic_manager
|
95
95
|
- elastic_manager.gemspec
|
96
96
|
- lib/elastic_manager.rb
|
97
|
+
- lib/elastic_manager/chill.rb
|
98
|
+
- lib/elastic_manager/close.rb
|
97
99
|
- lib/elastic_manager/config.rb
|
100
|
+
- lib/elastic_manager/delete.rb
|
98
101
|
- lib/elastic_manager/logger.rb
|
99
102
|
- lib/elastic_manager/open.rb
|
100
103
|
- lib/elastic_manager/request.rb
|
104
|
+
- lib/elastic_manager/snapdelete.rb
|
105
|
+
- lib/elastic_manager/snapshot.rb
|
101
106
|
- lib/elastic_manager/utils.rb
|
102
107
|
homepage: https://github.com/onetwotrip/elastic_manager
|
103
108
|
licenses:
|