ライブラリがありそうだが、ファイル編集するようなものは見つけられなかった。
やりたいこと
GitHubでPushできるようにするためのSSH設定を自動化したい。
しかし、~/.ssh/config
ファイル編集が曲者。簡単なツールやライブラリが見当たらない。
~/.ssh/config
ファイルを編集するクラスSshConfigurator.py
を作成したい
問題
~/.ssh/config
ファイルはシステム共有設定である。
今回、GitHubアカウント設定以外にもすでに設定があるかもしれない。それらを邪魔しないようにファイルを編集することが難しい。
Pythonインタフェース
import SshConfigurator
c = SshConfigurator.SshConfigurator()
読み込み
- c.Hosts # {‘some_host’: {…}, ‘some_host2’: {…}, …}
- c.Hosts[‘some_host’] # {‘Host’: ‘some_host’, ‘Port’: 22, …}
- c.Hosts[‘some_host’][‘Host’] # ‘some_host’
- c.Hosts[‘some_host’][‘Port’] # 22
書き込み
追記
ファイルの末尾に追記する。
import SshConfigurator host = {} host['Host'] = 'github.com.{user}' host['User'] = 'git' host['Port'] = 22 host['HostName'] = 'github.com' host['IdentityFile'] = '~/.ssh/rsa_{user}' host['TCPKeepAlive'] = 'yes' host['IdentitiesOnly'] = 'yes' c = SshConfigurator.SshConfigurator.Host() c.appendHost(host)
編集
import SshConfigurator c = SshConfigurator.SshConfigurator() c.Hosts['github.com.{user111}']['Port'] = 33 c.Hosts['github.com.{user222}']['Port'] = 44 c.modifyHost([c.Hosts['github.com.{user111}'], c.Hosts['github.com.{user222}'], ])
削除
import SshConfigurator c = SshConfigurator.SshConfigurator() c.deleteHost(['github.com.{user111}', 'github.com.{user222}'])
問題
ファイル内の設定順序を保とうとすると一気に難しくなる。
Pythonで順序付きdict型はOrderedDictを使えばできそう。
http://shannon-lab.org/?p=1743
しかし、問題はconfigファイルの内容はHostだけでなくコメント行やHost外定義も含まれる点。一体どうやってファイル位置を保ったまま、Hostの編集、削除、挿入すればいいのか。
やはり管理するアカウントの設定部分のみ別ファイルにし、あとで追記する形がよいか。そうすれば複数行マッチで定義部分を抜き出すこともできる。そういう管理スクリプトなどを書いている人もいるらしい。
ならば順序に関しては関与しないほうがいいか。
- ファイル内順序には関与しない
- ユーザが手で管理したい場合があるから
- ユーザが自作ツールなどで管理している場合があるから
そして、GitHubアカウント管理ツールで作成したconfig部分は別ファイルとして出力しておく。configマージツールを使っている場合に利用する。
結論
ファイル順序には関与しない。末尾の追記、編集、削除のみ行う。
削除が難しそうだが、複数行マッチング置換で解決する。
削除
Python: 正規表現で複数行マッチングの置換を行う - 無粋な日々に
手抜き案
追記はすでに実装した。
- configの編集、削除は行わないようにする
- データの取得はparamikoかfabricを使う
これで何もコードを書かなくてよくなる。
paramikoインストール
https://github.com/paramiko/paramiko
LGPLライセンス。
$ sudo pip3 install paramiko Downloading/unpacking paramiko Downloading paramiko-2.1.2-py2.py3-none-any.whl (172kB): 172kB downloaded Downloading/unpacking pyasn1>=0.1.7 (from paramiko) Downloading pyasn1-0.2.3-py2.py3-none-any.whl (53kB): 53kB downloaded Downloading/unpacking cryptography>=1.1 (from paramiko) Downloading cryptography-1.8.1.tar.gz (423kB): 423kB downloaded Running setup.py (path:/tmp/pip_build_root/cryptography/setup.py) egg_info for package cryptography no previously-included directories found matching 'docs/_build' warning: no previously-included files matching '*' found under directory 'vectors' Downloading/unpacking idna>=2.1 (from cryptography>=1.1->paramiko) Downloading idna-2.5-py2.py3-none-any.whl (55kB): 55kB downloaded Downloading/unpacking asn1crypto>=0.21.0 (from cryptography>=1.1->paramiko) Downloading asn1crypto-0.22.0-py2.py3-none-any.whl (97kB): 97kB downloaded Downloading/unpacking packaging (from cryptography>=1.1->paramiko) Downloading packaging-16.8-py2.py3-none-any.whl Requirement already satisfied (use --upgrade to upgrade): six>=1.4.1 in /usr/local/lib/python3.4/dist-packages (from cryptography>=1.1->paramiko) Downloading/unpacking setuptools>=11.3 (from cryptography>=1.1->paramiko) Downloading setuptools-34.3.3-py2.py3-none-any.whl (389kB): 389kB downloaded Downloading/unpacking cffi>=1.4.1 (from cryptography>=1.1->paramiko) Downloading cffi-1.10.0.tar.gz (418kB): 418kB downloaded Running setup.py (path:/tmp/pip_build_root/cffi/setup.py) egg_info for package cffi Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Downloading/unpacking pyparsing (from packaging->cryptography>=1.1->paramiko) Downloading pyparsing-2.2.0-py2.py3-none-any.whl (56kB): 56kB downloaded Downloading/unpacking appdirs>=1.4.0 (from setuptools>=11.3->cryptography>=1.1->paramiko) Downloading appdirs-1.4.3-py2.py3-none-any.whl Downloading/unpacking pycparser (from cffi>=1.4.1->cryptography>=1.1->paramiko) Downloading pycparser-2.17.tar.gz (231kB): 231kB downloaded Running setup.py (path:/tmp/pip_build_root/pycparser/setup.py) egg_info for package pycparser warning: no previously-included files matching 'yacctab.*' found under directory 'tests' warning: no previously-included files matching 'lextab.*' found under directory 'tests' warning: no previously-included files matching 'yacctab.*' found under directory 'examples' warning: no previously-included files matching 'lextab.*' found under directory 'examples' Installing collected packages: paramiko, pyasn1, cryptography, idna, asn1crypto, packaging, setuptools, cffi, pyparsing, appdirs, pycparser Running setup.py install for cryptography Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found c/_cffi_backend.c:15:17: fatal error: ffi.h: そのようなファイルやディレクトリはありません #include <ffi.h> ^ compilation terminated. Traceback (most recent call last): File "/usr/lib/python3.4/distutils/unixccompiler.py", line 116, in _compile extra_postargs) File "/usr/lib/python3.4/distutils/ccompiler.py", line 909, in spawn spawn(cmd, dry_run=self.dry_run) File "/usr/lib/python3.4/distutils/spawn.py", line 36, in spawn _spawn_posix(cmd, search_path, dry_run=dry_run) File "/usr/lib/python3.4/distutils/spawn.py", line 162, in _spawn_posix % (cmd, exit_status)) distutils.errors.DistutilsExecError: command 'i686-linux-gnu-gcc' failed with exit status 1 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3.4/distutils/core.py", line 148, in setup dist.run_commands() File "/usr/lib/python3.4/distutils/dist.py", line 955, in run_commands self.run_command(cmd) File "/usr/lib/python3.4/distutils/dist.py", line 974, in run_command cmd_obj.run() File "/usr/lib/python3/dist-packages/setuptools/command/bdist_egg.py", line 185, in run cmd = self.call_command('install_lib', warn_dir=0) File "/usr/lib/python3/dist-packages/setuptools/command/bdist_egg.py", line 171, in call_command self.run_command(cmdname) File "/usr/lib/python3.4/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/lib/python3.4/distutils/dist.py", line 974, in run_command cmd_obj.run() File "/usr/lib/python3/dist-packages/setuptools/command/install_lib.py", line 21, in run self.build() File "/usr/lib/python3.4/distutils/command/install_lib.py", line 109, in build self.run_command('build_ext') File "/usr/lib/python3.4/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/lib/python3.4/distutils/dist.py", line 974, in run_command cmd_obj.run() File "/usr/lib/python3/dist-packages/setuptools/command/build_ext.py", line 49, in run _build_ext.run(self) File "/usr/lib/python3.4/distutils/command/build_ext.py", line 339, in run self.build_extensions() File "/usr/lib/python3.4/distutils/command/build_ext.py", line 448, in build_extensions self.build_extension(ext) File "/usr/lib/python3/dist-packages/setuptools/command/build_ext.py", line 178, in build_extension _build_ext.build_extension(self,ext) File "/usr/lib/python3.4/distutils/command/build_ext.py", line 503, in build_extension depends=ext.depends) File "/usr/lib/python3.4/distutils/ccompiler.py", line 574, in compile self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) File "/usr/lib/python3.4/distutils/unixccompiler.py", line 118, in _compile raise CompileError(msg) distutils.errors.CompileError: command 'i686-linux-gnu-gcc' failed with exit status 1 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 1025, in run_setup run_setup(setup_script, args) File "/usr/lib/python3/dist-packages/setuptools/sandbox.py", line 50, in run_setup lambda: execfile( File "/usr/lib/python3/dist-packages/setuptools/sandbox.py", line 100, in run return func() File "/usr/lib/python3/dist-packages/setuptools/sandbox.py", line 52, in <lambda> {'__file__':setup_script, '__name__':'__main__'} File "/usr/lib/python3/dist-packages/setuptools/compat.py", line 78, in execfile exec(compile(source, fn, 'exec'), globs, locs) File "setup.py", line 232, in <module> File "/usr/lib/python3.4/distutils/core.py", line 163, in setup raise SystemExit("error: " + str(msg)) SystemExit: error: command 'i686-linux-gnu-gcc' failed with exit status 1 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<string>", line 1, in <module> File "/tmp/pip_build_root/cryptography/setup.py", line 335, in <module> **keywords_with_side_effects(sys.argv) File "/usr/lib/python3.4/distutils/core.py", line 108, in setup _setup_distribution = dist = klass(attrs) File "/usr/lib/python3/dist-packages/setuptools/dist.py", line 239, in __init__ self.fetch_build_eggs(attrs.pop('setup_requires')) File "/usr/lib/python3/dist-packages/setuptools/dist.py", line 264, in fetch_build_eggs replace_conflicting=True File "/usr/lib/python3/dist-packages/pkg_resources.py", line 620, in resolve dist = best[req.key] = env.best_match(req, ws, installer) File "/usr/lib/python3/dist-packages/pkg_resources.py", line 858, in best_match return self.obtain(req, installer) # try and download/install File "/usr/lib/python3/dist-packages/pkg_resources.py", line 870, in obtain return installer(requirement) File "/usr/lib/python3/dist-packages/setuptools/dist.py", line 314, in fetch_build_egg return cmd.easy_install(req) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 616, in easy_install return self.install_item(spec, dist.location, tmpdir, deps) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 646, in install_item dists = self.install_eggs(spec, download, tmpdir) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 834, in install_eggs return self.build_and_install(setup_script, setup_base) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 1040, in build_and_install self.run_setup(setup_script, setup_base, args) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 1028, in run_setup raise DistutilsError("Setup script exited with %s" % (v.args[0],)) distutils.errors.DistutilsError: Setup script exited with error: command 'i686-linux-gnu-gcc' failed with exit status 1 Complete output from command /usr/bin/python3 -c "import setuptools, tokenize;__file__='/tmp/pip_build_root/cryptography/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-w6pgmxjz-record/install-record.txt --single-version-externally-managed --compile: Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable No package 'libffi' found c/_cffi_backend.c:15:17: fatal error: ffi.h: そのようなファイルやディレクトリはありません #include <ffi.h> ^ compilation terminated. Traceback (most recent call last): File "/usr/lib/python3.4/distutils/unixccompiler.py", line 116, in _compile extra_postargs) File "/usr/lib/python3.4/distutils/ccompiler.py", line 909, in spawn spawn(cmd, dry_run=self.dry_run) File "/usr/lib/python3.4/distutils/spawn.py", line 36, in spawn _spawn_posix(cmd, search_path, dry_run=dry_run) File "/usr/lib/python3.4/distutils/spawn.py", line 162, in _spawn_posix % (cmd, exit_status)) distutils.errors.DistutilsExecError: command 'i686-linux-gnu-gcc' failed with exit status 1 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3.4/distutils/core.py", line 148, in setup dist.run_commands() File "/usr/lib/python3.4/distutils/dist.py", line 955, in run_commands self.run_command(cmd) File "/usr/lib/python3.4/distutils/dist.py", line 974, in run_command cmd_obj.run() File "/usr/lib/python3/dist-packages/setuptools/command/bdist_egg.py", line 185, in run cmd = self.call_command('install_lib', warn_dir=0) File "/usr/lib/python3/dist-packages/setuptools/command/bdist_egg.py", line 171, in call_command self.run_command(cmdname) File "/usr/lib/python3.4/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/lib/python3.4/distutils/dist.py", line 974, in run_command cmd_obj.run() File "/usr/lib/python3/dist-packages/setuptools/command/install_lib.py", line 21, in run self.build() File "/usr/lib/python3.4/distutils/command/install_lib.py", line 109, in build self.run_command('build_ext') File "/usr/lib/python3.4/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/lib/python3.4/distutils/dist.py", line 974, in run_command cmd_obj.run() File "/usr/lib/python3/dist-packages/setuptools/command/build_ext.py", line 49, in run _build_ext.run(self) File "/usr/lib/python3.4/distutils/command/build_ext.py", line 339, in run self.build_extensions() File "/usr/lib/python3.4/distutils/command/build_ext.py", line 448, in build_extensions self.build_extension(ext) File "/usr/lib/python3/dist-packages/setuptools/command/build_ext.py", line 178, in build_extension _build_ext.build_extension(self,ext) File "/usr/lib/python3.4/distutils/command/build_ext.py", line 503, in build_extension depends=ext.depends) File "/usr/lib/python3.4/distutils/ccompiler.py", line 574, in compile self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) File "/usr/lib/python3.4/distutils/unixccompiler.py", line 118, in _compile raise CompileError(msg) distutils.errors.CompileError: command 'i686-linux-gnu-gcc' failed with exit status 1 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 1025, in run_setup run_setup(setup_script, args) File "/usr/lib/python3/dist-packages/setuptools/sandbox.py", line 50, in run_setup lambda: execfile( File "/usr/lib/python3/dist-packages/setuptools/sandbox.py", line 100, in run return func() File "/usr/lib/python3/dist-packages/setuptools/sandbox.py", line 52, in <lambda> {'__file__':setup_script, '__name__':'__main__'} File "/usr/lib/python3/dist-packages/setuptools/compat.py", line 78, in execfile exec(compile(source, fn, 'exec'), globs, locs) File "setup.py", line 232, in <module> File "/usr/lib/python3.4/distutils/core.py", line 163, in setup raise SystemExit("error: " + str(msg)) SystemExit: error: command 'i686-linux-gnu-gcc' failed with exit status 1 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<string>", line 1, in <module> File "/tmp/pip_build_root/cryptography/setup.py", line 335, in <module> **keywords_with_side_effects(sys.argv) File "/usr/lib/python3.4/distutils/core.py", line 108, in setup _setup_distribution = dist = klass(attrs) File "/usr/lib/python3/dist-packages/setuptools/dist.py", line 239, in __init__ self.fetch_build_eggs(attrs.pop('setup_requires')) File "/usr/lib/python3/dist-packages/setuptools/dist.py", line 264, in fetch_build_eggs replace_conflicting=True File "/usr/lib/python3/dist-packages/pkg_resources.py", line 620, in resolve dist = best[req.key] = env.best_match(req, ws, installer) File "/usr/lib/python3/dist-packages/pkg_resources.py", line 858, in best_match return self.obtain(req, installer) # try and download/install File "/usr/lib/python3/dist-packages/pkg_resources.py", line 870, in obtain return installer(requirement) File "/usr/lib/python3/dist-packages/setuptools/dist.py", line 314, in fetch_build_egg return cmd.easy_install(req) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 616, in easy_install return self.install_item(spec, dist.location, tmpdir, deps) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 646, in install_item dists = self.install_eggs(spec, download, tmpdir) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 834, in install_eggs return self.build_and_install(setup_script, setup_base) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 1040, in build_and_install self.run_setup(setup_script, setup_base, args) File "/usr/lib/python3/dist-packages/setuptools/command/easy_install.py", line 1028, in run_setup raise DistutilsError("Setup script exited with %s" % (v.args[0],)) distutils.errors.DistutilsError: Setup script exited with error: command 'i686-linux-gnu-gcc' failed with exit status 1 ---------------------------------------- Cleaning up... Command /usr/bin/python3 -c "import setuptools, tokenize;__file__='/tmp/pip_build_root/cryptography/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-w6pgmxjz-record/install-record.txt --single-version-externally-managed --compile failed with error code 1 in /tmp/pip_build_root/cryptography Storing debug log for failure in /home/mint/.pip/pip.log
インストールに失敗しているっぽい。
distutils.errors.DistutilsError: Setup script exited with error: command 'i686-linux-gnu-gcc' failed with exit status 1
paramikoをimportしようとするとエラーが出る。
import paramiko
ImportError: No module named 'cryptography'
依存関係を解決してくれないパッケージ管理マネージャpip…。
何をすればいいのかわからない。諦めよう。
所感
本題とは関係ないところなので乗り気ではない。iniファイルだったら簡単に編集できたのに…。