ruby-quotaを使ってみる

# はじめに

rubyのgemであるruby-quotaを日本語で試されている人の文献が見つからなかったので、個人的なメモを残します。
gemについての詳しい情報はこちら => https://github.com/axic/ruby-quota

いわゆるクォータ情報を見たり、更新したり等々するものです。
現在設定されているクォータ情報の取得と更新を試してみたのでその際の記録を残していきます。

# 環境

今回試しているのは以下の環境になります。

* Ruby 
  * ruby 2.1.3p242 (2014-09-19 revision 47630) [x86_64-linux]
* ruby-quota
  *  ruby-quota-0.8.0.gem
* Cent OS
  * CentOS release 6.5 (Final)
  * kernel 2.6.32-431.5.1.el6.x86_64

# 使い方

## install

ご存知gem installで完了です。

```
# gem install ruby-quota --no-ri --no-rdoc
Fetching: ruby-quota-0.8.0.gem (100%)
Building native extensions.  This could take a while...
Successfully installed ruby-quota-0.8.0
1 gem installed
```

## require

使うには *quota* という名前でrequireできます。

``` require.rb
require 'quota'
```

## check

※私の環境ではgetquota実行時に権限が足りてなさそうだったので root で実行しました。linux の repquota コマンドで必要な権限分の権限は必要と思われます。

* uid のクォータ情報を取得

基本的にはuidかgidを引数にとってQuota::UserIDかQuota::GroupIDでインスタンス化してgetquotaで取得します。
※uidの番号であれば直接インスタンス化せず情報が取れるみたいです。
構造化データで返ってきます。

```userquota.rb
user = Quota::UserID.new(000)
user_quota = Quota.getquota('/var', user)
p user_quota
 => #<struct Struct::DiskQuota bhardlimit=0, bsoftlimit=0, curblocks=111111, ihardlimit=0, isoftlimit=0, curinodes=104, btimelimit=0, itimelimit=0>

```

* gid のクォータ情報を取得

uidと同様です。

```group.rb
group = Quota::GroupID.new(111) # => #<Quota::GroupID:0x007f2f9a09bcc8>
group_quota = Quota.getquota('/var', group)
p group_quota                    # => #<struct Struct::DiskQuota bhardlimit=1124000, bsoftlimit=1024000, curblocks=483328, ihardlimit=0, isoftlimit=0, curinodes=118, btimelimit=0, itimelimit=0>
```

## update

更新の場合はgetquotaしてきたデータに対してsetterで新しい値を入力し、setquotaしてあげれば反応してくれると思います。
※未検証だけど、おそらくこの形

```setquota_NG.rb
group = Quota::GroupID.new(111) # => #<Quota::GroupID:0x007f2f9a09bcc8>
group_quota = Quota.getquota('/var', group)

group_quota.bhardlimit = 1224000
p group_quota # => #<struct Struct::DiskQuota bhardlimit=1224000, bsoftlimit=1024000, curblocks=483328, ihardlimit=0, isoftlimit=0, curinodes=118, btimelimit=0, itimelimit=0>
Quota.setquota('/var', group, group_quota)

=> NameError: `curspace' is not a struct member

```

getquotaからsetquotaの書き換えは curblocks があってだめっぽいです。これは設定を変更するものではなくて常に変わっていくものだからという考え方の用に思えます。したがって別な方法でやってみました。

```setquota_NG2.rb
group = Quota::GroupID.new(1247)

limit_config = Quota::DiskQuota.new
limit_config.bsoftlimit = 5120000
limit_config.bhardlimit = 5620000
p limit_config
#<struct Struct::DiskQuota bhardlimit=5620000, bsoftlimit=5120000, curblocks=nil, ihardlimit=nil, isoftlimit=nil, curinodes=nil, btimelimit=nil, itimelimit=nil>
Quota.setquota('/var', group, limit_config)
 => NameError: `curspace' is not a struct member 
    from (pry):16:in `setquota'
```

これもだめでした。
先輩と話をした結果 ruby-quota のソースコード、linux の quotactl あたりの確認をするかということになりました。
結果、ruby-quotaで書かれているコードを変更して暫定対応とすることとしました。以下にその対応を記載します。

# ruby-quota の 変更

手順は

* quota.c の編集
* コンパイル
* gemファイルの入れ替え
* Quota.setquotaの再実行

です

## quota.c の編集

差分を表示します。
※ 取り急ぎの対応です
※ わたくしの環境での修正になりますのでご注意ください。

curspace がらみでエラーが出るようだったので、下記の通り編集しています。

```quota.c.diff
--- from_file
+++ C:\Users\katsumata\nfEngine\ruby-quota-master\quota.c
@@ -350,7 +350,7 @@
   c_dqb->dqb_bhardlimit = GetMember("bhardlimit");
   c_dqb->dqb_bsoftlimit = GetMember("bsoftlimit");
 #if !defined(USE_LINUX_CURBLOCKS)
-  c_dqb->dqb_curspace   = GetMember("curspace");
+  c_dqb->dqb_curspace   = GetMember("curblocks");
 #else
   c_dqb->dqb_curblocks  = GetMember("curblocks");
 #endif
@@ -612,4 +612,4 @@
 
   rb_alias(CLASS_OF(rb_mQuota), rb_intern("setlimit"), rb_intern("setqlim"));
   rb_alias(CLASS_OF(rb_mQuota), rb_intern("setusage"), rb_intern("setuse"));
```

## コンパイル

書き換えたものを含んだgemをまるごとアップロードし実際に使うサーバ上でコンパイルします。
gemのディレクトリ内で

```console.sh
$ ruby ./extconf.rb
checking for unistd.h... yes
checking for linux/quota.h... yes
checking for linux/types.h... yes
checking for sys/quota.h... yes
checking for sys/types.h... yes
checking for sys/fs/ufs_quota.h... no
checking for ufs/ufs/quota.h... no
checking for sys/ucred.h... no
checking for sys/statvfs.h... yes
creating Makefile

$ make
compiling quota.c
quota.c: In function ‘__getdevice’:
quota.c:213: 警告: unused variable ‘buf’
linking shared-object quota.so

```

上記手順を踏むと quota.so というファイルが生成されます。

## gemファイルの差し替え

このファイルを既存の ruby-quota の quota.so 差し替えます。
私の環境では差し替えるべきファイルは以下の三点でした。

* ./lib/ruby/gems/2.1.0/gems/ruby-quota-0.8.0/quota.so
* ./lib/ruby/gems/2.1.0/gems/ruby-quota-0.8.0/lib/quota.so
* ./lib/ruby/gems/2.1.0/extensions/x86_64-linux/2.1.0-static/ruby-quota-0.8.0/quota.so
  
これを差し替えた上で再実行します。

## 再実行

```setquota_ok.rb
require 'quota'

group = Quota::GroupID.new(1247) 
group_quota = Quota.getquota('/var', group)
=> #<struct Struct::DiskQuota
 bhardlimit=1124000,
 bsoftlimit=1024000,
 curblocks=499712,
 ihardlimit=0,
 isoftlimit=0,
 curinodes=122,
 btimelimit=0,
 itimelimit=0>

group_quota.bhardlimit = 1224000
group_quota.bsoftlimit = 1124000

p group_quota
#<struct Struct::DiskQuota bhardlimit=1224000, bsoftlimit=1124000, curblocks=499712, ihardlimit=0, isoftlimit=0, curinodes=122, btimelimit=0, itimelimit=0>

Quota.setquota('/var', group, group_quota)
=> nil

quota_after = Quota.getquota('/var', group)
=> #<struct Struct::DiskQuota
 bhardlimit=1224000,
 bsoftlimit=1124000,
 curblocks=499712,
 ihardlimit=0,
 isoftlimit=0,
 curinodes=122,
 btimelimit=0,
 itimelimit=0>

```

上記の設定をした後に

* quota.setquotaでエラーがでなくなった
* quota.getquotaで設定している値がとれている
* repquota コマンドで変更が反映されている

がうまくいっていることを確認しました。



# 終わりに

gemを手作業で直接変更してしまっているので、同環境を構築しなければいけない際に自動で行くようにしておくことが課題。
とりあえず動きました。