cobblerで使うKickstart/Preseedファイルで日本語を使う方法


日本語を含むKicstart/PreseedファイルをCobblerで扱おうとするとエラーが出るので、どうにかならんものかと試行錯誤したのでメモ


cobblerのインストール方法は、今回は省略します。


##準備（ざっくり書きます）

-  インストーラのlinuxカーネルと初期イメージ(initramfs)を取得
- cobblerにdistroとして登録

```
$cobbler distro add --name="test" --kernel="/opt/linux-kern" --initrd="/opt/initrd"
```

- kickstart/preseedファイルを作成(今回はubuntuを想定してpreseedファイル)

```test_preseed.seed
#テストコメント <= (注意)コメントが日本語
#サンプルなので、とりあえずこれだけ
d-i debian-installer/locale string en_US
d-i console-setup/ask_detect boolean false
d-i keyboard-configuration/layoutcode string us
d-i keyboard-configuration/variantcode string
```

- profileの登録

```
 $cobbler profile add --name="test-profile" --distro="test" --kickstart="test_preseed.seed"
```

で日本語付きのkickstart/preseedファイルを指定したprofileを作成する

##コマンドでpreseedファイルを確認しようとしてみる

cobblerのAPIを叩いて確認をしてもいいですが、手っ取り早くコマンドで、

```
$cobbler profile getks --name test-profile
# This kickstart had errors that prevented it from being rendered correctly.
# The cobbler.log should have information relating to this failure.
```

この通り、kickstart/preseedファイルに日本語が混じると、エラーが出てファイルが表示されません。ログを見ます

```/var/log/cobbler/cobbler.log
Wed Apr 29 14:01:05 2015 - INFO | generate_kickstart
Wed Apr 29 14:01:05 2015 - INFO | Exception occured: <type 'exceptions.UnicodeDecodeError'>
Wed Apr 29 14:01:05 2015 - INFO | Exception value: 'ascii' codec can't decode byte 0xe3 in position 181: ordinal not in range(128)
Wed Apr 29 14:01:05 2015 - INFO | Exception Info:
  File "/usr/lib/python2.7/dist-packages/cobbler/remote.py", line 1025, in generate_kickstart
    return self.api.generate_kickstart(profile,system)
   File "/usr/lib/python2.7/dist-packages/cobbler/api.py", line 684, in generate_kickstart
    return self.kickgen.generate_kickstart_for_profile(profile)
   File "/usr/lib/python2.7/dist-packages/cobbler/kickgen.py", line 311, in generate_kickstart_for_profile
    return self.generate_kickstart(profile=g)
   File "/usr/lib/python2.7/dist-packages/cobbler/kickgen.py", line 289, in generate_kickstart
    data = self.templar.render(raw_data, meta, None, obj)
   File "/usr/lib/python2.7/dist-packages/cobbler/templar.py", line 116, in render
    data_out = self.render_cheetah(raw_data, search_table, subject)
   File "/usr/lib/python2.7/dist-packages/cobbler/templar.py", line 201, in render_cheetah
    t = Template(source=raw_data, searchList=[search_table], compilerSettings={'useStackFrame':False})
   File "DynamicallyCompiledCheetahTemplate.py", line 58, in __init__
   File "_etc_cobbler_cheetah_macros.py", line 58, in __init__
   File "/usr/lib/python2.7/dist-packages/Cheetah/Template.py", line 1259, in __init__
    self._compile(source, file, compilerSettings=compilerSettings)
   File "/usr/lib/python2.7/dist-packages/Cheetah/Template.py", line 1553, in _compile
    keepRefToGeneratedCode=True)
   File "/usr/lib/python2.7/dist-packages/cobbler/template_api.py", line 127, in compile
    return Cheetah.Template.Template.compile(*args, **kwargs)
   File "/usr/lib/python2.7/dist-packages/Cheetah/Template.py", line 740, in compile
    settings=(compilerSettings or {}))
   File "/usr/lib/python2.7/dist-packages/Cheetah/Compiler.py", line 1579, in __init__
    source = unicode(source)
```

ASCIIじゃ、デコードできないよ！って怒られているように見える。

どうやら、cobblerでは、kickstart/preseedファイルは、Cheetahというテンプレートエンジンで解釈されてからユーザに表示されているらしい <= おさらい

Cheetahでencodeをutf-8とか指定して、読み込ませてあげることができたら日本語も対応できそうなことが分かる。
どうやって、Cheetahにencoding情報を教えてあげればいいんだろう？

とりあえず/usr/lib/python2.7/dist-packages/Cheetah/Compiler.pyを開いてみる

```/usr/lib/python2.7/dist-packages/Cheetah/Compiler.py
1558         if source == "":
1559             warnings.warn("You supplied an empty string for the source!", )
1560
1561         else:
1562             unicodeMatch = unicodeDirectiveRE.search(source)
1563             encodingMatch = encodingDirectiveRE.search(source)
1564             if unicodeMatch:
1565                 if encodingMatch:
1566                     raise ParseError(
1567                         self, "#encoding and #unicode are mutually exclusive! "
1568                         "Use one or the other.")
1569                 source = unicodeDirectiveRE.sub('', source)
1570                 if isinstance(source, str):
1571                     encoding = unicodeMatch.group(1) or 'ascii'
1572                     source = unicode(source, encoding)
1573             elif encodingMatch:
1574                 encodings = encodingMatch.groups()
1575                 if len(encodings):
1576                     encoding = encodings[0]
1577                     source = source.decode(encoding)
1578             else:
1579                 source = unicode(source)
````

1563行目のencodingMatchでencodeを調べているっぽい（引数のsourceはpreseed/kickstartファイルに書かれていた文字列が入っている変数）、何をやってるんだろう。見てみる。

どこで定義されているのか？

```/usr/lib/python2.7/dist-packages/Cheetah/Compiler.py
  27 from Cheetah.Parser import Parser, ParseError, specialVarRE, \
  28      STATIC_CACHE, REFRESH_CACHE, SET_LOCAL, SET_GLOBAL, SET_MODULE, \
  29      unicodeDirectiveRE, encodingDirectiveRE, escapedNewlineRE
```
あった、あった。Cheetah.Parserというところに、定義があるらしい。見てみる

```/usr/lib/python2.7/dist-packages/Cheetah/Parser.py
 139 specialVarRE=re.compile(r'([a-zA-z_]+)@') # for matching specialVar comments
 140 # e.g. ##author@ Tavis Rudd
 141
 142 unicodeDirectiveRE = re.compile(
 143     r'(?:^|\r\n|\r|\n)\s*#\s{0,5}unicode[:\s]*([-\w.]*)\s*(?:\r\n|\r|\n)', re.MULTILINE)
 144 encodingDirectiveRE = re.compile(
 145     r'(?:^|\r\n|\r|\n)\s*#\s{0,5}encoding[:\s]*([-\w.]*)\s*(?:\r\n|\r|\n)', re.MULTILINE)
```

なるほど、Cheetahではファイルの先頭に、encoding情報が書かれていた場合、正規表現でそれを抜き出して、その情報でdecodeするらしい。


正規表現を読み解いてみると、kickstart/preseedファイルの先頭に「#encoding: utf-8 ¥n」を書けばいけることが分かった

```test_preseed.seed
# encoding:  utf-8 

#テストコメント <= (注意)コメントが日本語
#サンプルなので、とりあえずこれだけ
d-i debian-installer/locale string en_US
d-i console-setup/ask_detect boolean false
d-i keyboard-configuration/layoutcode string us
d-i keyboard-configuration/variantcode string
```


##結論

kickstart/preseedファイルに日本語を混ぜる場合は、ファイルの先頭に下記の行を記入する

```hogehoge.ks/hogehoge.seed
# encoding:  utf-8 

# 下記省略
#
```

ファイル先頭にencoding情報を書いておくことで、テンプレートエンジンのCheetahがきちんとdecodeできるようになり、cobblerでうまくハンドリングできるようになる！

##所感
Cheetahを使ったことがなかったので、ソースを読まないと解決できなかったけど。
これぐらい、cobblerでkickstart/preseedファイルを登録する段階で、encodingが指定されていない警告(できれば指定方法も)ぐらい出してくれればいいのになぁ・・・・