Vagrant up 時に Ansible で構成を実装させて、トライアンドエラー出来るようにしたい

Vagrant up 時に Ansible で構成を自動的に実装させたい

トライアンドエラーしたい

環境

環境メモ

takashi@takashi-desktop:~/vagrant/jiratest1$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.5 LTS"
takashi@takashi-desktop:~/vagrant/jiratest1$ 
takashi@takashi-desktop:~/vagrant/jiratest1$ uname -a
Linux takashi-desktop 4.4.0-139-generic #165~14.04.1-Ubuntu SMP Wed Oct 31 10:55:11 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
takashi@takashi-desktop:~/vagrant/jiratest1$ 
takashi@takashi-desktop:~/vagrant/jiratest1$ python3 -V
Python 3.6.4
takashi@takashi-desktop:~/vagrant/jiratest1$ pip3 -V
pip 18.0 from /usr/local/lib/python3.6/site-packages/pip (python 3.6)
takashi@takashi-desktop:~/vagrant/jiratest1$ 

Ansible のインストール

Ansible をインストールする

takashi@takashi-desktop:~/vagrant/jiratest1$ sudo pip3 install ansible
[sudo] password for takashi: 
The directory '/home/takashi/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/takashi/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting ansible
  Downloading https://files.pythonhosted.org/packages/9e/df/b7ce359f9cc16864e0d5c9c93efe69f576fc437e74549bcc142f02cd4216/ansible-2.7.4.tar.gz (11.8MB)
    100% |████████████████████████████████| 11.8MB 3.6MB/s 
Requirement already satisfied: jinja2 in /usr/local/lib/python3.6/site-packages (from ansible) (2.10)
Collecting PyYAML (from ansible)
  Downloading https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz (270kB)
    100% |████████████████████████████████| 276kB 15.9MB/s 
Collecting paramiko (from ansible)
  Downloading https://files.pythonhosted.org/packages/cf/ae/94e70d49044ccc234bfdba20114fa947d7ba6eb68a2e452d89b920e62227/paramiko-2.4.2-py2.py3-none-any.whl (193kB)
    100% |████████████████████████████████| 194kB 9.2MB/s 
Requirement already satisfied: cryptography in /home/takashi/.local/lib/python3.6/site-packages (from ansible) (2.3.1)
Requirement already satisfied: setuptools in /usr/local/lib/python3.6/site-packages (from ansible) (28.8.0)
Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.6/site-packages (from jinja2->ansible) (1.0)
Collecting pyasn1>=0.1.7 (from paramiko->ansible)
  Downloading https://files.pythonhosted.org/packages/d1/a1/7790cc85db38daa874f6a2e6308131b9953feb1367f2ae2d1123bb93a9f5/pyasn1-0.4.4-py2.py3-none-any.whl (72kB)
    100% |████████████████████████████████| 81kB 4.8MB/s 
Collecting pynacl>=1.0.1 (from paramiko->ansible)
  Downloading https://files.pythonhosted.org/packages/27/15/2cd0a203f318c2240b42cd9dd13c931ddd61067809fee3479f44f086103e/PyNaCl-1.3.0-cp34-abi3-manylinux1_x86_64.whl (759kB)
    100% |████████████████████████████████| 768kB 4.4MB/s 
Collecting bcrypt>=3.1.3 (from paramiko->ansible)
  Downloading https://files.pythonhosted.org/packages/b8/09/905ec939994e2c49dcffff72f823802557f166b3815ea54c1db3671eed42/bcrypt-3.1.4-cp36-cp36m-manylinux1_x86_64.whl (54kB)
    100% |████████████████████████████████| 61kB 11.8MB/s 
Requirement already satisfied: asn1crypto>=0.21.0 in /home/takashi/.local/lib/python3.6/site-packages (from cryptography->ansible) (0.24.0)
Requirement already satisfied: cffi!=1.11.3,>=1.7 in /home/takashi/.local/lib/python3.6/site-packages (from cryptography->ansible) (1.11.5)
Requirement already satisfied: idna>=2.1 in /usr/local/lib/python3.6/site-packages (from cryptography->ansible) (2.6)
Requirement already satisfied: six>=1.4.1 in /usr/local/lib/python3.6/site-packages (from cryptography->ansible) (1.11.0)
Requirement already satisfied: pycparser in /home/takashi/.local/lib/python3.6/site-packages (from cffi!=1.11.3,>=1.7->cryptography->ansible) (2.18)
Installing collected packages: PyYAML, pyasn1, pynacl, bcrypt, paramiko, ansible
  Running setup.py install for PyYAML ... done
  Running setup.py install for ansible ... done
Successfully installed PyYAML-3.13 ansible-2.7.4 bcrypt-3.1.4 paramiko-2.4.2 pyasn1-0.4.4 pynacl-1.3.0
takashi@takashi-desktop:~/vagrant/jiratest1$ 
takashi@takashi-desktop:~/vagrant/jiratest1$ 
takashi@takashi-desktop:~/vagrant/jiratest1$ ansible --version
ansible 2.7.4
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/takashi/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.6 (default, Nov 13 2018, 12:45:42) [GCC 4.8.4]
takashi@takashi-desktop:~/vagrant/jiratest1$ 

良さそう

Vagrantfile の編集

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "centos/6"
  config.vm.box_check_update = false

  # Network
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.network "public_network"

  # RAM setting
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "1024"
  end

  # Run Ansible from the Vagrant Host
  config.vm.provision "ansible" do |ansible|
      ansible.playbook = "playbook.yaml"
  end
end

playbook.yaml の作成

- hosts: all
  tasks:
    - name: ensure ntpd is at the latest version
      yum: pkg=ntp state=latest
      notify:
      - restart ntpd
  handlers:
    - name: restart ntpd
      service: name=ntpd state=restarted

vagrant up

失敗したけど、特に ssh の設定を行うこと無く、 playbook.yaml の内容が起動時に反映されることがわかった。

takashi@takashi-desktop:~/vagrant/ansible_test$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos/6'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: ansible_test_default_1544475292941_35884
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Available bridged network interfaces:
1) eth0
2) virbr0
==> default: When choosing an interface, it is usually the one that is
==> default: being used to connect to the internet.
    default: Which interface should the network bridge to? 1 
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
    default: Adapter 3: bridged
==> default: Forwarding ports...
    default: 22 (guest) => 2200 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection reset. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
[default] No installation found.
Loaded plugins: fastestmirror, security
Setting up Install Process
No package kernel-devel-2.6.32-696.13.2.el6.x86_64 available.
Package 1:make-3.81-23.el6.x86_64 already installed and latest version
Package 4:perl-5.10.1-144.el6.x86_64 already installed and latest version
Package bzip2-1.0.5-7.el6_0.x86_64 already installed and latest version
Resolving Dependencies
--> Running transaction check
---> Package binutils.x86_64 0:2.20.51.0.2-5.47.el6_9.1 will be updated
---> Package binutils.x86_64 0:2.20.51.0.2-5.48.el6 will be an update
---> Package gcc.x86_64 0:4.4.7-23.el6 will be installed
--> Processing Dependency: libgomp = 4.4.7-23.el6 for package: gcc-4.4.7-23.el6.x86_64
--> Processing Dependency: cpp = 4.4.7-23.el6 for package: gcc-4.4.7-23.el6.x86_64
--> Processing Dependency: libgcc >= 4.4.7-23.el6 for package: gcc-4.4.7-23.el6.x86_64
--> Processing Dependency: glibc-devel >= 2.2.90-12 for package: gcc-4.4.7-23.el6.x86_64
--> Processing Dependency: cloog-ppl >= 0.15 for package: gcc-4.4.7-23.el6.x86_64
--> Processing Dependency: libgomp.so.1()(64bit) for package: gcc-4.4.7-23.el6.x86_64
--> Running transaction check
---> Package cloog-ppl.x86_64 0:0.15.7-1.2.el6 will be installed
--> Processing Dependency: libppl_c.so.2()(64bit) for package: cloog-ppl-0.15.7-1.2.el6.x86_64
--> Processing Dependency: libppl.so.7()(64bit) for package: cloog-ppl-0.15.7-1.2.el6.x86_64
---> Package cpp.x86_64 0:4.4.7-23.el6 will be installed
--> Processing Dependency: libmpfr.so.1()(64bit) for package: cpp-4.4.7-23.el6.x86_64
---> Package glibc-devel.x86_64 0:2.12-1.212.el6 will be installed
--> Processing Dependency: glibc-headers = 2.12-1.212.el6 for package: glibc-devel-2.12-1.212.el6.x86_64
--> Processing Dependency: glibc = 2.12-1.212.el6 for package: glibc-devel-2.12-1.212.el6.x86_64
--> Processing Dependency: glibc-headers for package: glibc-devel-2.12-1.212.el6.x86_64
---> Package libgcc.x86_64 0:4.4.7-18.el6 will be updated
---> Package libgcc.x86_64 0:4.4.7-23.el6 will be an update
---> Package libgomp.x86_64 0:4.4.7-23.el6 will be installed
--> Running transaction check
---> Package glibc.x86_64 0:2.12-1.209.el6_9.2 will be updated
--> Processing Dependency: glibc = 2.12-1.209.el6_9.2 for package: glibc-common-2.12-1.209.el6_9.2.x86_64
---> Package glibc.x86_64 0:2.12-1.212.el6 will be an update
---> Package glibc-headers.x86_64 0:2.12-1.212.el6 will be installed
--> Processing Dependency: kernel-headers >= 2.2.1 for package: glibc-headers-2.12-1.212.el6.x86_64
--> Processing Dependency: kernel-headers for package: glibc-headers-2.12-1.212.el6.x86_64
---> Package mpfr.x86_64 0:2.4.1-6.el6 will be installed
---> Package ppl.x86_64 0:0.10.2-11.el6 will be installed
--> Running transaction check
---> Package glibc-common.x86_64 0:2.12-1.209.el6_9.2 will be updated
---> Package glibc-common.x86_64 0:2.12-1.212.el6 will be an update
---> Package kernel-headers.x86_64 0:2.6.32-754.9.1.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package             Arch        Version                     Repository    Size
================================================================================
Installing:
 gcc                 x86_64      4.4.7-23.el6                base          10 M
Updating:
 binutils            x86_64      2.20.51.0.2-5.48.el6        base         2.8 M
Installing for dependencies:
 cloog-ppl           x86_64      0.15.7-1.2.el6              base          93 k
 cpp                 x86_64      4.4.7-23.el6                base         3.7 M
 glibc-devel         x86_64      2.12-1.212.el6              base         991 k
 glibc-headers       x86_64      2.12-1.212.el6              base         620 k
 kernel-headers      x86_64      2.6.32-754.9.1.el6          updates      4.5 M
 libgomp             x86_64      4.4.7-23.el6                base         135 k
 mpfr                x86_64      2.4.1-6.el6                 base         157 k
 ppl                 x86_64      0.10.2-11.el6               base         1.3 M
Updating for dependencies:
 glibc               x86_64      2.12-1.212.el6              base         3.8 M
 glibc-common        x86_64      2.12-1.212.el6              base          14 M
 libgcc              x86_64      4.4.7-23.el6                base         104 k

Transaction Summary
================================================================================
Install       9 Package(s)
Upgrade       4 Package(s)

Total download size: 43 M
Downloading Packages:
--------------------------------------------------------------------------------
Total                                           7.6 MB/s |  43 MB     00:05     
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
warning: rpmts_HdrFromFdno: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY
Importing GPG key 0xC105B9DE:
 Userid : CentOS-6 Key (CentOS 6 Official Signing Key) <centos-6-key@centos.org>
 Package: centos-release-6-9.el6.12.3.x86_64 (@anaconda-CentOS-201703281317.x86_64/6.9)
 From   : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Updating   : libgcc-4.4.7-23.el6.x86_64                                  1/17 
  Updating   : glibc-common-2.12-1.212.el6.x86_64                          2/17 
  Updating   : glibc-2.12-1.212.el6.x86_64                                 3/17 
  Installing : libgomp-4.4.7-23.el6.x86_64                                 4/17 
  Installing : mpfr-2.4.1-6.el6.x86_64                                     5/17 
  Installing : cpp-4.4.7-23.el6.x86_64                                     6/17 
  Updating   : binutils-2.20.51.0.2-5.48.el6.x86_64                        7/17 
  Installing : ppl-0.10.2-11.el6.x86_64                                    8/17 
  Installing : cloog-ppl-0.15.7-1.2.el6.x86_64                             9/17 
  Installing : kernel-headers-2.6.32-754.9.1.el6.x86_64                   10/17 
  Installing : glibc-headers-2.12-1.212.el6.x86_64                        11/17 
  Installing : glibc-devel-2.12-1.212.el6.x86_64                          12/17 
  Installing : gcc-4.4.7-23.el6.x86_64                                    13/17 
  Cleanup    : binutils-2.20.51.0.2-5.47.el6_9.1.x86_64                   14/17 
  Cleanup    : glibc-common-2.12-1.209.el6_9.2.x86_64                     15/17 
  Cleanup    : glibc-2.12-1.209.el6_9.2.x86_64                            16/17 
  Cleanup    : libgcc-4.4.7-18.el6.x86_64                                 17/17 
  Verifying  : libgomp-4.4.7-23.el6.x86_64                                 1/17 
  Verifying  : gcc-4.4.7-23.el6.x86_64                                     2/17 
  Verifying  : glibc-headers-2.12-1.212.el6.x86_64                         3/17 
  Verifying  : glibc-devel-2.12-1.212.el6.x86_64                           4/17 
  Verifying  : mpfr-2.4.1-6.el6.x86_64                                     5/17 
  Verifying  : cloog-ppl-0.15.7-1.2.el6.x86_64                             6/17 
  Verifying  : binutils-2.20.51.0.2-5.48.el6.x86_64                        7/17 
  Verifying  : glibc-2.12-1.212.el6.x86_64                                 8/17 
  Verifying  : kernel-headers-2.6.32-754.9.1.el6.x86_64                    9/17 
  Verifying  : glibc-common-2.12-1.212.el6.x86_64                         10/17 
  Verifying  : cpp-4.4.7-23.el6.x86_64                                    11/17 
  Verifying  : ppl-0.10.2-11.el6.x86_64                                   12/17 
  Verifying  : libgcc-4.4.7-23.el6.x86_64                                 13/17 
  Verifying  : binutils-2.20.51.0.2-5.47.el6_9.1.x86_64                   14/17 
  Verifying  : libgcc-4.4.7-18.el6.x86_64                                 15/17 
  Verifying  : glibc-2.12-1.209.el6_9.2.x86_64                            16/17 
  Verifying  : glibc-common-2.12-1.209.el6_9.2.x86_64                     17/17 

Installed:
  gcc.x86_64 0:4.4.7-23.el6                                                     

Dependency Installed:
  cloog-ppl.x86_64 0:0.15.7-1.2.el6                                             
  cpp.x86_64 0:4.4.7-23.el6                                                     
  glibc-devel.x86_64 0:2.12-1.212.el6                                           
  glibc-headers.x86_64 0:2.12-1.212.el6                                         
  kernel-headers.x86_64 0:2.6.32-754.9.1.el6                                    
  libgomp.x86_64 0:4.4.7-23.el6                                                 
  mpfr.x86_64 0:2.4.1-6.el6                                                     
  ppl.x86_64 0:0.10.2-11.el6                                                    

Updated:
  binutils.x86_64 0:2.20.51.0.2-5.48.el6                                        

Dependency Updated:
  glibc.x86_64 0:2.12-1.212.el6       glibc-common.x86_64 0:2.12-1.212.el6      
  libgcc.x86_64 0:4.4.7-23.el6       

Complete!
Copy iso file /usr/share/virtualbox/VBoxGuestAdditions.iso into the box /tmp/VBoxGuestAdditions.iso
Installing Virtualbox Guest Additions 5.1.38 - guest version is unknown
Verifying archive integrity... All good.
Uncompressing VirtualBox 5.1.38 Guest Additions for Linux...........
VirtualBox Guest Additions installer
Copying additional installer modules ...
Installing additional modules ...
vboxadd.sh: Starting the VirtualBox Guest Additions.
Failed to set up service vboxadd, please check the log file
/var/log/VBoxGuestAdditions.log for details.
An error occurred during installation of VirtualBox Guest Additions 5.1.38. Some functionality may not work as intended.
In most cases it is OK that the "Window System drivers" installation failed.
vboxadd.sh: Starting the VirtualBox Guest Additions.
vboxadd.sh: failed: Look at /var/log/vboxadd-install.log to find out what went wrong.
vboxadd.sh: failed: modprobe vboxguest failed.
==> default: Checking for guest additions in VM...
    default: No guest additions were detected on the base box for this VM! Guest
    default: additions are required for forwarded ports, shared folders, host only
    default: networking, and more. If SSH fails on this machine, please install
    default: the guest additions and repackage the box to continue.
    default: 
    default: This is not an error message; everything may continue to work properly,
    default: in which case you may ignore this message.
==> default: Configuring and enabling network interfaces...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
==> default: Rsyncing folder: /home/takashi/vagrant/ansible_test/ => /vagrant
==> default: Running provisioner: ansible...
    default: Running ansible-playbook...

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [default]

TASK [ensure ntpd is at the latest version] ************************************
fatal: [default]: FAILED! => {"changed": true, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror, security\n"]}
    to retry, use: --limit @/home/takashi/vagrant/ansible_test/playbook.retry

PLAY RECAP *********************************************************************
default                    : ok=1    changed=0    unreachable=0    failed=1   

Ansible failed to complete successfully. Any error output should be
visible above. Please fix these errors and try again.
takashi@takashi-desktop:~/vagrant/ansible_test$ 

残念ながらエラー。だけど、次に繋がるぞ。

今日は以上です。

人はこうしてスプリットキーボードに行き着く

この記事について

この記事は

adventar.org

上記アドベントカレンダーの3日目の記事です。前日の記事は、 @mycor さんの この沼の素晴らしさ(仮) でした。

今回の記事では私がどのようにしていわゆるJIS配列フルキーボードからスプリットキーボードに至ったかまでを紹介させて頂きたいと思います。そういう意味ではタイトルは大げさでしたね。

既存のフルキーボードに違和感を持ち始める

私も最初はPC付属のキーボードを使っていました。いわゆるJIS配列のフルキーボードですね。最初はキーボードの配列、キー配置を覚えるのが大変でタイピングオブザデッドをしたものでした。 そこそこブラインドタッチが出来るようになり、不自由をあまり感じずに文書を作ったりWebサイトを作ったりしていました。どちらかというと、Windows でマウス操作主流の時だったと思います。

f:id:takashikono:20181203045148j:plain
フルキーボードの例

私にとっての転機は Linux を触り始めた時でした。ターミナル(黒い画面)でマウスを使わずコマンドで操作しだしたのですが、最初からほぼコマンドで操作できたわけではありませんでした。当然のように検索したり、Teraterm でしたのでWindowの位置をずらしたりするのにマウスとキーボードを頻繁に切り替えて使っていました。その頃ふと思ったのです。

いらないキーがあるんじゃない?

  • テンキー使っていないからいらない
  • Home, End とかのエリアいらない(Deleteは使う)
  • 変なファンクションキーいらない(スリープボタンとかついていて、間違って押してしまった日には涙目になるわけです

体にも悪いよね?

  • モニターと体とキーボードの中心を一直線につなげると、左手は真っ直ぐ前に、右手はものすごーく左寄りに持って行かなくてはいけなくて単純にきつい
  • ホームポジションの中心をモニターと体を一直線につなげた線の上に持ってくると、方向キーやらテンキーのエリアを越えてマウスに手を持っていく必要があり右手が疲れる
  • ホームポジションからマウスまで遠すぎる

絵にすると下図の様な感じかと思います。フルキーボードで文字を入力しようとした時の右肩と右腕に注目しています。矢印の方向に特に負荷がかかっていると思います。

f:id:takashikono:20181202174540j:plain
フルキーボードの場合のモニタ、人の位置関係

こうしてフルキーボードに違和感を持った私は、数字キーを含むブラインドタッチが出来るようになったことも有り、テンキーレスのキーボードを探し求めるようになりました。

テンキーレスのキーボードを使いはじめる

初めて使ったテンキーレスのキーボードは多分 Thinkpad のキーボードだったと思います。デスクトップのキーボードと違い両手がスッと中央によっていくのでかなり自然だなと感じ、マウスへの右手の移動量も少なくて「よいよい」なんて思っていました。それからというもの、キーボードを買うときはテンキーレスで且つ方向キーのエリア?も無いものを購入するようになりました。

f:id:takashikono:20181203045148j:plain
フルキーボードの例。メインで入力するのはEnterのキーがあるところまでで、私は方向キーがあるところから右側が全ていらなかった。

今でも持っているし結構優秀だった?なと思うのが以下のキーボードです。これにはおせわになりました。方向キー以外は個人的に良かったと思っています。(メカニカルじゃないですが)

f:id:takashikono:20181203050151j:plain
テンキーレスキーボードの例。HHKB 等を購入する前の物です。そこそこ汚れていてすみません。

参考までに Amazon では以下のページに有りますが既に売っていません。

https://www.amazon.co.jp/dp/B00337XP0O

こうしてテンキーレス生活を始めたのですが、あるとき問題が起きます。

  • 肩幅が広い私のような人にはキーボード狭くて窮屈だな
  • 正直肩こりがひどい。
  • なんなら背中から腰にかけても痛みがすごい

これはテンキーレスのような小さいキーボードを使っているため、両手を中央に寄せた時に胸が窮屈になること、また猫背になりやすいことから痛みが起きているのだろうと思いました。

絵にすると下図の様な感じかと思います。矢印の方向に負荷がかかっていたと思います。背中の丸さは表現できていません。

f:id:takashikono:20181202174625j:plain
テンキーレスキーボードの場合のモニタ、人の位置関係

これらの問題を早急に解決せねばならないと思いました。そんな時目についたのが、 ErgoDox でした。とはいえ高いし輸入だし手を出したログもないし、ハードルが高かったので当時は諦めました。

スプリットキーボードに手を出す

スプリットキーボードを探していた私は、Mistel の BAROCCO MD600 が発売されるのを知り、アキバに走りました。

www.archisite.co.jp

これ、個人的に最高だったのです。今までのフルキーボードの時の問題点、テンキーレスのキーボードの問題点を一気に解決するものでした。

絵にすると以下のようになると思います。

f:id:takashikono:20181202175324j:plain
スプリットキーボードの場合のモニタ、人の位置関係

体からキーボードに伸びる腕は自然にまっすぐに伸び、胸が開くため姿勢が正しくなります。肩こり腰痛が激減しました。
私はこの体験から、今後はスプリットキーボード一本に絞り込んでいこうと思うようになりました。

このように、人が正しい姿勢で正しく打鍵するためには現在のフルキーボードは不適切かと私は考えています。結果として上記の様な流れでスプリットキーボードに行き着くのだと思います。むしろスプリットを標準にして欲しいくらいです。(笑)

そしてその先へ

BAROCCO MD600 を触っている時に1つのことに気づいてしまいました。あるとき、私は私がキーボードで文章を入力したり、プログラムを書いたりするときにスペースバーを左手の親指でしか押さないことに。また、このキーボードはスペースキーが2つに分割されていることに気づきました。そこで、右手で一番良く使う Enter を右側のスペースキーに割り当てました。このキーボードはキー配置がカスタマイズ出来るものだったのです。

f:id:takashikono:20181203052119j:plain
MD600 の写真。スペースキーが2つにわかれていることがわかります。右側のキーを Enter に割り当てました。

直ぐに気が付きました。親指に良く使うキーをアサインすることで明らかに小指の負荷が下がり、親指で軽く打つことで快適になることに。この体験をしたため、今後はただのスプリットキーボードだけではなく、親指に大事なキーをアサインできる、キー配列をカスタマイズ出来るキーボードを使うようにしよう。と思うようになりました。

しかし、このような体験をしていると、もっと親指にキーを集めたくなります。その時に私の目に飛び込んできたのが、 Helix や Ergo42 と言った、自作キーボードでした。自作キーボードはキー配列だけではなくキースイッチを何処につけるかまで自分の好きなように設計できるものでした。当然ですよね。基板から設計しているからいかようにも物理配置をカスタマイズ出来るのです。もう、感動を覚え、自作の道に進もう。私の考える最強のキーボードを作ろうと決めたのでした。

Helix

scrapbox.io

Ergo42

scrapbox.io

最後に

必ずしも全員がスプリットキーボードにたどり着いたり、基板を設計するわけではないと思いますが、1つの答えかなと思っています。
このアドベントカレンダーを読んだり、 Twitter でスプリットキーボードを見つけることが出来たなら、次に買うキーボードの選択肢の1つに入っていると嬉しいです。(笑)

明日は、 @mizping さんの 「沼の深い方へ一歩踏み出した話」 です。お楽しみに。

アドベントカレンダー
その1

adventar.org

その2

adventar.org

その3

adventar.org

では、この後もアドベントカレンダーをお楽しみに。

この記事は、 BAROCCO MD600 で書きました。

Thank you.

Flask の基本的な使いかたについてメモ

Flask メモ

Flask を実際触ってみたメモを残す

Python の情報

Python 自体の情報について Python 3.6.4 を今回使った。

Install Flask

インストールメモ

$ pip install Flask
$ pip show Flask

上記コマンドで Flask をインストールして、インストールされたことを確認する。
2018-09-24 現在 Flask のバージョンは、 1.0.2 が最新であった。

最新にするには、下記コマンドで出来る。

$ pip install -U Flask

Make Script

スクリプトを実際につくってみる

アプリケーション用のディレクトリを作る

$ mkdir flask
$ cd flask

動作確認も含めた最初のスクリプトを作る
ファイル名: app.py

# coding: utf-8

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello_world():
    return 'hello, world!'

ちなみに、 app = Flask(__name__) について。
__name__ はアプリケーションを直接実行したばいい、 __main__ が入る。
インポートした Flask に対し、引数として __main__ をつけて app というインスタンスを作成する。
というのがこの一文のはず。 その後は、作成した app というインスタンスに対して動作を定義していく。

ちなみに、モジュールとして他のアプリケーションから呼ばれて動作するか、スクリプトとして実行されているかで、 __name__ の中身が変わります。
(だから何を気をつければよいかはパッと出てこない。ごめんなさい)

実行する

$ export FLASK_APP=app.py
$ flask run
 * Serving Flask app "app.py"
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Warning 出ているけど、仮想環境を作っていないからだと思われる。(個人的には仮想環境的を作るより、 Vagrant や Cloud 環境を使って試したほうが良いと考えている)

動作確認
ブラウザを開いて、 http://localhost:5000/ にリクエストを投げる。

hello, world!

と出力されたらOK
もしくは、

$ curl -sSv http://localhost:5000/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 13
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:02:55 GMT
< 
* Closing connection 0
hello, world!$ 

のように、コマンドで確認しても良い。
上記の場合、 localhost:5000 に対して GET リクエストを出しているのがわかる。 Responce で HTTP/1.0 200 OK となっていることから、リクエストが正常にレスポンスされていることがわかる。

Closing connection 0 のあとで、

hello, world!

が出力されていることがわかる。 ! の後ろに改行コードを入れていないことから、レスポンス文字列は改行されず、プロンプトの $ が出力されていることがわかる。

Deep dive a bit

ちょっとだけ深く潜ってみる。

app.py

# coding: utf-8

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/")
def hello_world():
    return 'hello, world!'

@app.route("/about")
def about():
    return "<h1>About: </h1>"

@app.route("/hello1/<whom>")
def hello1(whom):
    return "Hello {}\n".format(whom)

@app.route("/hello2/<whom>")
def hello2(whom):
    return render_template("hello.html", whom=whom)

@app.route("/100_plus/<int:n>")
def adder(n):
    return "100+{}={}".format(n, 100+n)

上記スクリプトでは、テンプレートから参照できるようにもしている。
テンプレートもこのタイミングで作ってみる。

$ mkdir templates

テンプレートの html ファイルを作成する。
hello.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Hello</title>
</head>
<body>
  <h1>Using Jinja2 Template engine</h1>
  <h2>Hello {{whom}}<h2>
</body>
</html>

上記テンプレートは、 def hello2(whom): 内で呼ばれる。

動作確認

$ curl -sSv http://localhost:5000/about
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /about HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 16
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:17:30 GMT
< 
* Closing connection 0
<h1>About: </h1>$

ヘッダとして About: 文字列が表示されている

$ curl -sSv http://localhost:5000/hello1/someone
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /hello1/someone HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 14
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:19:13 GMT
< 
Hello someone
* Closing connection 0
$

Hello の後に、 URL の文字列で渡した、 someone が反映されているのがわかる。

$ curl -sSv http://localhost:5000/hello2/somebody
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /hello2/somebody HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 183
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:21:15 GMT
< 
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Hello</title>
</head>
<body>
  <h1>Using Jinja2 Template engine</h1>
  <h2>Hello somebody<h2>
</body>
* Closing connection 0
</html>$

テンプレートを使ってレンダリングしていることがわかる。また、 {{whom}} の内容が、 URL で渡された文字列になっていることがわかる。

  1. URL の文字列を @app.route("/hello2/<whom>)<whom> で受け取る
  2. def hello2(whom):whom に値を渡す
  3. render_template("hello.html", whom=whom) で、テンプレート hello.html を利用し、且つテンプレート内の whom 変数に対し、 whom の変数に入っている値を渡してあげた上で、レンダリングしていることがわかる
$ curl -sSv http://localhost:5000/100_plus/100
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /100_plus/100 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 11
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:28:53 GMT
< 
* Closing connection 0
100+100=200$

100100 という文字列結合ではなく、 200 という計算結果になっていることから、与えられた値が int 型として与えられていることがわかる。

では、文字列を渡してみる。

$ curl -sSv http://localhost:5000/100_plus/foo
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /100_plus/foo HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 404 NOT FOUND
< Content-Type: text/html
< Content-Length: 233
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:31:37 GMT
< 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again.</p>
* Closing connection 0

404 エラーになった。
int 型になりうる値以外は例外を出すという処理に対して期待どおりに動くことがわかった。
バリデーションに使えそう。

ちなみにこれらのひととおりの app.py 側のログは以下のように出力された。

127.0.0.1 - - [24/Sep/2018 17:17:03] "GET /about HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:17:17] "GET /about HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:17:30] "GET /about HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:19:13] "GET /hello1/someone HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:21:15] "GET /hello2/somebody HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:28:53] "GET /100_plus/100 HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:31:37] "GET /100_plus/foo HTTP/1.1" 404 -

出来れば日時表記は ISO 規格に合わせたいところである。

以上、やってみたログでした。

Ansible のCommand Modules を触ってみた

目的

Ansible にあるたくさんのモジュールの中からよく使われそうなモジュールを使ってみて知見を得る

環境

Microsoft Azure Cloud Shell
Ubuntu 16.04.4 LTS

Ansible のバージョン

kono@Azure:~/ansible_pj$ ansible --version
ansible 2.5.2
  config file = None
  configured module search path = [u'/home/kono/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/ansible/local/lib/python2.7/site-packages/ansible
  executable location = /opt/ansible/bin/ansible
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
kono@Azure:~/ansible_pj$

予めしておくこと

Ansible による構成対象の VMssh ログイン出来るように Cloud Shell の公開鍵を登録することです。おそらく VM を作るときに聞いてくると思いますが。。。すみません確認して追記します。

Commands modules

Commands modules — Ansible Documentation

まずは Commands modules から見ていく

command

command_module.yaml を作成

$ cat command_modules.yaml
# Command modules test
- hosts: any
  user: takashi
  tasks:
    - name: return distribution
      command: cat /etc/lsb-release
    - name: return df
      command: df -h
    - name: return memory free
      command: free
    - name: return inodes info
      command: df -i
    - name: ip address
      command: ip a
    - name: list of listened ports
      command: netstat -nlt

実行してみる

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts command_modules.yaml

PLAY [any] **********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [40.74.70.39]

TASK [return distribution] ******************************************************************************************************************************************************************************************************************
changed: [40.74.70.39]

TASK [return df] ****************************************************************************************************************************************************************************************************************************
changed: [40.74.70.39]

TASK [return memory free] *******************************************************************************************************************************************************************************************************************
changed: [40.74.70.39]

TASK [return inodes info] *******************************************************************************************************************************************************************************************************************
changed: [40.74.70.39]

TASK [ip address] ***************************************************************************************************************************************************************************************************************************
changed: [40.74.70.39]

TASK [list of listened ports] ***************************************************************************************************************************************************************************************************************
changed: [40.74.70.39]

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.70.39                : ok=7    changed=6    unreachable=0    failed=0

kono@Azure:~/ansible_pj$

実行されているような気がしますね。では、デバッグメッセージのオプションを使ってみましょう

$ ansible-playbook -i hosts command_modules.yaml -vvv
上記のように、 -vvv オプションをつけることで詳細なメッセージを確認できます。 v の数が何個かで出力レベルがわかるので、自分の欲しい情報が何か整理してから使っても良いかも。基本とりあえず -vvv で。という感じなんだと妄想してみる。

ちなみに、ソースコードを確認してみる

https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/commands/command.py

https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/commands/command.py#L137-L145

このあたりで色々定義しているのがわかる

expect

https://docs.ansible.com/ansible/latest/modules/expect_module.html#expect-module

先に書きますが、私の環境では上手く行きませんでした。

以下は参考までにどうぞ。
まず、適当に playbook を書いてみた。
expect.yaml

kono@Azure:~/ansible_pj$ cat expect.yaml
- hosts: any
  user: takashi
  tasks:
    - name: expect module test
      expect:
        command: /usr/bin/python3 /home/takashi/input_test.py
        responses:
          'please input something: ' : 'This is expect module test'
kono@Azure:~/ansible_pj$

expect というくらいなので、ターミナルに何かコメントがあって、入力待ち状態になる時にこれを使うはず。
というわけで、ssh 先の VM に python3 で適当にコードを書いてみた。

takashi@test1:~$ cat input_test.py 
#!/usr/bin/python3
# coding: utf-8

tmp = input("please input something: ")
print(tmp)

takashi@test1:~$ 

で、実行

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts expect.yaml

PLAY [any] **********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [40.74.95.98]

TASK [expect module test] *******************************************************************************************************************************************************************************************************************
fatal: [40.74.95.98]: FAILED! => {"changed": false, "msg": "The pexpect python module is required"}
        to retry, use: --limit @/home/kono/ansible_pj/expect.retry

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.95.98                : ok=1    changed=0    unreachable=0    failed=1

kono@Azure:~/ansible_pj$ 

失敗した。
メッセージには、 The expected python module is required とある。
このモジュールを使うためには、 pexpect が必須なんですね。
公式のドキュメントにも書いてありました。

expect - Executes a command and responds to prompts. — Ansible Documentation

ということで、 pexpect が入っているか確認します。

kono@Azure:~/ansible_pj$ ansible --version
ansible 2.5.2
...
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
kono@Azure:~/ansible_pj$ pip2
pip2    pip2.7
kono@Azure:~/ansible_pj$ pip2.7 list | grep pexpect
kono@Azure:~/ansible_pj$

ない!

pexpect のインストールですね。 やってみましょう。

kono@Azure:~/ansible_pj$ pip2.7 install pexpect
Collecting pexpect
  Using cached https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl
Collecting ptyprocess>=0.5 (from pexpect)
  Using cached https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess-0.6.0-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect
Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/ptyprocess-0.6.0.dist-info'
Consider using the `--user` option or check the permissions.

kono@Azure:~/ansible_pj$ 

??? 何かおかしい。 Permissiono denied ですと!?
--user オプションを使ってみましょう。書いてあるとおり。

kono@Azure:~/ansible_pj$ pip2.7 install pexpect --user
Collecting pexpect
  Using cached https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl
Collecting ptyprocess>=0.5 (from pexpect)
  Using cached https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess-0.6.0-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect
Successfully installed pexpect-4.6.0 ptyprocess-0.6.0
kono@Azure:~/ansible_pj$
kono@Azure:~/ansible_pj$ pip2.7 list | grep pexpect
pexpect    4.6.0
kono@Azure:~/ansible_pj$

あっさり解決。。。ちゃんと入った。
ということで、リトライします。

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts expect.yaml

PLAY [any] **********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [40.74.95.98]

TASK [expect module test] *******************************************************************************************************************************************************************************************************************
fatal: [40.74.95.98]: FAILED! => {"changed": false, "msg": "The pexpect python module is required"}
        to retry, use: --limit @/home/kono/ansible_pj/expect.retry

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.95.98                : ok=1    changed=0    unreachable=0    failed=1

kono@Azure:~/ansible_pj$

??? 同じエラーが出てしましました。 モジュールが必要だったのは、もしかすると、VM の方だったのかな?
本当は pip のインストール及び、 pexpect のインストールも Ansible で実施したかったけど、本質じゃないので普通に apt-get install python3-pip で実施することにした。

takashi@test1:~$ pip3 -V
pip 1.5.4 from /usr/lib/python3/dist-packages (python 3.4)
takashi@test1:~$

しかし、 pip3 がインストールできたので、 pexpext のインストールを Ansible でやってみる。

kono@Azure:~/ansible_pj$ cat python_modules.yaml
# Command modules test
- hosts: any
  user: takashi
  become: true
  tasks:
    - name: install pexpect
      command: /usr/bin/pip3 install pexpect
kono@Azure:~/ansible_pj$

プレイブックが出来たので、実行してみる

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts python_modules.yaml

PLAY [any] **********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [40.74.95.98]

TASK [install pexpect] **********************************************************************************************************************************************************************************************************************
changed: [40.74.95.98]

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.95.98                : ok=2    changed=1    unreachable=0    failed=0

kono@Azure:~/ansible_pj$

お。出来てそう。VM で確認してみる。

takashi@test1:~$ pip3 list | grep pexpect
pexpect (4.6.0)
takashi@test1:~$ 

入りました。
ようやく準備が出来たので、 expect をもう一度試してみる。

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts expect.yaml

PLAY [any] **********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [40.74.95.98]

TASK [expect module test] *******************************************************************************************************************************************************************************************************************
fatal: [40.74.95.98]: FAILED! => {"changed": false, "msg": "The pexpect python module is required"}
        to retry, use: --limit @/home/kono/ansible_pj/expect.retry

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.95.98                : ok=1    changed=0    unreachable=0    failed=1

kono@Azure:~/ansible_pj$

それでもダメだった。。。んー、何がいけなかったのか。。。
テスト
AzureCloud Shell 環境での動作について

https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/commands/expect.py#L95-L99

ここでやっていることと同じことをしてみる。

python2.7 の場合の import pexpect のテスト

kono@Azure:~/ansible_pj$ python2.7
Python 2.7.12 (default, Dec  4 2017, 14:50:18)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> try:
...     import pexpect
...     print(True)
... except ImportError:
...     print(False)
...
True
>>>

OK 問題ない。

kono@Azure:~/ansible_pj$ python
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try:
...     import pexpect
...     print(True)
... except ImportError:
...     print(False)
...
False
>>>

pexpect がインポート出来ていない。
もう一度、確認。

kono@Azure:~/ansible_pj$ ansible --version
ansible 2.5.2
  config file = None
  configured module search path = [u'/home/kono/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/ansible/local/lib/python2.7/site-packages/ansible
  executable location = /opt/ansible/bin/ansible
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
kono@Azure:~/ansible_pj$

executable location に実行ファイルがありそうだ。

kono@Azure:~/ansible_pj$ ls -l /opt/ansible/bin/ | grep python
...
lrwxrwxrwx  1 root root       7 Jun 15 19:04 python -> python2
-rwxr-xr-x 11 root root 3492656 Jun 15 19:04 python2
lrwxrwxrwx  1 root root       7 Jun 15 19:04 python2.7 -> python2
...
kono@Azure:~/ansible_pj$

おお。 python2 に全て集約されている!!
pip 系はどうだろう?

kono@Azure:~/ansible_pj$ ls -l /opt/ansible/bin/ | grep pip
-rwxr-xr-x 11 root root     224 Jun 15 19:04 pip
-rwxr-xr-x 11 root root     224 Jun 15 19:04 pip2
-rwxr-xr-x 11 root root     224 Jun 15 19:04 pip2.7
kono@Azure:~/ansible_pj$

pip 系はそのままなんだろうか。。。それぞれのバージョンを見てみよう。

kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip -V
pip 10.0.1 from /opt/ansible/local/lib/python2.7/site-packages/pip (python 2.7)
kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip2 -V
pip 10.0.1 from /opt/ansible/local/lib/python2.7/site-packages/pip (python 2.7)
kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip2.7 -V
pip 10.0.1 from /opt/ansible/local/lib/python2.7/site-packages/pip (python 2.7)
kono@Azure:~/ansible_pj$

全部同じ出力である。同じ所を参照しているので問題なさそう。
pip に関して問題ないことがわかったので、 python2import pexpect テストを実施してみよう

kono@Azure:~/ansible_pj$ /opt/ansible/bin/python2
Python 2.7.12 (default, Dec  4 2017, 14:50:18)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> try:
...   import pexpect
...   print True
... except ImportError:
...   print False
...
False
>>>

なんだと。。。 python2 の起動 path が違うから別物を使っていたということか???

kono@Azure:~/ansible_pj$ whereis python2.7
python2: /usr/bin/python2.7 /usr/bin/python2 /usr/bin/python2.7-config /usr/lib/python2.7 /etc/python2.7 /usr/local/lib/python2.7 /usr/include/python2.7 /opt/ansible/bin/python2.7 /opt/ansible/bin/python2 /usr/share/man/man1/python2.1.gz
kono@Azure:~/ansible_pj$

なるほど。PATH は通っているけれど、$ python2.7 で呼び出した時は、 /usr/bin/python2.7 が呼び出され、$ /opt/ansible/bin/python2.7 では、そのとおりの pathPython2.7 が起動したわけですね。
結果、同じように import pexpect をしているけど、挙動が違うと。

では、 Ansible が参照する pippexpect をインストールしよう。

kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip install pexpect --user virtualenv
Can not perform a '--user' install. User site-packages are not visible in this virtualenv.
kono@Azure:~/ansible_pj$

ダメなようですね。
ちなみに、本当に参照している先が違うのでしょうか。

kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip show wheel
...
Location: /opt/ansible/lib/python2.7/site-packages
...
kono@Azure:~/ansible_pj$

これと

kono@Azure:~/ansible_pj$ pip2.7 show wheel
...
Location: /usr/lib/python2.7/dist-packages
...
kono@Azure:~/ansible_pj$

同じパッケージで目に入ったのが wheel なので指定させて頂いた。
まるで使う場所が違いますね。

別のアプローチを考えよう。
Ansible が利用するデフォルトの Python を指定するとか。

kono@Azure:~/ansible_pj$ pip2.7 list | wc -l
9
kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip list | wc -l
73
kono@Azure:~/ansible_pj$

この結果を見るとアプローチを変えるのは厳しそう。
/opt/ansible/lib/python2.7/site-packages 内に無理やり入れる方法を考えてみよう。

Azure にて、 /opt/ansible/lib/python2.7/site-package に何かを入れられるとは思いがたい。。。

なにせ、Azure Cloud Shell の制限事項として、 sudo がつかえないのである。

どこか詰んだ感じがしますね。
Ansible が使うパッケージの場所

kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip show wheel | grep Location
Location: /opt/ansible/lib/python2.7/site-packages
kono@Azure:~/ansible_pj$

ロケーションのディレクトリのパーミッション及び、所有者:グループ

kono@Azure:~/ansible_pj$ ls -l /opt/ansible/lib/python2.7/ | grep site-packages
-rw-r--r--  11 root root     0 Jun 15 19:04 no-global-site-packages.txt
drwxr-xr-x 120 root root  4096 Jun 18 20:26 site-packages
kono@Azure:~/ansible_pj$

root:root でないとインストールできないことがわかります。

試しに、 /opt/ansible/bin/pip install pexpect を実行

kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip install pexpect
Collecting pexpect
  Using cached https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl
Collecting ptyprocess>=0.5 (from pexpect)
  Using cached https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess-0.6.0-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect
Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/opt/ansible/lib/python2.7/site-packages/ptyprocess-0.6.0.dist-info'
Consider using the `--user` option or check the permissions.

kono@Azure:~/ansible_pj$

--user をつけると

kono@Azure:~/ansible_pj$ /opt/ansible/bin/pip install pexpect --user
Can not perform a '--user' install. User site-packages are not visible in this virtualenv.
kono@Azure:~/ansible_pj$

Permission denied 権限がない。 sudo をつけると、、、

kono@Azure:~/ansible_pj$ sudo /opt/ansible/bin/pip install pexpect --user
bash: sudo: command not found
kono@Azure:~/ansible_pj$

コマンドがない。 orz

ということで、 expect モジュールですが、これは上手く出来た時に追記したいと思います。
非常に残念ですが、しょうが無いですね。

raw

raw - Executes a low-down and dirty SSH command — Ansible Documentation

純粋に ssh コマンドを実行して何かを手で実施する時と同じことを実施してくれるモジュール。

playbook

kono@Azure:~/ansible_pj$ cat raw.yaml
# Command modules test
- hosts: any
  user: takashi
  #become: true
  tasks:
    - name: tail syslog
      raw: tail /var/log/syslog
kono@Azure:~/ansible_pj$

ansible 実行

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts raw.yaml

PLAY [any] **********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [40.74.67.49]

TASK [tail syslog] **************************************************************************************************************************************************************************************************************************
changed: [40.74.67.49]

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.67.49                : ok=2    changed=1    unreachable=0    failed=0

kono@Azure:~/ansible_pj$

これではよくわからないので、 -vvv オプションをつけてみましょう。

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts raw.yaml -vvv
...json
TASK [tail syslog] **************************************************************************************************************************************************************************************************************************
task path: /home/kono/ansible_pj/raw.yaml:6
<40.74.67.49> ESTABLISH SSH CONNECTION FOR USER: takashi
<40.74.67.49> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o 'IdentityFile="/home/kono/.ssh/id_rsa"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=takashi -o ConnectTimeout=10 -o ControlPath=/home/kono/.ansible/cp/8aa58877c0 -tt 40.74.67.49 'tail /var/log/syslog'
<40.74.67.49> (0, "Jun 24 01:36:11 test1 kernel: [   62.830553] hv_balloon: Data Size is 8\r\nJun 24 02:05:11 test1 ansible-setup: Invoked with filter=* gather_subset=['all'] fact_path=/etc/ansible/facts.d gather_timeout=10\r\nJun 24 02:05:14 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/uname -a removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:16 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/df -h removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:19 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/df -i removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:21 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/usr/bin/free removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:24 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/sbin/ip a removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:26 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/netstat -nlt removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:29 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=which python3 removes=None creates=None chdir=None stdin=None\r\nJun 24 02:12:17 test1 ansible-setup: Invoked with filter=* gather_subset=['all'] fact_path=/etc/ansible/facts.d gather_timeout=10\r\n", 'Shared connection to 40.74.67.49 closed.\r\n')
changed: [40.74.67.49] => {
    "changed": true,
    "rc": 0,
    "stderr": "Shared connection to 40.74.67.49 closed.\r\n",
    "stdout": "Jun 24 01:36:11 test1 kernel: [   62.830553] hv_balloon: Data Size is 8\r\nJun 24 02:05:11 test1 ansible-setup: Invoked with filter=* gather_subset=['all'] fact_path=/etc/ansible/facts.d gather_timeout=10\r\nJun 24 02:05:14 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/uname -a removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:16 test1 ansible-command: Invoked with warn=True executable=None_uses_shell=False _raw_params=/bin/df -h removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:19 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/df -i removes=None creates=Nonechdir=None stdin=None\r\nJun 24 02:05:21 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/usr/bin/free removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:24 test1 ansible-command:Invoked with warn=True executable=None _uses_shell=False _raw_params=/sbin/ip a removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:26 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/netstat -nlt removes=None creates=None chdir=None stdin=None\r\nJun 24 02:05:29 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=which python3 removes=None creates=None chdir=None stdin=None\r\nJun 24 02:12:17 test1 ansible-setup: Invoked with filter=* gather_subset=['all'] fact_path=/etc/ansible/facts.d gather_timeout=10\r\n",
    "stdout_lines": [
        "Jun 24 01:36:11 test1 kernel: [   62.830553] hv_balloon: Data Size is 8",
        "Jun 24 02:05:11 test1 ansible-setup: Invoked with filter=* gather_subset=['all'] fact_path=/etc/ansible/facts.d gather_timeout=10",
        "Jun 24 02:05:14 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/uname -a removes=None creates=None chdir=None stdin=None",
        "Jun 24 02:05:16 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/df -h removes=None creates=None chdir=None stdin=None",
        "Jun 24 02:05:19 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/df -i removes=None creates=None chdir=None stdin=None",
        "Jun 24 02:05:21 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/usr/bin/free removes=None creates=None chdir=None stdin=None",
        "Jun 24 02:05:24 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/sbin/ip a removes=None creates=None chdir=None stdin=None",
        "Jun 24 02:05:26 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/netstat -nlt removes=None creates=None chdir=None stdin=None",
        "Jun 24 02:05:29 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=which python3 removes=None creates=None chdir=None stdin=None",
        "Jun 24 02:12:17 test1 ansible-setup: Invoked with filter=* gather_subset=['all'] fact_path=/etc/ansible/facts.d gather_timeout=10"
    ]
}
META: ran handlers
META: ran handlers

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.67.49                : ok=2    changed=1    unreachable=0    failed=0

kono@Azure:~/ansible_pj$

コマンドでは以下と同じことを行っています。

kono@Azure:~/ansible_pj$ ssh takashi@40.74.67.49 tail /var/log/syslog
Jun 24 02:05:14 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/uname -a removes=None creates=None chdir=None stdin=None
Jun 24 02:05:16 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/df -h removes=None creates=None chdir=None stdin=None
Jun 24 02:05:19 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/df -i removes=None creates=None chdir=None stdin=None
Jun 24 02:05:21 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/usr/bin/free removes=None creates=None chdir=None stdin=None
Jun 24 02:05:24 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/sbin/ip a removes=None creates=None chdir=None stdin=None
Jun 24 02:05:26 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=/bin/netstat -nlt removes=None creates=None chdir=None stdin=None
Jun 24 02:05:29 test1 ansible-command: Invoked with warn=True executable=None _uses_shell=False _raw_params=which python3 removes=None creates=None chdir=None stdin=None
Jun 24 02:12:17 test1 ansible-setup: Invoked with filter=* gather_subset=['all'] fact_path=/etc/ansible/facts.d gather_timeout=10
Jun 24 02:13:10 test1 ansible-setup: Invoked with filter=* gather_subset=['all'] fact_path=/etc/ansible/facts.d gather_timeout=10
Jun 24 02:17:01 test1 CRON[3938]: (root) CMD (   cd / && run-parts --report /etc/cron.hourly)
kono@Azure:~/ansible_pj$

ソースコードは以下。ドキュメントしか無いことがわかります。

ansible/raw.py at devel · ansible/ansible · GitHub

ssh したあとのコマンドをしっかり記述することで、何でも出来そうですね。その反面べき等性は自分たちで実装する必要はあるのだと思います。

script

script - Runs a local script on a remote node after transferring it — Ansible Documentation

気になった特徴は以下の内容です。

This module does not require python on the remote system, much like the raw module.

つまり、 python の入っていないようなネットワークデバイスでもスクリプトが使えるということなんだと思います。

とはいえ、サンプルに Shell Script しか書かれていないので、心配になったため、Pythonスクリプトも準備する。

こんな感じで playbook を書いてみました。

kono@Azure:~/ansible_pj$ cat script.yaml
# Command modules test
- hosts: any
  user: takashi
  #become: true
  tasks:
    - name: execute local shell script
      script: ./scripts/get_cwd.sh
      register: result
    - debug:
        var: result
    - name: execute local python script
      script: ./scripts/get_cwd.py
      register: result
    - debug:
        var: result
kono@Azure:~/ansible_pj$

それぞれのスクリプトは以下の通り。

kono@Azure:~/ansible_pj$ cat ./scripts/get_cwd.sh
echo `pwd`
kono@Azure:~/ansible_pj$ cat ./scripts/get_cwd.py
#!/usr/bin/python3
# coding: utf-8
import os
print(os.getcwd())


kono@Azure:~/ansible_pj$

動かしてみましょう。

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts script.yaml

PLAY [any] **********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [40.74.67.49]

TASK [execute local shell script] ***********************************************************************************************************************************************************************************************************
changed: [40.74.67.49]

TASK [debug] ********************************************************************************************************************************************************************************************************************************
ok: [40.74.67.49] => {
    "result": {
        "changed": true,
        "failed": false,
        "rc": 0,
        "stderr": "Shared connection to 40.74.67.49 closed.\r\n",
        "stdout": "/home/takashi\r\n",
        "stdout_lines": [
            "/home/takashi"
        ]
    }
}

TASK [execute local python script] **********************************************************************************************************************************************************************************************************
changed: [40.74.67.49]

TASK [debug] ********************************************************************************************************************************************************************************************************************************
ok: [40.74.67.49] => {
    "result": {
        "changed": true,
        "failed": false,
        "rc": 0,
        "stderr": "Shared connection to 40.74.67.49 closed.\r\n",
        "stdout": "/home/takashi\r\n",
        "stdout_lines": [
            "/home/takashi"
        ]
    }
}

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.67.49                : ok=5    changed=2    unreachable=0    failed=0

kono@Azure:~/ansible_pj$

どちらでも実行できましたね。
しかも、ローカルホストで作成しておいた Script ファイルをリモートで実行してくれている。
raw に似ていますね。

どうやって実現しているのか謎ですが、以外に便利な反面、冪等性の担保が難しそうです。
show run 取るくらいならこんな感じでいいかもしれませんけどね。

ソースコードは以下のリンクです。こちらも raw 同様、何もしていませんね。。。

ansible/script.py at devel · ansible/ansible · GitHub

shell

shell - Execute commands in nodes. — Ansible Documentation

次は、 shell モジュールですね。
ドキュメントの中にもあるように、 command モジュールライクらしいです。

The shell module takes the command name followed by a list of space-delimited arguments. It is almost exactly like the command module but runs the command through a shell (/bin/sh) on the remote node.

このドキュメントだけではちょっと良くわからなかったのでググってみた結果

shell モジュールは、 &, |, > 等のシェルの機能が使えるが、 command モジュールでは使えないというのが最も大きな差であるようだ。

ということで、 Playbook を書いてみた。

kono@Azure:~/ansible_pj$ cat shell.yaml
# Command modules test
- hosts: any
  user: takashi
  #become: true
  tasks:
    - name: Execute the command in remote shell.
      shell: ls /var/log/ | wc -l
      register: result
    - debug:
        var: result
kono@Azure:~/ansible_pj$

パイプでつなげているが、使えるだろうか。

kono@Azure:~/ansible_pj$ ansible-playbook -i hosts shell.yaml

PLAY [any] **********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [40.74.67.49]

TASK [Execute the command in remote shell.] *************************************************************************************************************************************************************************************************
changed: [40.74.67.49]

TASK [debug] ********************************************************************************************************************************************************************************************************************************
ok: [40.74.67.49] => {
    "result": {
        "changed": true,
        "cmd": "ls /var/log/ | wc -l",
        "delta": "0:00:00.004307",
        "end": "2018-06-24 03:57:31.229757",
        "failed": false,
        "rc": 0,
        "start": "2018-06-24 03:57:31.225450",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "29",
        "stdout_lines": [
            "29"
        ]
    }
}

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
40.74.67.49                : ok=3    changed=1    unreachable=0    failed=0

kono@Azure:~/ansible_pj$ 

きちんと出来ましたね。さすがです。 raw モジュールとも似ているのでしょうかね。。。

ansible/shell.py at devel · ansible/ansible · GitHub

ソースコードを見ても、ドキュメントとサンプルしか無いような気が。。。

telnet

telnet - Executes a low-down and dirty telnet command — Ansible Documentation

rawssh ではなく telnet で実行するという理解で合っているはず。

1時間くらい ubuntutelnet 出来るように調べてみたけど、あまり進展がなかったので、このモジュールについては今後機会があれば追記するようにします。

最後に

今回はコマンドモジュールにフォーカスして使いかたを自分用の備忘録として書き留めました。
ここまで読んで力になれ無かったとしても。。。ごめんなさい。
というか、未来の自分にごめんなさいをしたい。。。意外と expect を一番試してみたかったんですよね。そのため、結構試行錯誤したのですが、残念でした。

今度時間と環境を作ってリトライしようと思います。

以上です。

jinja2 備忘録

jinja2 の簡単な使いかたについての備忘録

jinja2 について調べてみたのはいいが、使えなくならないために備忘録として残す。

いきなりサンプル

Code
ファイル名: jinja2_sample.py

# coding: utf-8
from jinja2 import Template, Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('sample.tpl')

data = {'name': 'Kuro', 'lang': 'Python'}
disp = template.render(data)
print(disp)

Template file
ファイル名: sample.tpl

My name is {{ name }}. I like {{ lang }}.
{% set text=name %}
{{ text }}
{{ text }}
{{ text }}
{{ text }}
{{ text }}

2つのファイルを作ったので実行してみる

データは辞書にして渡す必要がるようです。
テンプレートファイルに set を入れるとテンプレートファイル内で変数として扱えるようです。便利。

実行結果

$ python3 jinja2_sample.py
My name is Kuro. I like Python.

Kuro
Kuro
Kuro
Kuro
Kuro

今のところ、テンプレートファイルのファイル名をコードに埋め込んでいるので、外だししたほうが良さそう。

まぁ、手順書を作るんならこの辺を使えればだいたいいい気がする。

以上

How to use argparse module on Python3

Learning using argparse module on Python3

Make an argparse.py file

At first, make a file that name is argparse.py file by any text editor.

Open the official document on your browser

Aliases
Japanese

Argparse チュートリアル — Python 3.6.5 ドキュメント

English

Argparse Tutorial — Python 3.6.5 documentation

Make the most simple script

Code is bellow.

# coding: utf-8
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()

Show help.

$ python3 argparse.py -h
usage: argparse.py [-h]

optional arguments:
  -h, --help  show this help message and exit
$

Add positional arguments

Make positional arguments.
Code

# coding: utf-8
import argparse
parser = argparse.ArgumentParser()
# Positional arguments.
parser.add_argument("echo")
args = parser.parse_args()
print(args)

Show help message.

$ python3 argparse.py -h
usage: argparse.py [-h] echo

positional arguments:
  echo

optional arguments:
  -h, --help  show this help message and exit

$

Run the script with foo in positional arguments.

$ python3 argparse.py foo
Namespace(echo='foo')
$

Check what functions in ArgumentParser()

Check in interpreter mode.

$ ipython
Python 3.6.4 (default, Jan  6 2018, 11:51:15) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import argparse

In [2]: parser = argparse.ArgumentParser()

In [3]: dir(parser)
Out[3]: 
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_action_groups',
 '_actions',
 '_add_action',
 '_add_container_actions',
 '_check_conflict',
 '_check_value',
 '_defaults',
 '_get_args',
 '_get_formatter',
 '_get_handler',
 '_get_kwargs',
 '_get_nargs_pattern',
 '_get_option_tuples',
 '_get_optional_actions',
 '_get_optional_kwargs',
 '_get_positional_actions',
 '_get_positional_kwargs',
 '_get_value',
 '_get_values',
 '_handle_conflict_error',
 '_handle_conflict_resolve',
 '_has_negative_number_optionals',
 '_match_argument',
 '_match_arguments_partial',
 '_mutually_exclusive_groups',
 '_negative_number_matcher',
 '_option_string_actions',
 '_optionals',
 '_parse_known_args',
 '_parse_optional',
 '_pop_action_class',
 '_positionals',
 '_print_message',
 '_read_args_from_files',
 '_registries',
 '_registry_get',
 '_remove_action',
 '_subparsers',
 'add_argument',
 'add_argument_group',
 'add_help',
 'add_mutually_exclusive_group',
 'add_subparsers',
 'allow_abbrev',
 'argument_default',
 'conflict_handler',
 'convert_arg_line_to_args',
 'description',
 'epilog',
 'error',
 'exit',
 'format_help',
 'format_usage',
 'formatter_class',
 'fromfile_prefix_chars',
 'get_default',
 'parse_args',
 'parse_known_args',
 'prefix_chars',
 'print_help',
 'print_usage',
 'prog',
 'register',
 'set_defaults',
 'usage']

In [4]: 

We can see a lot of functions.
And there is add_argument function.
There is add_argument() method document.

Japanese

16.4. argparse — コマンドラインオプション、引数、サブコマンドのパーサー — Python 3.6.5 ドキュメント

Engelish

16.4. argparse — Parser for command-line options, arguments and sub-commands — Python 3.6.5 documentation

Add help detail message.

Code changes...

# coding: utf-8
import argparse
parser = argparse.ArgumentParser()
# Positional arguments.
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print(args)

Only add help= option.

Run this.

$ python3 argparse.py -h
usage: argparse.py [-h] echo

positional arguments:
  echo        echo the string you use here

optional arguments:
  -h, --help  show this help message and exit
$

Added echo argument message. OK, we can add posisional argument message!

Add argument type in positional argument.

Code is bellow.

# coding: utf-8
import argparse
parser = argparse.ArgumentParser()
# Positional arguments.
parser.add_argument("echo_num", help="echo the number you use here", type=int)
args = parser.parse_args()
print(args)

Run this help.

$ python3 argparse.py -h
usage: argparse.py [-h] echo_num

positional arguments:
  echo_num    echo the number you use here

optional arguments:
  -h, --help  show this help message and exit
$

OK, it seems good.

Run the script with 2 that is positional argument.

$ python3 argparse.py 2
Namespace(echo_num=2)

$

OK, next. I will give test string to this script.

$ python3 argparse.py test
argparse.py: error: argument echo_num: invalid int value: 'test'

shell returned 2

$

I got an error with helpfull message. It's good behavior.

Use optional arguments.

Make optional argument sample.

# coding: utf-8
import argparse
parser = argparse.ArgumentParser()
# Optional arguments
parser.add_argument("--verbose", help="increase output verbosity")
args = parser.parse_args()
if args.verbose:
    print("verbosity turned on")

parser.add_argument() is same.
It is EASY!

Run the script.

$ python3 argparse.py -h
usage: argparse.py [-h] [--verbose VERBOSE]

optional arguments:
  -h, --help         show this help message and exit
  --verbose VERBOSE  increase output verbosity

Run the script with optional argument.

$ python3 argparse.py --verbose 1
verbosity turned on

args.verbose becomes True, run the print function.
It seems good!

Customize!

Change to get True or False on --verbose option. Now, --verbose opiton is able to get integer values.
Code bellow.

# coding: utf-8
import argparse
parser = argparse.ArgumentParser()
# Optional arguments
parser.add_argument("--verbose", help="increase output verbosity", action="store_true")
args = parser.parse_args()
if args.verbose:
    print("verbosity turned on")

Run the script with -h.

$ python3 argparse.py -h
usage: argparse.py [-h] [--verbose]

optional arguments:
  -h, --help  show this help message and exit
  --verbose   increase output verbosity

Help text has changed.

Short options

Easy

# coding: utf-8
import argparse
parser = argparse.ArgumentParser()
# Optional arguments
parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
args = parser.parse_args()
if args.verbose:
    print("verbosity turned on")

Only add "-v" option. It's so easy!

Run the script with -h option.

$ python3 argparse.py -h
usage: argparse.py [-h] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -v, --verbose  increase output verbosity

Help message has changed!

Thank you!

Python3 で Requests モジュール使い方の備忘録

Requests モジュールの備忘録

備忘録を書くことで、自分自身が使い方を忘れた時にそれを思い出す手助けとする。

Requests とは

Web アクセスののための Python モジュール?だと思います。
そもそも作られた理由が、urllib2 という Python 付属のモジュールがややこしすぎたからだとか。

Requests の公式ページにかかれているのは以下の通り。

Requests is an elegant and simple HTTP library for Python, built for human beings.

私の拙い訳ですと、

Requests は Python のためのエレガントでシンプルな HTTP ライブラリで、人間のために作られました。

うーん。変ですね。
translate.google.co.jp さんに翻訳していただきましょう。

Requestsは、人間のために構築された、Python用のエレガントでシンプルなHTTPライブラリです。

わかりやすい!

半端な翻訳よりよっぽどいいですね!テクノロジー。すごいです。

公式ページは以下からたどれます。
英語版

Requests: HTTP for Humans — Requests 2.18.4 documentation

日本語版

Requests: 人間のためのHTTP — requests-docs-ja 1.0.4 documentation

日本語版は、リリースバージョンが v1.0.4 と少し古い? 英語版だと v2.18.4 ですのでやはり古く感じてしまいますね。

更新履歴があるところを見つけました。良かったです。

Community Updates — Requests 2.18.4 documentation

インストール

$ pip install requests

で入るはずです。
入らなければ、公式を見ましょう。

Installation of Requests — Requests 2.18.4 documentation

Getting Started

ここにも書いてあるので、それでもいいと思います。

http://docs.python-requests.org/en/master/user/quickstart/#quickstart

やり方はそれぞれだと思いますが、やはり、インタプリタで試してからテキストファイルに落とすほうが確実だと思います。
今回はインタプリタで実行していきます。  

>>> import requests
>>> target_url = "https://github.com"
>>> r = requests.get(target_url)
>>> r.status_code
200

とりあえず、アクセスが出来ました。
この時拾ってきたレスポンスボディを表示させてみましょう。

>>> r.text
'\n\n\n\n\n\n<!DOCTYPE html>\n<html lang="en">\n  <head>\n    <meta charset="utf-8">\n  <link rel="dns-prefetch" href="https://assets-cdn.github.com">\n  <link rel="dns-prefetch" href="https://avatars0.githubusercontent.com">\n  <link rel="dns-prefetch" href="https://avatars1.githubusercontent.com">\n  <link rel="dns-prefetch" href="https://avatars2.githubusercontent.com">\n  <link rel="dns-prefetch" href="https://avatars3.githubusercontent.com">\n  <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">\n  <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">\n\n\n\n 
...

ちゃんと取ってこれているようです。整形されていないので、見づらいですね。
そういう問のために、BeautifulSoup4 という素敵なものもあるんですが、それはまた別途使ってみようと思います。

ちなみにこの時の r が持つメソッド一覧

>>> [print(x) for x in dir(r)]
__attrs__
__bool__
__class__
__delattr__
__dict__
__dir__
__doc__
__enter__
__eq__
__exit__
__format__
__ge__
__getattribute__
__getstate__
__gt__
__hash__
__init__
__init_subclass__
__iter__
__le__
__lt__
__module__
__ne__
__new__
__nonzero__
__reduce__
__reduce_ex__
__repr__
__setattr__
__setstate__
__sizeof__
__str__
__subclasshook__
__weakref__
_content
_content_consumed
_next
apparent_encoding
close
connection
content
cookies
elapsed
encoding
headers
history
is_permanent_redirect
is_redirect
iter_content
iter_lines
json
links
next
ok
raise_for_status
raw
reason
request
status_code
text
url
[None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]
>>> 

この辺の詳しいことは、以下の リンク にも書いてあります。

Developer Interface — Requests 2.18.4 documentation

どんな値を拾ってデバッグしたいかによって、使いたいメソッド?が変わると思いますので、適宜ドキュメントを活用しましょう。
たとえば、ヘッダー情報を見てみたいときは、headers だったり、 request.headers を使えばわかります。

Requests は色々できて、ログインしてセッション維持して次のページへ遷移するとか、フォームに値を入れるとか、ユーザーエージェント情報を偽装?するとか。

そのあたりも順次追記していきたいと思います。


とりあえず、今日はここまで。(内容薄いな)
また、このページに追記します。