elastic_manager 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/onetwotrip/elastic_manager.svg?branch=master)](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:
|