gargor 0.0.1
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.
- data/.gitignore +17 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +171 -0
- data/Rakefile +1 -0
- data/bin/gargor +26 -0
- data/gargor.gemspec +23 -0
- data/lib/gargor.rb +154 -0
- data/lib/gargor/individual.rb +71 -0
- data/lib/gargor/parameter.rb +24 -0
- data/lib/gargor/version.rb +3 -0
- metadata +90 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Yoshihiro TAKAHARA
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
# gargor
|
2
|
+
|
3
|
+
`gargor`はChefで管理されたサーバのパラメータと負荷試験結果を遺伝的アルゴリズム(genetic algorithm)により機械学習し、最適値を探索します。本ソフトウェアをうまく使うことで今まで勘と経験に頼っていたサーバチューニングをより最適にかつ自動化することができます。
|
4
|
+
|
5
|
+
## インストール
|
6
|
+
|
7
|
+
Ruby 1.9以降が必要です
|
8
|
+
|
9
|
+
$ gem install gargor
|
10
|
+
|
11
|
+
## どのように動くのか
|
12
|
+
|
13
|
+
1. 現在のChefの設定ファイル(JSON)から個体を一つ作ります。
|
14
|
+
2. 残りの個体を突然変異により作ります。
|
15
|
+
3. 各個体に対し、負荷試験を実施し、適応値を算出します。
|
16
|
+
4. 現世代の個体群に対して、エリートと残りを交叉および突然変異により次世代の個体群をつくります。
|
17
|
+
5. 次世代の個体群を現世代として`3.`に戻ります。これを指定した世代分実施します。
|
18
|
+
6. 最後に最も高い適応値の個体をサーバに配備して終了します
|
19
|
+
|
20
|
+
## 使い方
|
21
|
+
|
22
|
+
$ gargor [dsl-file]
|
23
|
+
|
24
|
+
|
25
|
+
`dsl-file`を省略した場合は、カレントディレクトリの`gargor.rb`を探します。
|
26
|
+
|
27
|
+
dsl-fileの書き方は、添付の`doc/sample.rb`を御覧ください
|
28
|
+
|
29
|
+
|
30
|
+
### 注意
|
31
|
+
|
32
|
+
`gargor`は、DSLファイルで指定されたChefのJSONを直接(問答無用で)書き換えます。`git stash`を使うなりしてオリジナルが消えないように配慮ください。
|
33
|
+
|
34
|
+
## サンプルレシピ
|
35
|
+
|
36
|
+
`Chef`のサンプルレシピをTipsを交えてご紹介します。
|
37
|
+
|
38
|
+
### mysql
|
39
|
+
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
# この時点で起動しようとすると以前の設定ファイルがおかしい場合にエラーが出てしまう
|
43
|
+
service "mysqld" do
|
44
|
+
action :nothing
|
45
|
+
end
|
46
|
+
|
47
|
+
template "/etc/my.cnf" do
|
48
|
+
source "www/my.cnf.erb"
|
49
|
+
notifies :restart,"service[mysqld]"
|
50
|
+
end
|
51
|
+
|
52
|
+
# ib_logfile0,1はib_logfile1はinnodb_log_file_sizeを変えるとエラーになるので毎回消す
|
53
|
+
file "/var/lib/mysql/ib_logfile0" do
|
54
|
+
action :delete
|
55
|
+
notifies :restart,"service[mysqld]"
|
56
|
+
end
|
57
|
+
file "/var/lib/mysql/ib_logfile1" do
|
58
|
+
action :delete
|
59
|
+
notifies :restart,"service[mysqld]"
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
このテンプレートは以下のようになっています。`innodb_buffer_pool_size`を探索対象のパラメータにしたいのですが、「`innodb_log_file_size`を`innodb_buffer_pool_size`の25%にすべし」と注意書きがあるので、`innodb_log_file_size`を設定対象にして、`innodb_buffer_pool_size`をその4倍にしています。
|
64
|
+
|
65
|
+
```
|
66
|
+
[mysqld]
|
67
|
+
datadir=/var/lib/mysql
|
68
|
+
socket=/var/lib/mysql/mysql.sock
|
69
|
+
user=mysql
|
70
|
+
# Default to using old password format for compatibility with mysql 3.x
|
71
|
+
# clients (those using the mysqlclient10 compatibility package).
|
72
|
+
old_passwords=1
|
73
|
+
|
74
|
+
# Disabling symbolic-links is recommended to prevent assorted security risks;
|
75
|
+
# to do so, uncomment this line:
|
76
|
+
# symbolic-links=0
|
77
|
+
|
78
|
+
skip-locking
|
79
|
+
query_cache_size = <%= node["mysqld"]["query_cache_size"] %>M
|
80
|
+
key_buffer = <%= node["mysqld"]["key_buffer"] %>K
|
81
|
+
max_allowed_packet = <%= node["mysqld"]["max_allowed_packet"] %>M
|
82
|
+
table_cache = <%= node["mysqld"]["table_cache"] %>
|
83
|
+
sort_buffer_size = <%= node["mysqld"]["sort_buffer_size"] %>K
|
84
|
+
read_buffer_size = <%= node["mysqld"]["read_buffer_size"] %>K
|
85
|
+
read_rnd_buffer_size = <%= node["mysqld"]["read_rnd_buffer_size"] %>K
|
86
|
+
net_buffer_length = <%= node["mysqld"]["net_buffer_length"] %>K
|
87
|
+
thread_stack = <%= node["mysqld"]["thread_stack"] %>K
|
88
|
+
|
89
|
+
skip-networking
|
90
|
+
server-id = 1
|
91
|
+
|
92
|
+
# Uncomment the following if you want to log updates
|
93
|
+
#log-bin=mysql-bin
|
94
|
+
|
95
|
+
# Disable Federated by default
|
96
|
+
skip-federated
|
97
|
+
|
98
|
+
# Uncomment the following if you are NOT using BDB tables
|
99
|
+
#skip-bdb
|
100
|
+
|
101
|
+
# Uncomment the following if you are using InnoDB tables
|
102
|
+
innodb_data_home_dir = /var/lib/mysql/
|
103
|
+
innodb_data_file_path = ibdata1:10M:autoextend
|
104
|
+
innodb_log_group_home_dir = /var/lib/mysql/
|
105
|
+
innodb_log_arch_dir = /var/lib/mysql/
|
106
|
+
# You can set .._buffer_pool_size up to 50 - 80 %
|
107
|
+
# of RAM but beware of setting memory usage too high
|
108
|
+
innodb_buffer_pool_size = <%= node["mysqld"]["innodb_log_file_size"]*4 %>M
|
109
|
+
innodb_additional_mem_pool_size = <%= node["mysqld"]["innodb_additional_mem_pool_size"] %>M
|
110
|
+
# Set .._log_file_size to 25 % of buffer pool size
|
111
|
+
innodb_log_file_size = <%= node["mysqld"]["innodb_log_file_size"] %>M
|
112
|
+
innodb_log_buffer_size = <%= node["mysqld"]["innodb_log_buffer_size"] %>M
|
113
|
+
innodb_flush_log_at_trx_commit = 1
|
114
|
+
innodb_lock_wait_timeout = 50
|
115
|
+
|
116
|
+
[mysqld_safe]
|
117
|
+
log-error=/var/log/mysqld.log
|
118
|
+
pid-file=/var/run/mysqld/mysqld.pid
|
119
|
+
```
|
120
|
+
|
121
|
+
### httpd
|
122
|
+
|
123
|
+
Apacheのパフォーマンスチューニングでは以下の様にしています。レシピには全く工夫はありません。
|
124
|
+
|
125
|
+
```
|
126
|
+
service "httpd" do
|
127
|
+
action :nothing
|
128
|
+
end
|
129
|
+
|
130
|
+
template "/etc/httpd/conf/httpd.conf" do
|
131
|
+
source "www/httpd.conf.erb"
|
132
|
+
notifies :restart,"service[httpd]"
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
ポイントは`MinSpareServers`と`MaxSpareServers`のように上下関係のあるパラメータの決定方法を以下のように`min_spare_servers`と`range_spare_servers`としている点です。また、`ServerLimit`=`MaxClients`としています。
|
137
|
+
|
138
|
+
```
|
139
|
+
<IfModule prefork.c>
|
140
|
+
StartServers <%= node["httpd"]["start_servers"] %>
|
141
|
+
MinSpareServers <%= node["httpd"]["min_spare_servers"] %>
|
142
|
+
MaxSpareServers <%= node["httpd"]["min_spare_servers"] + node["httpd"]["range_spare_servers"] %>
|
143
|
+
ServerLimit <%= node["httpd"]["max_clients"] %>
|
144
|
+
MaxClients <%= node["httpd"]["max_clients"] %>
|
145
|
+
MaxRequestsPerChild <%= node["httpd"]["max_request_per_child"] %>
|
146
|
+
</IfModule>
|
147
|
+
```
|
148
|
+
|
149
|
+
## 負荷試験ツール
|
150
|
+
|
151
|
+
コマンドラインで使えるものであれば、大体使うことができます。サンプルでは、[グリーン破壊](https://github.com/KLab/green-hakai/)を使わせて頂きました。
|
152
|
+
|
153
|
+
負荷試験の厳しさによって個体が死滅することがあります。すると以下のように表示され、プログラムが終了します。
|
154
|
+
|
155
|
+
***** EXTERMINATION ******
|
156
|
+
|
157
|
+
これは、個体の環境が厳しすぎるためで負荷試験を緩めて再度実施してください。
|
158
|
+
|
159
|
+
## FAQ
|
160
|
+
|
161
|
+
##### `gargor`はなんて読むの?
|
162
|
+
|
163
|
+
「がるごる」
|
164
|
+
|
165
|
+
## Contributing
|
166
|
+
|
167
|
+
1. Fork it
|
168
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
169
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
170
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
171
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/gargor
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
3
|
+
require 'gargor'
|
4
|
+
|
5
|
+
dsl_file=ARGV.last||"gargor.rb"
|
6
|
+
|
7
|
+
Gargor.start
|
8
|
+
Gargor.load_dsl(dsl_file)
|
9
|
+
loop {
|
10
|
+
Gargor.populate.each { |i|
|
11
|
+
if i.fitness == nil
|
12
|
+
i.set_params
|
13
|
+
if i.deploy
|
14
|
+
i.attack
|
15
|
+
end
|
16
|
+
end
|
17
|
+
}
|
18
|
+
break unless Gargor.next_generation
|
19
|
+
}
|
20
|
+
|
21
|
+
best = Gargor.individuals.max { |a,b| a.fitness <=> b.fitness }
|
22
|
+
if best
|
23
|
+
best.set_params
|
24
|
+
best.deploy
|
25
|
+
end
|
26
|
+
p best
|
data/gargor.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gargor/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "gargor"
|
8
|
+
spec.version = Gargor::VERSION
|
9
|
+
spec.authors = ["Yoshihiro TAKAHARA"]
|
10
|
+
spec.email = ["y.takahara@gmail.com"]
|
11
|
+
spec.description = %q{auto server tunner by generic algorithm}
|
12
|
+
spec.summary = %q{auto server tunner by generic algorithm using chef}
|
13
|
+
spec.homepage = "https://github.com/tumf/gargor"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/lib/gargor.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "gargor/version"
|
3
|
+
require "gargor/individual"
|
4
|
+
require "gargor/parameter"
|
5
|
+
|
6
|
+
class Gargor
|
7
|
+
GLOBAL_OPTS = ["population","max_generations","target_nodes",
|
8
|
+
"attack_cmd","elite","mutation","target_cooking_cmd",
|
9
|
+
"attack_node","fitness_precision","attack_result"]
|
10
|
+
|
11
|
+
GLOBAL_OPTS.each { |name|
|
12
|
+
define_method(name) { |val|
|
13
|
+
Gargor.class_variable_set("@@#{name}", val)
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def start
|
19
|
+
@@fitness_precision = 100000000
|
20
|
+
@@prev_generation = nil
|
21
|
+
@@individuals = []
|
22
|
+
@@param_procs = {}
|
23
|
+
@@population = 10
|
24
|
+
@@max_generations = 1
|
25
|
+
@@generation = 1
|
26
|
+
@@elite = 0
|
27
|
+
@@attack_cmd = "false"
|
28
|
+
@@attack_proc = nil
|
29
|
+
@@evaluate_proc = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_dsl(params_file)
|
33
|
+
contents = File.open(params_file, 'rb'){ |f| f.read }
|
34
|
+
new.instance_eval(contents)
|
35
|
+
end
|
36
|
+
|
37
|
+
def mutation
|
38
|
+
individual = Individual.new
|
39
|
+
@@param_procs.each { |name,proc|
|
40
|
+
param = Parameter.new(name)
|
41
|
+
param.instance_eval(&proc)
|
42
|
+
individual.params[name] = param
|
43
|
+
}
|
44
|
+
individual
|
45
|
+
end
|
46
|
+
|
47
|
+
# 浮動小数点対応のrand
|
48
|
+
def float_rand(f,p = @@fitness_precision)
|
49
|
+
f *= @@fitness_precision
|
50
|
+
i = f.to_i
|
51
|
+
f = rand(i)
|
52
|
+
f / @@fitness_precision.to_f
|
53
|
+
end
|
54
|
+
|
55
|
+
def crossover a,b
|
56
|
+
return a.clone if a.params == b.params
|
57
|
+
puts "crossover: #{a} #{b}"
|
58
|
+
total = a.fitness + b.fitness
|
59
|
+
c = Individual.new
|
60
|
+
c.params = a.params.clone
|
61
|
+
|
62
|
+
c.params.each { |name,param|
|
63
|
+
cur = float_rand(total)
|
64
|
+
c.params[name] = b.params[name] if b.fitness > cur
|
65
|
+
}
|
66
|
+
puts "#{a.to_s} + #{b.to_s} \n => #{c.to_s}"
|
67
|
+
c
|
68
|
+
end
|
69
|
+
|
70
|
+
def selection g
|
71
|
+
total = g.inject(0) { |sum,i| sum += i.fitness }
|
72
|
+
cur = float_rand(total)
|
73
|
+
g.each { |i|
|
74
|
+
return i if i.fitness > cur
|
75
|
+
cur -= i.fitness
|
76
|
+
}
|
77
|
+
raise "error selection"
|
78
|
+
end
|
79
|
+
|
80
|
+
def populate
|
81
|
+
@@individuals = []
|
82
|
+
|
83
|
+
# 第一世代
|
84
|
+
unless @@prev_generation
|
85
|
+
@@individuals << mutation.load_now
|
86
|
+
loop{
|
87
|
+
break if @@individuals.length >= @@population
|
88
|
+
@@individuals << mutation
|
89
|
+
}
|
90
|
+
return @@individuals
|
91
|
+
end
|
92
|
+
|
93
|
+
# fitness > 0 適応している個体
|
94
|
+
prev_count = @@prev_generation.select { |i| i.fitness > 0 }.count
|
95
|
+
|
96
|
+
if prev_count < 2
|
97
|
+
raise "***** EXTERMINATION ******"
|
98
|
+
end
|
99
|
+
|
100
|
+
puts "population: #{@@prev_generation.length}"
|
101
|
+
@@individuals = @@prev_generation.sort{ |a,b| a.fitness<=>b.fitness }.last(@@elite) if @@elite > 0
|
102
|
+
loop{
|
103
|
+
break if @@individuals.length >= @@population
|
104
|
+
if rand <= @@mutation
|
105
|
+
i = mutation
|
106
|
+
puts "mutation #{i}"
|
107
|
+
else
|
108
|
+
a = selection @@prev_generation
|
109
|
+
b = selection @@prev_generation
|
110
|
+
i = crossover(a,b)
|
111
|
+
end
|
112
|
+
|
113
|
+
#同じのは追加しない
|
114
|
+
if i and !@@individuals.find { |ii| ii.params == i.params }
|
115
|
+
@@individuals << i
|
116
|
+
end
|
117
|
+
}
|
118
|
+
puts "poulate: #{@@individuals}"
|
119
|
+
@@individuals
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def next_generation
|
124
|
+
puts "<== end generation #{@@generation}"
|
125
|
+
@@generation += 1
|
126
|
+
return false if @@generation > @@max_generations
|
127
|
+
|
128
|
+
puts "==> next generation #{@@generation}"
|
129
|
+
@@prev_generation = @@individuals
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
133
|
+
def individuals
|
134
|
+
@@individuals
|
135
|
+
end
|
136
|
+
|
137
|
+
def opt name
|
138
|
+
Gargor.class_variable_get("@@#{name}")
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def param name,&block
|
143
|
+
@@param_procs[name] = block
|
144
|
+
end
|
145
|
+
|
146
|
+
def attack cmd,&block
|
147
|
+
@@attack_cmd = cmd
|
148
|
+
@@attack_proc = block
|
149
|
+
end
|
150
|
+
|
151
|
+
def evaluate &block
|
152
|
+
@@evaluate_proc = block
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'json'
|
3
|
+
require 'jsonpath'
|
4
|
+
require 'benchmark'
|
5
|
+
|
6
|
+
class Gargor
|
7
|
+
class Individual
|
8
|
+
attr_accessor :params,:fitness
|
9
|
+
def initialize
|
10
|
+
@params = {}
|
11
|
+
@fitness = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
[@params,@fitness].to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_now
|
19
|
+
@params.each { |name,param|
|
20
|
+
json = File.open(param.file).read
|
21
|
+
@params[name].value = JsonPath.on(json,param.path).first
|
22
|
+
}
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_params
|
27
|
+
puts "==> set params"
|
28
|
+
puts @params
|
29
|
+
@params.each { |name,param|
|
30
|
+
json = File.open(param.file).read
|
31
|
+
json = JSON.pretty_generate(JsonPath.for(json).gsub(param.path) { |v| param.value }.to_hash)
|
32
|
+
# 書き出し
|
33
|
+
File.open(param.file,"w") { |f| f.write(json) }
|
34
|
+
puts " write #{param.file}"
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def deploy
|
39
|
+
ret = true
|
40
|
+
Gargor.opt("target_nodes").each { |node|
|
41
|
+
puts "==> deploy to #{node}"
|
42
|
+
cmd = Gargor.opt("target_cooking_cmd") % [node]
|
43
|
+
puts " #{cmd}"
|
44
|
+
r = system(cmd)
|
45
|
+
unless r
|
46
|
+
puts "deploy failed"
|
47
|
+
@fitness = 0
|
48
|
+
sleep 1
|
49
|
+
end
|
50
|
+
ret &= r
|
51
|
+
}
|
52
|
+
ret
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def attack
|
57
|
+
ret = nil;out = nil
|
58
|
+
cmd = Gargor.opt('attack_cmd')
|
59
|
+
puts "==> attack"
|
60
|
+
puts "execute: #{cmd}"
|
61
|
+
tms = Benchmark.realtime do
|
62
|
+
out = `#{cmd}`
|
63
|
+
ret = $?
|
64
|
+
end
|
65
|
+
|
66
|
+
@fitness = Gargor.opt('evaluate_proc').call(ret.to_i,out,tms)
|
67
|
+
puts "fitness: #{@fitness}"
|
68
|
+
@fitness
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
class Gargor
|
3
|
+
class Parameter
|
4
|
+
attr_accessor :file, :path, :value, :name
|
5
|
+
alias :json_file :file=
|
6
|
+
alias :json_path :path=
|
7
|
+
alias :mutation :value=
|
8
|
+
alias_method :eql, :==
|
9
|
+
def initialize name
|
10
|
+
@name = name
|
11
|
+
end
|
12
|
+
def to_s
|
13
|
+
@value.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
name == other.name and
|
18
|
+
file == other.file and
|
19
|
+
path == other.path and
|
20
|
+
value == other.value
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gargor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Yoshihiro TAKAHARA
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: auto server tunner by generic algorithm
|
47
|
+
email:
|
48
|
+
- y.takahara@gmail.com
|
49
|
+
executables:
|
50
|
+
- gargor
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- bin/gargor
|
60
|
+
- gargor.gemspec
|
61
|
+
- lib/gargor.rb
|
62
|
+
- lib/gargor/individual.rb
|
63
|
+
- lib/gargor/parameter.rb
|
64
|
+
- lib/gargor/version.rb
|
65
|
+
homepage: https://github.com/tumf/gargor
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.8.25
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: auto server tunner by generic algorithm using chef
|
90
|
+
test_files: []
|