├── LICENSE.txt ├── scripts ├── euca2ools.py └── nova.py ├── T03_KVM - Status Report of running VMs.ipynb ├── T03_KVM - Confirm KVM is healthy .ipynb ├── README.md ├── O03_GCE - Destroy VM (Google Compute Engine).ipynb ├── README.ipynb ├── O03_KVM - Destroy VM on KVM.ipynb ├── D03c_KVM - Go! VM.ipynb ├── D90_Postscript - Operational Policy Settings; Security etc. (to be elaborated).ipynb ├── D01_GCE - Set! Go! (Google Compute Engine).ipynb ├── D00_Prerequisits for Literate Computing via Notebooks.ipynb └── D03b_KVM - Set! Ubuntu 14.04.ipynb /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Literate-computing-Basics (c) by National Institute of Informatics 2 | 3 | Literate-computing-Basics is licensed under a 4 | Creative Commons Attribution 4.0 International License. 5 | 6 | You should have received a copy of the license along with this 7 | work. If not, see . -------------------------------------------------------------------------------- /scripts/euca2ools.py: -------------------------------------------------------------------------------- 1 | # helper functions for Euca2ools 2 | 3 | import subprocess 4 | import os 5 | import time 6 | 7 | def run_euca2ools(envfile, cmd): 8 | env = os.environ.copy() 9 | with open(os.path.expanduser(envfile), 'r') as f: 10 | for l in f.readlines(): 11 | if l.startswith('export '): 12 | l = l[6:].strip() 13 | name, value = tuple(l.split('=')) 14 | if value.startswith('"'): 15 | value = value[1:-1] 16 | env[name] = value 17 | return subprocess.check_output(cmd, env=env).split('\n') 18 | -------------------------------------------------------------------------------- /scripts/nova.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from keystoneclient.v2_0 import client as kc 4 | from novaclient import client as nc 5 | from quantumclient.v2_0 import client as qc 6 | from glanceclient.v2 import client as gc 7 | import requests 8 | import subprocess 9 | 10 | kv_pattern = re.compile(r'\s*export\s+([A-Za-z_]+)\=(.*)\s*$') 11 | 12 | 13 | class BaremetalService(object): 14 | def __init__(self, keystone): 15 | self.keystone = keystone 16 | 17 | def list_nodes(self): 18 | nova_url = self.keystone.service_catalog.get_endpoints()['compute'][0]['adminURL'] 19 | return requests.get(nova_url + '/os-baremetal-nodes', headers={ 'X-Auth-Token': self.keystone.auth_token }).json()['nodes'] 20 | 21 | class ClientFactory(object): 22 | def __init__(self, authinfo): 23 | self.authinfo = authinfo 24 | 25 | def keystone(self): 26 | return kc.Client(auth_url=self.authinfo['OS_AUTH_URL'], username=self.authinfo['OS_USERNAME'], 27 | password=self.authinfo['OS_PASSWORD'], tenant_name=self.authinfo['OS_TENANT_NAME']) 28 | 29 | def nova(self): 30 | return nc.Client('2', self.authinfo['OS_USERNAME'], self.authinfo['OS_PASSWORD'], self.authinfo['OS_TENANT_NAME'], 31 | auth_url=self.authinfo['OS_AUTH_URL']) 32 | 33 | def quantum(self): 34 | return qc.Client(auth_url=self.authinfo['OS_AUTH_URL'], username=self.authinfo['OS_USERNAME'], 35 | password=self.authinfo['OS_PASSWORD'], tenant_name=self.authinfo['OS_TENANT_NAME']) 36 | 37 | def glance(self): 38 | keystone = self.keystone() 39 | return gc.Client(endpoint=keystone.service_catalog.get_endpoints()['image'][0]['publicURL'], token=keystone.auth_token) 40 | 41 | def nova_baremetal(self): 42 | return BaremetalService(self.keystone()) 43 | 44 | def get_client_factory(aic_auth_path): 45 | authinfo = {} 46 | with open(os.path.expanduser(aic_auth_path), 'r') as f: 47 | for line in f.readlines(): 48 | m = kv_pattern.match(line) 49 | if m: 50 | authinfo[m.group(1)] = m.group(2) 51 | return ClientFactory(authinfo) 52 | 53 | def run_client(envfile, cmd): 54 | env = os.environ.copy() 55 | with open(os.path.expanduser(envfile), 'r') as f: 56 | for line in f.readlines(): 57 | m = kv_pattern.match(line) 58 | if m: 59 | env[m.group(1)] = m.group(2) 60 | return subprocess.check_output(cmd, env=env).split('\n') 61 | -------------------------------------------------------------------------------- /T03_KVM - Status Report of running VMs.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: Status Report of running VMs\n", 8 | "\n", 9 | "---\n", 10 | "\n", 11 | "現在起動中のVMの一覧を確認し、各VMにpingが到達するかをチェックするNotebook。" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## Operation Note\n", 19 | "\n", 20 | "\n", 21 | "*ここに経緯を記述*" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "# Notebookと環境のBinding\n", 29 | "\n", 30 | "Inventory中のgroup名でBind対象を指示する。**Bind対象はKVMがインストールされた物理マシンとする。**" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 1, 36 | "metadata": { 37 | "ExecuteTime": { 38 | "end_time": "2016-04-26T08:07:17.170145", 39 | "start_time": "2016-04-26T08:07:17.162322" 40 | }, 41 | "collapsed": true 42 | }, 43 | "outputs": [], 44 | "source": [ 45 | "target_group = 'test-hypervisor'" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "Bind対象への疎通状態を確認する。SUCCESSと表示されればよい。" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 2, 58 | "metadata": { 59 | "ExecuteTime": { 60 | "end_time": "2016-04-26T08:07:19.352910", 61 | "start_time": "2016-04-26T08:07:18.027874" 62 | }, 63 | "collapsed": false 64 | }, 65 | "outputs": [ 66 | { 67 | "name": "stdout", 68 | "output_type": "stream", 69 | "text": [ 70 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 71 | " \"changed\": false, \r\n", 72 | " \"ping\": \"pong\"\r\n", 73 | "}\u001b[0m\r\n" 74 | ] 75 | } 76 | ], 77 | "source": [ 78 | "!ansible -m ping {target_group}" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "# VMの一覧確認\n", 86 | "\n", 87 | "起動中のVMの一覧を確認する。" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 3, 93 | "metadata": { 94 | "ExecuteTime": { 95 | "end_time": "2016-04-26T08:07:39.755701", 96 | "start_time": "2016-04-26T08:07:38.482204" 97 | }, 98 | "collapsed": false 99 | }, 100 | "outputs": [ 101 | { 102 | "name": "stdout", 103 | "output_type": "stream", 104 | "text": [ 105 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 106 | " Id Name State\r\n", 107 | "----------------------------------------------------\r\n", 108 | " 5 testvm-001 running\r\n", 109 | "\u001b[0m\r\n" 110 | ] 111 | } 112 | ], 113 | "source": [ 114 | "!ansible -b -a 'virsh list' {target_group}" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "各VMのIPアドレスを確認する。" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 4, 127 | "metadata": { 128 | "collapsed": false 129 | }, 130 | "outputs": [ 131 | { 132 | "data": { 133 | "text/plain": [ 134 | "['testvm-001']" 135 | ] 136 | }, 137 | "execution_count": 4, 138 | "metadata": {}, 139 | "output_type": "execute_result" 140 | } 141 | ], 142 | "source": [ 143 | "vmnames = !ansible -b -a 'virsh list' {target_group}\n", 144 | "vmnames = vmnames[1:]\n", 145 | "vmnames = vmnames[map(lambda l: l[0], filter(lambda l: l[1].startswith('----'), enumerate(vmnames)))[0] + 1:]\n", 146 | "vmnames = map(lambda l: l.split()[1], vmnames)\n", 147 | "vmnames" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "VM名-IPアドレス のtupleのリストを作成する。" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 5, 160 | "metadata": { 161 | "collapsed": false 162 | }, 163 | "outputs": [], 164 | "source": [ 165 | "import re\n", 166 | "def get_mac_address(vmname):\n", 167 | " domiflist_stdio = !ansible -b -a \"virsh domiflist {vmname}\" {target_group}\n", 168 | " mac_pattern = re.compile(r'.*bridge.*\\s([0-9a-f\\:]+)\\s*')\n", 169 | " vmmac = [mac_pattern.match(line).group(1) for line in domiflist_stdio if mac_pattern.match(line)][0]\n", 170 | " return vmmac\n", 171 | "\n", 172 | "def get_ip_address(vmmac):\n", 173 | " leases_stdio = !ansible -b -a \"grep {vmmac} /var/lib/dnsmasq/dnsmasq.leases\" {target_group}\n", 174 | " ip_pattern = re.compile(r'.*\\s([0-9a-f\\:]+)\\s+([0-9\\.]+)\\s.*')\n", 175 | " ipaddr = [ip_pattern.match(line).group(2) for line in leases_stdio if ip_pattern.match(line)][0]\n", 176 | " return ipaddr" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 6, 182 | "metadata": { 183 | "collapsed": false 184 | }, 185 | "outputs": [ 186 | { 187 | "data": { 188 | "text/plain": [ 189 | "[('testvm-001', 'XXX.XXX.XXX.66')]" 190 | ] 191 | }, 192 | "execution_count": 6, 193 | "metadata": {}, 194 | "output_type": "execute_result" 195 | } 196 | ], 197 | "source": [ 198 | "vmdescs = zip(vmnames, map(lambda mac: get_ip_address(mac), map(lambda n: get_mac_address(n), vmnames)))\n", 199 | "vmdescs" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "# VMの到達性確認\n", 207 | "\n", 208 | "VMへの到達性を確認する。" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": 7, 214 | "metadata": { 215 | "collapsed": false 216 | }, 217 | "outputs": [ 218 | { 219 | "name": "stdout", 220 | "output_type": "stream", 221 | "text": [ 222 | "VM: testvm-001 IP address: XXX.XXX.XXX.66\n", 223 | "PING XXX.XXX.XXX.66 (XXX.XXX.XXX.66) 56(84) bytes of data.\n", 224 | "64 bytes from XXX.XXX.XXX.66: icmp_seq=1 ttl=63 time=0.577 ms\n", 225 | "64 bytes from XXX.XXX.XXX.66: icmp_seq=2 ttl=63 time=0.450 ms\n", 226 | "64 bytes from XXX.XXX.XXX.66: icmp_seq=3 ttl=63 time=0.436 ms\n", 227 | "64 bytes from XXX.XXX.XXX.66: icmp_seq=4 ttl=63 time=0.436 ms\n", 228 | "\n", 229 | "--- XXX.XXX.XXX.66 ping statistics ---\n", 230 | "4 packets transmitted, 4 received, 0% packet loss, time 2998ms\n", 231 | "rtt min/avg/max/mdev = 0.436/0.474/0.577/0.065 ms\n", 232 | "\n", 233 | "\n" 234 | ] 235 | } 236 | ], 237 | "source": [ 238 | "for name, ipaddr in vmdescs:\n", 239 | " print('VM: {0} IP address: {1}'.format(name, ipaddr))\n", 240 | " !ping -c 4 {ipaddr}\n", 241 | " print('\\n')" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "metadata": { 248 | "collapsed": true 249 | }, 250 | "outputs": [], 251 | "source": [] 252 | } 253 | ], 254 | "metadata": { 255 | "kernelspec": { 256 | "display_name": "Python 2", 257 | "language": "python", 258 | "name": "python2" 259 | }, 260 | "language_info": { 261 | "codemirror_mode": { 262 | "name": "ipython", 263 | "version": 2 264 | }, 265 | "file_extension": ".py", 266 | "mimetype": "text/x-python", 267 | "name": "python", 268 | "nbconvert_exporter": "python", 269 | "pygments_lexer": "ipython2", 270 | "version": "2.7.12" 271 | }, 272 | "toc": { 273 | "toc_cell": false, 274 | "toc_number_sections": true, 275 | "toc_threshold": 6, 276 | "toc_window_display": false 277 | } 278 | }, 279 | "nbformat": 4, 280 | "nbformat_minor": 0 281 | } 282 | -------------------------------------------------------------------------------- /T03_KVM - Confirm KVM is healthy .ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: Confirm KVM is healthy\n", 8 | "\n", 9 | "---\n", 10 | "\n", 11 | "KVMをインストールしたマシンに関して、事後条件を満たしているか(KVMが正しくインストールされ、予期した通りに動作しているか)を確認するためのNotebook。" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## Operation Note\n", 19 | "\n", 20 | "\n", 21 | "*ここに経緯を記述*" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "# Notebookと環境のBinding\n", 29 | "\n", 30 | "Inventory中のgroup名でBind対象を指示する。\n", 31 | "**KVMなどがインストールされた物理マシン**を示すInventory中の名前を以下に指定する。" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 1, 37 | "metadata": { 38 | "ExecuteTime": { 39 | "end_time": "2016-04-26T08:07:17.170145", 40 | "start_time": "2016-04-26T08:07:17.162322" 41 | }, 42 | "collapsed": true 43 | }, 44 | "outputs": [], 45 | "source": [ 46 | "target_group = 'test-hypervisor'" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "Bind対象への疎通状態を確認する。" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 2, 59 | "metadata": { 60 | "ExecuteTime": { 61 | "end_time": "2016-04-26T08:07:19.352910", 62 | "start_time": "2016-04-26T08:07:18.027874" 63 | }, 64 | "collapsed": false 65 | }, 66 | "outputs": [ 67 | { 68 | "name": "stdout", 69 | "output_type": "stream", 70 | "text": [ 71 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 72 | " \"changed\": false, \r\n", 73 | " \"ping\": \"pong\"\r\n", 74 | "}\u001b[0m\r\n" 75 | ] 76 | } 77 | ], 78 | "source": [ 79 | "!ansible -m ping {target_group}" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "# 状態の検証\n" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## 仮想マシン用ブリッジが作成されていること\n", 94 | "\n", 95 | "仮想マシン用のブリッジが作成されていること。お手本を作成している環境においては、以下のようなインタフェース構成となることを想定している。\n", 96 | "\n", 97 | "- ブリッジ br-eth1 インタフェース ... ここにはサービス用IPアドレスが設定される\n", 98 | "- eth1インタフェース ... Promiscuousモードでサービス用NICと対応付け、br-eth1インタフェースに接続される" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 3, 104 | "metadata": { 105 | "collapsed": true 106 | }, 107 | "outputs": [], 108 | "source": [ 109 | "external_nic = 'eth1'\n", 110 | "bridge_nic = 'br-eth1'" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 4, 116 | "metadata": { 117 | "ExecuteTime": { 118 | "end_time": "2016-04-26T08:07:25.847812", 119 | "start_time": "2016-04-26T08:07:22.127652" 120 | }, 121 | "collapsed": false 122 | }, 123 | "outputs": [ 124 | { 125 | "name": "stdout", 126 | "output_type": "stream", 127 | "text": [ 128 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 129 | "10: br-eth1: mtu 1500 qdisc noqueue state UNKNOWN \n", 130 | " link/ether XX:XX:XX:XX:XX:XX brd XX:XX:XX:XX:XX:XX\n", 131 | " inet XXX.XXX.XXX.105/26 brd XXX.XXX.XXX.127 scope global br-eth1\n", 132 | " inet6 XX:XX:XX:XX:XX:XX/64 scope link \n", 133 | " valid_lft forever preferred_lft forever\n", 134 | "\u001b[0m\n", 135 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 136 | "2: eth1: mtu 1500 qdisc mq state UP qlen 1000\n", 137 | " link/ether XX:XX:XX:XX:XX:XX brd XX:XX:XX:XX:XX:XX\n", 138 | " inet6 XX:XX:XX:XX:XX:XX/64 scope link \n", 139 | " valid_lft forever preferred_lft forever\n", 140 | "\u001b[0m\n", 141 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 142 | "bridge name\tbridge id\t\tSTP enabled\tinterfaces\n", 143 | "br-eth1\t\t8000.246e960db538\tno\t\teth1\n", 144 | "\t\t\t\t\t\t\tvnet0\n", 145 | "\u001b[0m\n" 146 | ] 147 | } 148 | ], 149 | "source": [ 150 | "!ansible -a \"/sbin/ip addr show {bridge_nic}\" {target_group}\n", 151 | "!ansible -a \"/sbin/ip addr show {external_nic}\" {target_group}\n", 152 | "!ansible -a \"/usr/sbin/brctl show {bridge_nic}\" {target_group}" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "**br-bond0, bond0が定義されており、br-bond0にサービス用IPアドレスが定義されていれば**、仮想マシン操作に関するお手本Notebookはそのまま利用することができる。異なるNICの定義方法を採っている場合は、適宜NIC名などを書き換えながら実施する必要がある。" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "## libvirtのNetwork設定が無効化されていること\n", 167 | "\n", 168 | "defaultのNetwork設定が無効化されているかどうかを確認する。" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 5, 174 | "metadata": { 175 | "ExecuteTime": { 176 | "end_time": "2016-04-26T08:07:32.682995", 177 | "start_time": "2016-04-26T08:07:31.432732" 178 | }, 179 | "collapsed": false 180 | }, 181 | "outputs": [ 182 | { 183 | "name": "stdout", 184 | "output_type": "stream", 185 | "text": [ 186 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 187 | "Name State Autostart Persistent\r\n", 188 | "--------------------------------------------------\r\n", 189 | "default inactive no yes\r\n", 190 | "\u001b[0m\r\n" 191 | ] 192 | } 193 | ], 194 | "source": [ 195 | "!ansible -b -a 'virsh net-list --all' {target_group}" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "**defaultのstateがinactiveになっていて、かつautostartがnoになっていれば**OK。" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "## dnsmasqが起動していること\n", 210 | "\n", 211 | "dnsmasqが実行されているかを確認する。" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": 6, 217 | "metadata": { 218 | "ExecuteTime": { 219 | "end_time": "2016-04-26T08:07:36.086188", 220 | "start_time": "2016-04-26T08:07:34.768858" 221 | }, 222 | "collapsed": false 223 | }, 224 | "outputs": [ 225 | { 226 | "name": "stdout", 227 | "output_type": "stream", 228 | "text": [ 229 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 230 | "dnsmasq (pid 74097) is running...dnsdomainname: Unknown host\r\n", 231 | "\u001b[0m\r\n" 232 | ] 233 | } 234 | ], 235 | "source": [ 236 | "!ansible -b -a 'service dnsmasq status' {target_group}" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": {}, 242 | "source": [ 243 | "**dnsmasq (pid XXXXX) is running と表示されれば**OK。" 244 | ] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "metadata": {}, 249 | "source": [ 250 | "## libvirtが動作していること\n", 251 | "\n", 252 | "libvirtが動作しており、仮想マシン一覧が取得できるかどうかを確認する。" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 7, 258 | "metadata": { 259 | "ExecuteTime": { 260 | "end_time": "2016-04-26T08:07:39.755701", 261 | "start_time": "2016-04-26T08:07:38.482204" 262 | }, 263 | "collapsed": false 264 | }, 265 | "outputs": [ 266 | { 267 | "name": "stdout", 268 | "output_type": "stream", 269 | "text": [ 270 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 271 | " Id Name State\r\n", 272 | "----------------------------------------------------\r\n", 273 | " 5 testvm-001 running\r\n", 274 | "\u001b[0m\r\n" 275 | ] 276 | } 277 | ], 278 | "source": [ 279 | "!ansible -b -a 'virsh list' {target_group}" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "**エラーメッセージが表示されなければ**OK。" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "metadata": { 293 | "collapsed": true 294 | }, 295 | "outputs": [], 296 | "source": [] 297 | } 298 | ], 299 | "metadata": { 300 | "kernelspec": { 301 | "display_name": "Python 2", 302 | "language": "python", 303 | "name": "python2" 304 | }, 305 | "language_info": { 306 | "codemirror_mode": { 307 | "name": "ipython", 308 | "version": 2 309 | }, 310 | "file_extension": ".py", 311 | "mimetype": "text/x-python", 312 | "name": "python", 313 | "nbconvert_exporter": "python", 314 | "pygments_lexer": "ipython2", 315 | "version": "2.7.12" 316 | }, 317 | "toc": { 318 | "toc_cell": false, 319 | "toc_number_sections": true, 320 | "toc_threshold": 6, 321 | "toc_window_display": false 322 | } 323 | }, 324 | "nbformat": 4, 325 | "nbformat_minor": 0 326 | } 327 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # About: Literate Computing for Reproducible Infrastructure README 3 | 4 | --- 5 | 6 | **Literate Computing for Reproducible Infrastructure:** Notebooks for operations of infrastructure. Operational procedures and considerations are described literatelly and reproducibly using Jupyter and Ansible. 7 | These are a kind of exemplary copybooks which present how NII cloud operation does, thus you may need appropriate modification on your practice. 8 | 9 | インフラ運用をJupyter + Ansibleでおこなう際のお手本Notebookです。
10 | **なお、これらのNotebookはNIIクラウドチーム内で行っている作業の考え方を示すためのもので、環境によってはそのままでは動作しないものもあります。** 11 | 12 | ---- 13 | 14 | [![Creative Commons License](https://i.creativecommons.org/l/by/4.0/88x31.png)](http://creativecommons.org/licenses/by/4.0/) 15 | 16 | Literate-computing-Basics (c) by National Institute of Informatics 17 | 18 | Literate-computing-Basics is licensed under a 19 | Creative Commons Attribution 4.0 International License. 20 | 21 | You should have received a copy of the license along with this 22 | work. If not, see . 23 | 24 | ## 関連資料 25 | 26 | 27 | - [Jupyter notebook を用いた文芸的インフラ運用のススメ - SlideShare](http://www.slideshare.net/nobu758/jupyter-notebook-63167604) 28 | - [Literate Automation(文芸的自動化)についての考察 - めもめも](http://enakai00.hatenablog.com/entry/2016/04/22/204125) 29 | 30 | 31 | 32 | # お手本Notebook 33 | 34 | お手本NotebookはこのNotebookと同じディレクトリにあります。Notebookは目的に応じて以下のような命名規則に則って名前がつけられています。 35 | 36 | - D(NN)\_(Notebook名) ... インストール関連Notebook 37 | - O(NN)\_(Notebook名) ... 運用関連Notebook 38 | - T(NN)\_(Notebook名) ... テスト関連Notebook 39 | 40 | 特に、**[Notebook環境Prerequisite](D00_Notebook%E7%92%B0%E5%A2%83Prerequisite.ipynb)は、お手本Notebookが適用可能なNotebook環境、Bind対象であるかどうかを確認するためのNotebook**です。はじめに実施して、これらのお手本Notebookが利用可能な状態かを確認してみてください。 41 | 42 | 現在、このNotebook環境からアクセス可能なNotebookの一覧を参照するには、以下のセルを実行(`Run cell`)してください。Notebookファイルへのリンクが表示されます。 43 | 44 | 45 | ```python 46 | import re 47 | import os 48 | from IPython.core.display import HTML 49 | 50 | ref_notebooks = filter(lambda m: m, map(lambda n: re.match(r'([A-Z][0-9][0-9a-z]+_.*)\.ipynb', n), os.listdir('.'))) 51 | ref_notebooks = sorted(ref_notebooks, key=lambda m: m.group(1)) 52 | HTML(''.join(map(lambda m: '
{title}
'.format(name=m.group(0), title=m.group(1)), 53 | ref_notebooks))) 54 | ``` 55 | 56 | 57 | 58 | 59 |
D00_Prerequisits for Literate Computing via Notebooks
D01_GCE - Set! Go! (Google Compute Engine)
D02_Docker - Ready! on Ubuntu and Set! Go!
D03_KVM - Ready! on CentOS
D03b_KVM - Set! CentOS6
D03b_KVM - Set! Ubuntu 14.04
D03c_KVM - Go! VM
D90_Postscript - Operational Policy Settings; Security etc. (to be elaborated)
O03_GCE - Destroy VM (Google Compute Engine)
O03_KVM - Destroy VM on KVM
T03_KVM - Confirm KVM is healthy
T03_KVM - Status Report of running VMs
60 | 61 | 62 | 63 | ## お手本Notebookと証跡Notebook 64 | 65 | お手本Notebookを使う場合は、お手本をコピーし、そのコピーを開きます。このように、**お手本と作業証跡は明確に分けながら作業をおこないます。** 66 | 67 | また、お手本をコピーする際は、 `YYYYMMDD_NN_` といった実施日を示すプレフィックスを付加することで、後で整理しやすくしています。 68 | 69 | 70 | ## 実際にお手本Notebookを使ってみる 71 | 72 | 以下のJavaScriptを実行することで、簡単にお手本から作業用Notebookを作成することもできます。 73 | 74 | 以下のセルを実行すると、Notebook名のドロップダウンリストと[作業開始]ボタンが現れます。 75 | [作業開始]ボタンを押すと、お手本Notebookのコピーを作成した後、自動的にブラウザでコピーが開きます。 76 | Notebookの説明を確認しながら実行、適宜修正しながら実行していってください。 77 | 78 | 79 | ```python 80 | from datetime import datetime 81 | import shutil 82 | 83 | def copy_ref_notebook(src): 84 | prefix = datetime.now().strftime('%Y%m%d') + '_' 85 | index = len(filter(lambda name: name.startswith(prefix), os.listdir('.'))) + 1 86 | new_notebook = '{0}{1:0>2}_{2}'.format(prefix, index, src) 87 | shutil.copyfile(src, new_notebook) 88 | print(new_notebook) 89 | 90 | frags = map(lambda m: ''.format(name=m.group(0), title=m.group(1)), 91 | ref_notebooks) 92 | HTML(''' 93 | 102 | ') 103 | ``` 104 | 105 | 106 | 107 | 108 | 109 | 118 | 119 | 120 | 121 | 122 | ## お手本のアーカイブ 123 | 124 | 以下のセルで、お手本NotebookのZIPアーカイブを作成できます。 125 | 126 | 127 | ```python 128 | ref_notebooks = filter(lambda m: m, map(lambda n: re.match(r'([A-Z][0-9][0-9a-z]+_.*)\.ipynb', n), os.listdir('.'))) 129 | ref_notebooks = sorted(ref_notebooks, key=lambda m: m.group(1)) 130 | !zip ref_notebooks-{datetime.now().strftime('%Y%m%d')}.zip README.ipynb {' '.join(map(lambda n: '"' + n.group(0) + '"', ref_notebooks))} scripts/* 131 | ``` 132 | 133 | adding: README.ipynb (deflated 73%) 134 | adding: D00_Prerequisits for Literate Computing via Notebooks.ipynb (deflated 79%) 135 | adding: D01_GCE - Set! Go! (Google Compute Engine).ipynb (deflated 78%) 136 | adding: D02_Docker - Ready! on Ubuntu and Set! Go!.ipynb (deflated 87%) 137 | adding: D03_KVM - Ready! on CentOS.ipynb (deflated 88%) 138 | adding: D03b_KVM - Set! CentOS6.ipynb (deflated 82%) 139 | adding: D03b_KVM - Set! Ubuntu 14.04.ipynb (deflated 81%) 140 | adding: D03c_KVM - Go! VM.ipynb (deflated 77%) 141 | adding: D90_Postscript - Operational Policy Settings; Security etc. (to be elaborated).ipynb (deflated 78%) 142 | adding: O03_GCE - Destroy VM (Google Compute Engine).ipynb (deflated 74%) 143 | adding: O03_KVM - Destroy VM on KVM.ipynb (deflated 79%) 144 | adding: T03_KVM - Confirm KVM is healthy .ipynb (deflated 73%) 145 | adding: T03_KVM - Status Report of running VMs.ipynb (deflated 71%) 146 | adding: scripts/euca2ools.py (deflated 47%) 147 | adding: scripts/nova.py (deflated 67%) 148 | 149 | 150 | こいつを・・・以下のURLからダウンロードできます。 151 | 152 | 153 | ```python 154 | HTML('{filename}' \ 155 | .format(filename='ref_notebooks-' + datetime.now().strftime('%Y%m%d') + '.zip')) 156 | ``` 157 | 158 | 159 | 160 | 161 | ref_notebooks-20160801.zip 162 | 163 | 164 | 165 | 166 | ```python 167 | 168 | ``` 169 | -------------------------------------------------------------------------------- /O03_GCE - Destroy VM (Google Compute Engine).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: Destroy VM (Google Compute Engine)\n", 8 | "\n", 9 | "---\n", 10 | "\n", 11 | "Google Compute Engine上のマシンの削除をおこなうためのNotebook。" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## *Operation Note*\n", 19 | "\n", 20 | "*This is a cell for your own recording. ここに経緯を記述*" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "# 動作環境の確認\n", 28 | "\n", 29 | "このNotebookは、 [Google Python Client Library](https://github.com/google/google-api-python-client) を使ってマシンの削除を行います。\n", 30 | "\n", 31 | "このNotebook環境にGoogle Python Client Libraryがインストールされている必要があります。インストールされていない場合は、以下のセル実行に失敗し、 `ImportError` となります。" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 1, 37 | "metadata": { 38 | "collapsed": false 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "from googleapiclient import discovery" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "もし、インストールされていない場合は、[Using the Python Client Library](https://cloud.google.com/compute/docs/tutorials/python-guide)を参考にインストールしてください。" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 2, 55 | "metadata": { 56 | "collapsed": false, 57 | "scrolled": true 58 | }, 59 | "outputs": [], 60 | "source": [ 61 | "#!pip2 install --upgrade google-api-python-client\n", 62 | "#from googleapiclient import discovery" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "# 設定の定義\n", 70 | "\n", 71 | "どのマシンを削除するか?を定義していく。" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "## Credentialの指定\n", 79 | "\n", 80 | "Google Compute EngineにアクセスするためのCredentialを指示する。\n", 81 | "\n", 82 | "- JSONフォーマットのService Account情報\n", 83 | "- プロジェクトID\n", 84 | "\n", 85 | "を用意しておく。" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 3, 91 | "metadata": { 92 | "collapsed": true 93 | }, 94 | "outputs": [], 95 | "source": [ 96 | "creds = '~/.keys/xxxxxxxx.json'\n", 97 | "target_project_id = 'my_project_id'" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "computeサービスを取得しておく。" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 4, 110 | "metadata": { 111 | "collapsed": false 112 | }, 113 | "outputs": [ 114 | { 115 | "data": { 116 | "text/plain": [ 117 | "" 118 | ] 119 | }, 120 | "execution_count": 4, 121 | "metadata": {}, 122 | "output_type": "execute_result" 123 | } 124 | ], 125 | "source": [ 126 | "import os\n", 127 | "from oauth2client.client import GoogleCredentials\n", 128 | "\n", 129 | "credentials = GoogleCredentials.from_stream(os.path.expanduser(creds))\n", 130 | "compute = discovery.build('compute', 'v1', credentials=credentials)\n", 131 | "compute" 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": {}, 137 | "source": [ 138 | "## 削除対象の指定\n", 139 | "\n", 140 | "削除対象となるgroup名を定義する。" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 5, 146 | "metadata": { 147 | "collapsed": true 148 | }, 149 | "outputs": [], 150 | "source": [ 151 | "target_group = 'test-gce'" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "Bind対象への疎通状態を確認する。" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 6, 164 | "metadata": { 165 | "collapsed": false 166 | }, 167 | "outputs": [ 168 | { 169 | "name": "stdout", 170 | "output_type": "stream", 171 | "text": [ 172 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS => {\r\n", 173 | " \"changed\": false, \r\n", 174 | " \"ping\": \"pong\"\r\n", 175 | "}\u001b[0m\r\n" 176 | ] 177 | } 178 | ], 179 | "source": [ 180 | "!ansible -m ping {target_group}" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "ゾーンの定義を設定する。" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 7, 193 | "metadata": { 194 | "collapsed": true 195 | }, 196 | "outputs": [], 197 | "source": [ 198 | "target_zone = 'us-central1-f'" 199 | ] 200 | }, 201 | { 202 | "cell_type": "markdown", 203 | "metadata": {}, 204 | "source": [ 205 | "# マシンの削除" 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": {}, 211 | "source": [ 212 | "対象のIPアドレスを列挙する。" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 8, 218 | "metadata": { 219 | "collapsed": false 220 | }, 221 | "outputs": [ 222 | { 223 | "data": { 224 | "text/plain": [ 225 | "['XXX.XXX.XXX.59']" 226 | ] 227 | }, 228 | "execution_count": 8, 229 | "metadata": {}, 230 | "output_type": "execute_result" 231 | } 232 | ], 233 | "source": [ 234 | "ping_stdout = !ansible -m ping {target_group}\n", 235 | "import re\n", 236 | "pattern = re.compile(r'^([0-9\\.]+)\\s*\\|\\s*SUCCESS\\s+')\n", 237 | "ping_stdout = filter(lambda l: pattern.match(l), ping_stdout)\n", 238 | "ip_addresses = map(lambda l: pattern.match(l).group(1), ping_stdout)\n", 239 | "ip_addresses" 240 | ] 241 | }, 242 | { 243 | "cell_type": "markdown", 244 | "metadata": {}, 245 | "source": [ 246 | "対象のIPアドレスに対応するインスタンス名を列挙する。" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 9, 252 | "metadata": { 253 | "collapsed": false, 254 | "scrolled": true 255 | }, 256 | "outputs": [ 257 | { 258 | "data": { 259 | "text/plain": [ 260 | "[u'test-gce']" 261 | ] 262 | }, 263 | "execution_count": 9, 264 | "metadata": {}, 265 | "output_type": "execute_result" 266 | } 267 | ], 268 | "source": [ 269 | "vm_descs = compute.instances().list(project=target_project_id, zone=target_zone).execute()['items']\n", 270 | "vm_descs = filter(lambda i: filter(lambda interface: filter(lambda i: i['natIP'] in ip_addresses,\n", 271 | " interface['accessConfigs']), i['networkInterfaces']), vm_descs)\n", 272 | "vm_names = map(lambda i: i['name'], vm_descs)\n", 273 | "vm_names" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 10, 279 | "metadata": { 280 | "collapsed": false 281 | }, 282 | "outputs": [], 283 | "source": [ 284 | "assert(len(ip_addresses) == len(vm_names))" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": 11, 290 | "metadata": { 291 | "collapsed": false 292 | }, 293 | "outputs": [ 294 | { 295 | "data": { 296 | "text/plain": [ 297 | "{u'id': u'649172682625639893',\n", 298 | " u'insertTime': u'2016-06-17T13:19:06.485-07:00',\n", 299 | " u'kind': u'compute#operation',\n", 300 | " u'name': u'operation-1466194746086-5357f11031271-ca798771-ffceb3aa',\n", 301 | " u'operationType': u'delete',\n", 302 | " u'progress': 0,\n", 303 | " u'selfLink': u'https://www.googleapis.com/compute/v1/projects/my_project_id/zones/us-central1-f/operations/operation-1466194746086-5357f11031271-ca798771-ffceb3aa',\n", 304 | " u'status': u'PENDING',\n", 305 | " u'targetId': u'7960517509428916462',\n", 306 | " u'targetLink': u'https://www.googleapis.com/compute/v1/projects/my_project_id/zones/us-central1-f/instances/test-gce',\n", 307 | " u'user': u'me@developer.gserviceaccount.com',\n", 308 | " u'zone': u'https://www.googleapis.com/compute/v1/projects/my_project_id/zones/us-central1-f'}" 309 | ] 310 | }, 311 | "execution_count": 11, 312 | "metadata": {}, 313 | "output_type": "execute_result" 314 | } 315 | ], 316 | "source": [ 317 | "for vm_name in vm_names:\n", 318 | " resp = compute.instances().delete(project=target_project_id, zone=target_zone, instance=vm_name).execute()\n", 319 | "resp" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": {}, 325 | "source": [ 326 | "# Inventoryの更新\n", 327 | "\n", 328 | "Inventoryに、作成したマシンのIPアドレスを追加する。変更する前に、現在の内容をコピーしておく。" 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": 12, 334 | "metadata": { 335 | "collapsed": false 336 | }, 337 | "outputs": [ 338 | { 339 | "data": { 340 | "text/plain": [ 341 | "'/tmp/tmp2lf3Uy'" 342 | ] 343 | }, 344 | "execution_count": 12, 345 | "metadata": {}, 346 | "output_type": "execute_result" 347 | } 348 | ], 349 | "source": [ 350 | "import tempfile\n", 351 | "work_dir = tempfile.mkdtemp()\n", 352 | "work_dir" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": 13, 358 | "metadata": { 359 | "collapsed": true 360 | }, 361 | "outputs": [], 362 | "source": [ 363 | "!cp inventory {work_dir}/inventory-old" 364 | ] 365 | }, 366 | { 367 | "cell_type": "markdown", 368 | "metadata": {}, 369 | "source": [ 370 | "[Inventory](../edit/inventory) を修正する。" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": 14, 376 | "metadata": { 377 | "collapsed": false 378 | }, 379 | "outputs": [ 380 | { 381 | "name": "stdout", 382 | "output_type": "stream", 383 | "text": [ 384 | "--- /tmp/tmp2lf3Uy/inventory-old\t2016-06-18 05:19:14.221939266 +0900\r\n", 385 | "+++ inventory\t2016-06-18 05:19:19.965986220 +0900\r\n", 386 | "@@ -3,6 +3,3 @@\r\n", 387 | " \r\n", 388 | " [test-vm]\r\n", 389 | " XXX.XXX.XXX.66\r\n", 390 | "-\r\n", 391 | "-[test-gce]\r\n", 392 | "-XXX.XXX.XXX.59\r\n" 393 | ] 394 | } 395 | ], 396 | "source": [ 397 | "!diff -ur {work_dir}/inventory-old inventory" 398 | ] 399 | }, 400 | { 401 | "cell_type": "markdown", 402 | "metadata": {}, 403 | "source": [ 404 | "Bind対象にpingが**通じない**かどうかを確認する。" 405 | ] 406 | }, 407 | { 408 | "cell_type": "code", 409 | "execution_count": 15, 410 | "metadata": { 411 | "collapsed": false 412 | }, 413 | "outputs": [ 414 | { 415 | "name": "stdout", 416 | "output_type": "stream", 417 | "text": [ 418 | "\u001b[1;35m [WARNING]: provided hosts list is empty, only localhost is available\r\n", 419 | "\u001b[0m\r\n" 420 | ] 421 | } 422 | ], 423 | "source": [ 424 | "!ansible -m ping {target_group}" 425 | ] 426 | }, 427 | { 428 | "cell_type": "markdown", 429 | "metadata": {}, 430 | "source": [ 431 | "意図した結果となった。これで完了とする。" 432 | ] 433 | }, 434 | { 435 | "cell_type": "markdown", 436 | "metadata": {}, 437 | "source": [ 438 | "# 後始末\n", 439 | "\n", 440 | "一時ディレクトリを削除する。" 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": 16, 446 | "metadata": { 447 | "collapsed": true 448 | }, 449 | "outputs": [], 450 | "source": [ 451 | "!rm -fr {work_dir}" 452 | ] 453 | }, 454 | { 455 | "cell_type": "code", 456 | "execution_count": null, 457 | "metadata": { 458 | "collapsed": true 459 | }, 460 | "outputs": [], 461 | "source": [] 462 | } 463 | ], 464 | "metadata": { 465 | "kernelspec": { 466 | "display_name": "Python 2", 467 | "language": "python", 468 | "name": "python2" 469 | }, 470 | "language_info": { 471 | "codemirror_mode": { 472 | "name": "ipython", 473 | "version": 2 474 | }, 475 | "file_extension": ".py", 476 | "mimetype": "text/x-python", 477 | "name": "python", 478 | "nbconvert_exporter": "python", 479 | "pygments_lexer": "ipython2", 480 | "version": "2.7.12" 481 | }, 482 | "toc": { 483 | "toc_cell": false, 484 | "toc_number_sections": true, 485 | "toc_threshold": 6, 486 | "toc_window_display": false 487 | } 488 | }, 489 | "nbformat": 4, 490 | "nbformat_minor": 0 491 | } 492 | -------------------------------------------------------------------------------- /README.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: Literate Computing for Reproducible Infrastructure README\n", 8 | "\n", 9 | "---\n", 10 | "\n", 11 | "**Literate Computing for Reproducible Infrastructure:** Notebooks for operations of infrastructure. Operational procedures and considerations are described literatelly and reproducibly using Jupyter and Ansible. \n", 12 | "These are a kind of exemplary copybooks which present how NII cloud operation does, thus you may need appropriate modification on your practice.\n", 13 | "\n", 14 | "インフラ運用をJupyter + Ansibleでおこなう際のお手本Notebookです。
\n", 15 | "**なお、これらのNotebookはNIIクラウドチーム内で行っている作業の考え方を示すためのもので、環境によってはそのままでは動作しないものもあります。**" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "----\n", 23 | "\n", 24 | "[![Creative Commons License](https://i.creativecommons.org/l/by/4.0/88x31.png)](http://creativecommons.org/licenses/by/4.0/)\n", 25 | "\n", 26 | "Literate-computing-Basics (c) by National Institute of Informatics\n", 27 | "\n", 28 | "Literate-computing-Basics is licensed under a\n", 29 | "Creative Commons Attribution 4.0 International License.\n", 30 | "\n", 31 | "You should have received a copy of the license along with this\n", 32 | "work. If not, see ." 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "## 関連資料\n", 40 | "\n", 41 | "\n", 42 | "- [Jupyter notebook を用いた文芸的インフラ運用のススメ - SlideShare](http://www.slideshare.net/nobu758/jupyter-notebook-63167604)\n", 43 | "- [Literate Automation(文芸的自動化)についての考察 - めもめも](http://enakai00.hatenablog.com/entry/2016/04/22/204125)\n", 44 | "\n" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "# お手本Notebook\n", 52 | "\n", 53 | "お手本NotebookはこのNotebookと同じディレクトリにあります。Notebookは目的に応じて以下のような命名規則に則って名前がつけられています。\n", 54 | "\n", 55 | "- D(NN)\\_(Notebook名) ... インストール関連Notebook\n", 56 | "- O(NN)\\_(Notebook名) ... 運用関連Notebook\n", 57 | "- T(NN)\\_(Notebook名) ... テスト関連Notebook\n", 58 | "\n", 59 | "特に、**[Notebook環境Prerequisite](D00_Notebook%E7%92%B0%E5%A2%83Prerequisite.ipynb)は、お手本Notebookが適用可能なNotebook環境、Bind対象であるかどうかを確認するためのNotebook**です。はじめに実施して、これらのお手本Notebookが利用可能な状態かを確認してみてください。" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "現在、このNotebook環境からアクセス可能なNotebookの一覧を参照するには、以下のセルを実行(`Run cell`)してください。Notebookファイルへのリンクが表示されます。" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 1, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "data": { 76 | "text/html": [ 77 | "" 78 | ], 79 | "text/plain": [ 80 | "" 81 | ] 82 | }, 83 | "execution_count": 1, 84 | "metadata": {}, 85 | "output_type": "execute_result" 86 | } 87 | ], 88 | "source": [ 89 | "import re\n", 90 | "import os\n", 91 | "from IPython.core.display import HTML\n", 92 | "\n", 93 | "ref_notebooks = filter(lambda m: m, map(lambda n: re.match(r'([A-Z][0-9][0-9a-z]+_.*)\\.ipynb', n), os.listdir('.')))\n", 94 | "ref_notebooks = sorted(ref_notebooks, key=lambda m: m.group(1))\n", 95 | "HTML(''.join(map(lambda m: ''.format(name=m.group(0), title=m.group(1)),\n", 96 | " ref_notebooks)))" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "## お手本Notebookと証跡Notebook\n", 104 | "\n", 105 | "お手本Notebookを使う場合は、お手本をコピーし、そのコピーを開きます。このように、**お手本と作業証跡は明確に分けながら作業をおこないます。**\n", 106 | "\n", 107 | "また、お手本をコピーする際は、 `YYYYMMDD_NN_` といった実施日を示すプレフィックスを付加することで、後で整理しやすくしています。\n" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "## 実際にお手本Notebookを使ってみる\n", 115 | "\n", 116 | "以下のJavaScriptを実行することで、簡単にお手本から作業用Notebookを作成することもできます。\n", 117 | "\n", 118 | "以下のセルを実行すると、Notebook名のドロップダウンリストと[作業開始]ボタンが現れます。\n", 119 | "[作業開始]ボタンを押すと、お手本Notebookのコピーを作成した後、自動的にブラウザでコピーが開きます。\n", 120 | "Notebookの説明を確認しながら実行、適宜修正しながら実行していってください。" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 2, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "data": { 130 | "text/html": [ 131 | "\n", 132 | "\n", 141 | "" 142 | ], 143 | "text/plain": [ 144 | "" 145 | ] 146 | }, 147 | "execution_count": 2, 148 | "metadata": {}, 149 | "output_type": "execute_result" 150 | } 151 | ], 152 | "source": [ 153 | "from datetime import datetime\n", 154 | "import shutil\n", 155 | "\n", 156 | "def copy_ref_notebook(src):\n", 157 | " prefix = datetime.now().strftime('%Y%m%d') + '_'\n", 158 | " index = len(filter(lambda name: name.startswith(prefix), os.listdir('.'))) + 1\n", 159 | " new_notebook = '{0}{1:0>2}_{2}'.format(prefix, index, src)\n", 160 | " shutil.copyfile(src, new_notebook)\n", 161 | " print(new_notebook)\n", 162 | "\n", 163 | "frags = map(lambda m: ''.format(name=m.group(0), title=m.group(1)),\n", 164 | " ref_notebooks)\n", 165 | "HTML('''\n", 166 | "\n", 175 | "')" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": { 181 | "collapsed": true 182 | }, 183 | "source": [ 184 | "## お手本のアーカイブ\n", 185 | "\n", 186 | "以下のセルで、お手本NotebookのZIPアーカイブを作成できます。" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": 3, 192 | "metadata": {}, 193 | "outputs": [ 194 | { 195 | "name": "stdout", 196 | "output_type": "stream", 197 | "text": [ 198 | " adding: README.ipynb (deflated 73%)\r\n", 199 | " adding: D00_Prerequisits for Literate Computing via Notebooks.ipynb (deflated 79%)\r\n", 200 | " adding: D01_GCE - Set! Go! (Google Compute Engine).ipynb (deflated 78%)\r\n", 201 | " adding: D02_Docker - Ready! on Ubuntu and Set! Go!.ipynb (deflated 87%)\r\n", 202 | " adding: D03_KVM - Ready! on CentOS.ipynb (deflated 88%)\r\n", 203 | " adding: D03b_KVM - Set! CentOS6.ipynb (deflated 82%)\r\n", 204 | " adding: D03b_KVM - Set! Ubuntu 14.04.ipynb (deflated 81%)\r\n", 205 | " adding: D03c_KVM - Go! VM.ipynb (deflated 77%)\r\n", 206 | " adding: D90_Postscript - Operational Policy Settings; Security etc. (to be elaborated).ipynb (deflated 78%)\r\n", 207 | " adding: O03_GCE - Destroy VM (Google Compute Engine).ipynb (deflated 74%)\r\n", 208 | " adding: O03_KVM - Destroy VM on KVM.ipynb (deflated 79%)\r\n", 209 | " adding: T03_KVM - Confirm KVM is healthy .ipynb (deflated 73%)\r\n", 210 | " adding: T03_KVM - Status Report of running VMs.ipynb (deflated 71%)\r\n", 211 | " adding: scripts/euca2ools.py (deflated 47%)\r\n", 212 | " adding: scripts/nova.py (deflated 67%)\r\n" 213 | ] 214 | } 215 | ], 216 | "source": [ 217 | "ref_notebooks = filter(lambda m: m, map(lambda n: re.match(r'([A-Z][0-9][0-9a-z]+_.*)\\.ipynb', n), os.listdir('.')))\n", 218 | "ref_notebooks = sorted(ref_notebooks, key=lambda m: m.group(1))\n", 219 | "!zip ref_notebooks-{datetime.now().strftime('%Y%m%d')}.zip README.ipynb {' '.join(map(lambda n: '\"' + n.group(0) + '\"', ref_notebooks))} scripts/*" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "こいつを・・・以下のURLからダウンロードできます。" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": 4, 232 | "metadata": {}, 233 | "outputs": [ 234 | { 235 | "data": { 236 | "text/html": [ 237 | "ref_notebooks-20160801.zip" 238 | ], 239 | "text/plain": [ 240 | "" 241 | ] 242 | }, 243 | "execution_count": 4, 244 | "metadata": {}, 245 | "output_type": "execute_result" 246 | } 247 | ], 248 | "source": [ 249 | "HTML('{filename}' \\\n", 250 | " .format(filename='ref_notebooks-' + datetime.now().strftime('%Y%m%d') + '.zip'))" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": null, 256 | "metadata": { 257 | "collapsed": true 258 | }, 259 | "outputs": [], 260 | "source": [] 261 | } 262 | ], 263 | "metadata": { 264 | "kernelspec": { 265 | "display_name": "Python 2", 266 | "language": "python", 267 | "name": "python2" 268 | }, 269 | "language_info": { 270 | "codemirror_mode": { 271 | "name": "ipython", 272 | "version": 2 273 | }, 274 | "file_extension": ".py", 275 | "mimetype": "text/x-python", 276 | "name": "python", 277 | "nbconvert_exporter": "python", 278 | "pygments_lexer": "ipython2", 279 | "version": "2.7.9" 280 | }, 281 | "toc": { 282 | "colors": { 283 | "hover_highlight": "#DAA520", 284 | "running_highlight": "#FF0000", 285 | "selected_highlight": "#FFD700" 286 | }, 287 | "moveMenuLeft": true, 288 | "nav_menu": { 289 | "height": "135px", 290 | "width": "252px" 291 | }, 292 | "navigate_menu": true, 293 | "number_sections": true, 294 | "sideBar": true, 295 | "threshold": 4, 296 | "toc_cell": false, 297 | "toc_number_sections": true, 298 | "toc_section_display": "block", 299 | "toc_threshold": 6, 300 | "toc_window_display": false, 301 | "widenNotebook": false 302 | } 303 | }, 304 | "nbformat": 4, 305 | "nbformat_minor": 1 306 | } 307 | -------------------------------------------------------------------------------- /O03_KVM - Destroy VM on KVM.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: VM - Destroy VM on KVM\n", 8 | "\n", 9 | "---\n", 10 | "\n", 11 | "VMの停止用 Notebook。" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## *Operation Note*\n", 19 | "\n", 20 | "*This is a cell for your own recording. ここに経緯を記述*" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "# Notebookと環境のBinding\n", 28 | "\n", 29 | "Inventory中のgroup名でBind対象を指示する。ここでは、**VMを収容している物理マシンを指定する。**" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 1, 35 | "metadata": { 36 | "ExecuteTime": { 37 | "end_time": "2016-04-26T08:07:17.170145", 38 | "start_time": "2016-04-26T08:07:17.162322" 39 | }, 40 | "collapsed": true 41 | }, 42 | "outputs": [], 43 | "source": [ 44 | "target_group = 'test-hypervisor'" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "Bind対象への疎通状態を確認する。" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 2, 57 | "metadata": { 58 | "ExecuteTime": { 59 | "end_time": "2016-04-26T08:07:19.352910", 60 | "start_time": "2016-04-26T08:07:18.027874" 61 | }, 62 | "collapsed": false 63 | }, 64 | "outputs": [ 65 | { 66 | "name": "stdout", 67 | "output_type": "stream", 68 | "text": [ 69 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 70 | " \"changed\": false, \r\n", 71 | " \"ping\": \"pong\"\r\n", 72 | "}\u001b[0m\r\n" 73 | ] 74 | } 75 | ], 76 | "source": [ 77 | "!ansible -m ping {target_group}" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "対象マシンにlibvirtがインストールされているかを確認する。" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 3, 90 | "metadata": { 91 | "collapsed": false 92 | }, 93 | "outputs": [ 94 | { 95 | "name": "stdout", 96 | "output_type": "stream", 97 | "text": [ 98 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 99 | "Compiled against library: libvirt 0.10.2\r\n", 100 | "Using library: libvirt 0.10.2\r\n", 101 | "Using API: QEMU 0.10.2\r\n", 102 | "Running hypervisor: QEMU 0.12.1\r\n", 103 | "\u001b[0m\r\n" 104 | ] 105 | } 106 | ], 107 | "source": [ 108 | "!ansible -b -a 'virsh version' {target_group}" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "# 停止対象のVM確認\n", 116 | "\n", 117 | "停止したいVMのIPアドレスを定義する。" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 4, 123 | "metadata": { 124 | "collapsed": true 125 | }, 126 | "outputs": [], 127 | "source": [ 128 | "target_vm = 'XXX.XXX.XXX.66'" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "対象にpingが通ることを確認する。" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 5, 141 | "metadata": { 142 | "collapsed": false 143 | }, 144 | "outputs": [ 145 | { 146 | "name": "stdout", 147 | "output_type": "stream", 148 | "text": [ 149 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS => {\r\n", 150 | " \"changed\": false, \r\n", 151 | " \"ping\": \"pong\"\r\n", 152 | "}\u001b[0m\r\n" 153 | ] 154 | } 155 | ], 156 | "source": [ 157 | "!ansible -m ping {target_vm}" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": {}, 163 | "source": [ 164 | "対象のVM名を検索する。" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 6, 170 | "metadata": { 171 | "collapsed": false 172 | }, 173 | "outputs": [ 174 | { 175 | "data": { 176 | "text/plain": [ 177 | "['testvm-001']" 178 | ] 179 | }, 180 | "execution_count": 6, 181 | "metadata": {}, 182 | "output_type": "execute_result" 183 | } 184 | ], 185 | "source": [ 186 | "vmnames = !ansible -b -a 'virsh list' {target_group}\n", 187 | "vmnames = vmnames[1:]\n", 188 | "vmnames = vmnames[map(lambda l: l[0], filter(lambda l: l[1].startswith('----'), enumerate(vmnames)))[0] + 1:]\n", 189 | "vmnames = map(lambda l: l.split()[1], vmnames)\n", 190 | "vmnames" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 7, 196 | "metadata": { 197 | "collapsed": true 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "import re\n", 202 | "def get_mac_address(vmname):\n", 203 | " domiflist_stdio = !ansible -b -a \"virsh domiflist {vmname}\" {target_group}\n", 204 | " mac_pattern = re.compile(r'.*bridge.*\\s([0-9a-f\\:]+)\\s*')\n", 205 | " vmmac = [mac_pattern.match(line).group(1) for line in domiflist_stdio if mac_pattern.match(line)][0]\n", 206 | " return vmmac\n", 207 | "\n", 208 | "def get_ip_address(vmmac):\n", 209 | " leases_stdio = !ansible -b -a \"grep {vmmac} /var/lib/dnsmasq/dnsmasq.leases\" {target_group}\n", 210 | " ip_pattern = re.compile(r'.*\\s([0-9a-f\\:]+)\\s+([0-9\\.]+)\\s.*')\n", 211 | " ipaddr = [ip_pattern.match(line).group(2) for line in leases_stdio if ip_pattern.match(line)][0]\n", 212 | " return ipaddr" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 8, 218 | "metadata": { 219 | "collapsed": false 220 | }, 221 | "outputs": [ 222 | { 223 | "data": { 224 | "text/plain": [ 225 | "[('testvm-001', 'XXX.XXX.XXX.66')]" 226 | ] 227 | }, 228 | "execution_count": 8, 229 | "metadata": {}, 230 | "output_type": "execute_result" 231 | } 232 | ], 233 | "source": [ 234 | "vmdescs = zip(vmnames, map(lambda mac: get_ip_address(mac), map(lambda n: get_mac_address(n), vmnames)))\n", 235 | "vmdescs" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "対象のVM名を取得する。" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 9, 248 | "metadata": { 249 | "collapsed": false 250 | }, 251 | "outputs": [ 252 | { 253 | "data": { 254 | "text/plain": [ 255 | "'testvm-001'" 256 | ] 257 | }, 258 | "execution_count": 9, 259 | "metadata": {}, 260 | "output_type": "execute_result" 261 | } 262 | ], 263 | "source": [ 264 | "vmname = filter(lambda e: e[1] == target_vm, vmdescs)[0][0]\n", 265 | "vmname" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "VMの状態の確認。runningならば想定通り。" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": 10, 278 | "metadata": { 279 | "ExecuteTime": { 280 | "end_time": "2016-04-26T08:13:00.376011", 281 | "start_time": "2016-04-26T08:12:59.085499" 282 | }, 283 | "collapsed": false 284 | }, 285 | "outputs": [ 286 | { 287 | "name": "stdout", 288 | "output_type": "stream", 289 | "text": [ 290 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 291 | "running\r\n", 292 | "\u001b[0m\r\n" 293 | ] 294 | } 295 | ], 296 | "source": [ 297 | "!ansible -b -a \"virsh domstate {vmname}\" {target_group}" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [ 304 | "# 仮想マシンの停止\n", 305 | "\n", 306 | "仮想マシンを停止する。" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": 11, 312 | "metadata": { 313 | "ExecuteTime": { 314 | "end_time": "2016-04-26T08:14:08.449313", 315 | "start_time": "2016-04-26T08:14:07.153775" 316 | }, 317 | "collapsed": false 318 | }, 319 | "outputs": [ 320 | { 321 | "name": "stdout", 322 | "output_type": "stream", 323 | "text": [ 324 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 325 | "Domain testvm-001 destroyed\r\n", 326 | "\u001b[0m\r\n" 327 | ] 328 | } 329 | ], 330 | "source": [ 331 | "!ansible -b -a \"virsh destroy {vmname}\" {target_group}" 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "metadata": {}, 337 | "source": [ 338 | "しばらく待ってから再度 virsh listを実行すると、仮想マシンが停止してリストから消えたことがわかる。" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": 12, 344 | "metadata": { 345 | "ExecuteTime": { 346 | "end_time": "2016-04-26T08:14:12.198745", 347 | "start_time": "2016-04-26T08:14:10.927748" 348 | }, 349 | "collapsed": false 350 | }, 351 | "outputs": [ 352 | { 353 | "name": "stdout", 354 | "output_type": "stream", 355 | "text": [ 356 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 357 | " Id Name State\r\n", 358 | "----------------------------------------------------\r\n", 359 | "\u001b[0m\r\n" 360 | ] 361 | } 362 | ], 363 | "source": [ 364 | "!ansible -b -a \"virsh list\" {target_group}" 365 | ] 366 | }, 367 | { 368 | "cell_type": "markdown", 369 | "metadata": {}, 370 | "source": [ 371 | "## dnsmasqの後始末\n", 372 | "\n", 373 | "dnsmasqのリース情報の後始末。VM用IPアドレスが潤沢にある場合は不要。" 374 | ] 375 | }, 376 | { 377 | "cell_type": "code", 378 | "execution_count": 13, 379 | "metadata": { 380 | "ExecuteTime": { 381 | "end_time": "2016-04-26T08:14:20.386679", 382 | "start_time": "2016-04-26T08:14:19.161372" 383 | }, 384 | "collapsed": false 385 | }, 386 | "outputs": [ 387 | { 388 | "name": "stdout", 389 | "output_type": "stream", 390 | "text": [ 391 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 392 | "1466235520 XX:XX:XX:XX:XX:XX XXX.XXX.XXX.66 ubuntu *\r\n", 393 | "\u001b[0m\r\n" 394 | ] 395 | } 396 | ], 397 | "source": [ 398 | "!ansible -a \"cat /var/lib/dnsmasq/dnsmasq.leases\" {target_group}" 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": 14, 404 | "metadata": { 405 | "ExecuteTime": { 406 | "end_time": "2016-04-26T08:14:28.141800", 407 | "start_time": "2016-04-26T08:14:26.902022" 408 | }, 409 | "collapsed": false 410 | }, 411 | "outputs": [ 412 | { 413 | "name": "stdout", 414 | "output_type": "stream", 415 | "text": [ 416 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 417 | " \"backup\": \"\", \r\n", 418 | " \"changed\": true, \r\n", 419 | " \"found\": 1, \r\n", 420 | " \"msg\": \"1 line(s) removed\"\r\n", 421 | "}\u001b[0m\r\n" 422 | ] 423 | } 424 | ], 425 | "source": [ 426 | "!ansible -b -m lineinfile -a \"dest=/var/lib/dnsmasq/dnsmasq.leases regexp='^.*\\s+{ target_vm }\\s+.*' state=absent\" {target_group}" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": 15, 432 | "metadata": { 433 | "ExecuteTime": { 434 | "end_time": "2016-04-26T08:14:29.858483", 435 | "start_time": "2016-04-26T08:14:28.634401" 436 | }, 437 | "collapsed": false 438 | }, 439 | "outputs": [ 440 | { 441 | "name": "stdout", 442 | "output_type": "stream", 443 | "text": [ 444 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 445 | "\r\n", 446 | "\u001b[0m\r\n" 447 | ] 448 | } 449 | ], 450 | "source": [ 451 | "!ansible -a \"cat /var/lib/dnsmasq/dnsmasq.leases\" {target_group}" 452 | ] 453 | }, 454 | { 455 | "cell_type": "code", 456 | "execution_count": 16, 457 | "metadata": { 458 | "ExecuteTime": { 459 | "end_time": "2016-04-26T08:14:34.415883", 460 | "start_time": "2016-04-26T08:14:32.872087" 461 | }, 462 | "collapsed": false 463 | }, 464 | "outputs": [ 465 | { 466 | "name": "stdout", 467 | "output_type": "stream", 468 | "text": [ 469 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 470 | " \"changed\": true, \r\n", 471 | " \"name\": \"dnsmasq\", \r\n", 472 | " \"state\": \"started\"\r\n", 473 | "}\u001b[0m\r\n" 474 | ] 475 | } 476 | ], 477 | "source": [ 478 | "!ansible -b -m service -a \"name=dnsmasq state=restarted\" {target_group}" 479 | ] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "metadata": {}, 484 | "source": [ 485 | "# Inventoryの更新\n", 486 | "\n", 487 | "Inventoryから、破棄したマシンのIPアドレスを削除する。変更する前に、現在の内容をコピーしておく。" 488 | ] 489 | }, 490 | { 491 | "cell_type": "code", 492 | "execution_count": 17, 493 | "metadata": { 494 | "collapsed": false 495 | }, 496 | "outputs": [ 497 | { 498 | "data": { 499 | "text/plain": [ 500 | "'/tmp/tmp5Wl4z9'" 501 | ] 502 | }, 503 | "execution_count": 17, 504 | "metadata": {}, 505 | "output_type": "execute_result" 506 | } 507 | ], 508 | "source": [ 509 | "import tempfile\n", 510 | "work_dir = tempfile.mkdtemp()\n", 511 | "work_dir" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 18, 517 | "metadata": { 518 | "collapsed": false 519 | }, 520 | "outputs": [], 521 | "source": [ 522 | "!cp inventory {work_dir}/inventory-old" 523 | ] 524 | }, 525 | { 526 | "cell_type": "markdown", 527 | "metadata": {}, 528 | "source": [ 529 | "[Inventory](../edit/inventory) を修正する。" 530 | ] 531 | }, 532 | { 533 | "cell_type": "code", 534 | "execution_count": 19, 535 | "metadata": { 536 | "collapsed": false 537 | }, 538 | "outputs": [ 539 | { 540 | "name": "stdout", 541 | "output_type": "stream", 542 | "text": [ 543 | "--- /tmp/tmp5Wl4z9/inventory-old\t2016-06-18 05:25:51.809190153 +0900\r\n", 544 | "+++ inventory\t2016-06-18 05:25:57.569237254 +0900\r\n", 545 | "@@ -1,5 +1,2 @@\r\n", 546 | " [test-hypervisor]\r\n", 547 | " XXX.XXX.XXX.105\r\n", 548 | "-\r\n", 549 | "-[test-vm]\r\n", 550 | "-XXX.XXX.XXX.66\r\n" 551 | ] 552 | } 553 | ], 554 | "source": [ 555 | "!diff -ur {work_dir}/inventory-old inventory" 556 | ] 557 | }, 558 | { 559 | "cell_type": "markdown", 560 | "metadata": {}, 561 | "source": [ 562 | "削除したVMに**pingが通じない**かどうかを確認する。" 563 | ] 564 | }, 565 | { 566 | "cell_type": "code", 567 | "execution_count": 20, 568 | "metadata": { 569 | "collapsed": false 570 | }, 571 | "outputs": [ 572 | { 573 | "name": "stdout", 574 | "output_type": "stream", 575 | "text": [ 576 | "\u001b[1;35m [WARNING]: provided hosts list is empty, only localhost is available\r\n", 577 | "\u001b[0m\r\n" 578 | ] 579 | } 580 | ], 581 | "source": [ 582 | "!ansible -m ping {target_vm}" 583 | ] 584 | }, 585 | { 586 | "cell_type": "markdown", 587 | "metadata": { 588 | "collapsed": true 589 | }, 590 | "source": [ 591 | "# 後始末\n", 592 | "\n", 593 | "一時ディレクトリを削除する。" 594 | ] 595 | }, 596 | { 597 | "cell_type": "code", 598 | "execution_count": 21, 599 | "metadata": { 600 | "ExecuteTime": { 601 | "end_time": "2016-04-26T08:14:39.273093", 602 | "start_time": "2016-04-26T08:14:39.103511" 603 | }, 604 | "collapsed": true 605 | }, 606 | "outputs": [], 607 | "source": [ 608 | "!rm -fr {work_dir}" 609 | ] 610 | }, 611 | { 612 | "cell_type": "code", 613 | "execution_count": null, 614 | "metadata": { 615 | "collapsed": true 616 | }, 617 | "outputs": [], 618 | "source": [] 619 | } 620 | ], 621 | "metadata": { 622 | "kernelspec": { 623 | "display_name": "Python 2", 624 | "language": "python", 625 | "name": "python2" 626 | }, 627 | "language_info": { 628 | "codemirror_mode": { 629 | "name": "ipython", 630 | "version": 2 631 | }, 632 | "file_extension": ".py", 633 | "mimetype": "text/x-python", 634 | "name": "python", 635 | "nbconvert_exporter": "python", 636 | "pygments_lexer": "ipython2", 637 | "version": "2.7.12" 638 | }, 639 | "toc": { 640 | "toc_cell": false, 641 | "toc_number_sections": true, 642 | "toc_threshold": 6, 643 | "toc_window_display": false 644 | } 645 | }, 646 | "nbformat": 4, 647 | "nbformat_minor": 0 648 | } 649 | -------------------------------------------------------------------------------- /D03c_KVM - Go! VM.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: VM - Go! with prepared VM image\n", 8 | "\n", 9 | "---\n", 10 | "\n", 11 | "Start VM instance with prepared VM image using livbirt.\n", 12 | "\n", 13 | "libvirtがインストールされている仮想化基盤上で、VMを起動するためのNotebook。\n", 14 | "\n", 15 | "すでに**VMイメージ作成Notebook**により、イメージが作成されているものとする。" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "## *Operation Note*\n", 23 | "\n", 24 | "*This is a cell for your own recording. ここに経緯を記述*" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "# Notebookと環境のBinding\n", 32 | "\n", 33 | "Inventory中のgroup名でBind対象を指示する。" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 1, 39 | "metadata": { 40 | "ExecuteTime": { 41 | "end_time": "2016-04-26T08:07:17.170145", 42 | "start_time": "2016-04-26T08:07:17.162322" 43 | }, 44 | "collapsed": true 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "target_group = 'test-hypervisor'" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "Bind対象への疎通状態を確認する。" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 2, 61 | "metadata": { 62 | "ExecuteTime": { 63 | "end_time": "2016-04-26T08:07:19.352910", 64 | "start_time": "2016-04-26T08:07:18.027874" 65 | }, 66 | "collapsed": false 67 | }, 68 | "outputs": [ 69 | { 70 | "name": "stdout", 71 | "output_type": "stream", 72 | "text": [ 73 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 74 | " \"changed\": false, \r\n", 75 | " \"ping\": \"pong\"\r\n", 76 | "}\u001b[0m\r\n" 77 | ] 78 | } 79 | ], 80 | "source": [ 81 | "!ansible -m ping {target_group}" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "対象マシンにlibvirtがインストールされているかを確認する。" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 3, 94 | "metadata": { 95 | "collapsed": false 96 | }, 97 | "outputs": [ 98 | { 99 | "name": "stdout", 100 | "output_type": "stream", 101 | "text": [ 102 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 103 | "Compiled against library: libvirt 0.10.2\r\n", 104 | "Using library: libvirt 0.10.2\r\n", 105 | "Using API: QEMU 0.10.2\r\n", 106 | "Running hypervisor: QEMU 0.12.1\r\n", 107 | "\u001b[0m\r\n" 108 | ] 109 | } 110 | ], 111 | "source": [ 112 | "!ansible -b -a 'virsh version' {target_group}" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "# VMイメージの指定\n", 120 | "\n", 121 | "作成対象のVMのあるディレクトリを指定する。**VMイメージ作成Notebook**により生成されたイメージが格納されているディレクトリを指定すること。" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 4, 127 | "metadata": { 128 | "collapsed": true 129 | }, 130 | "outputs": [], 131 | "source": [ 132 | "image_base_dir = '/mnt/ubuntu14.04-base-vm'" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "以下の2つのファイルが存在している必要がある。\n", 140 | "\n", 141 | "- base.img\n", 142 | "- libvirt-base.xml" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 5, 148 | "metadata": { 149 | "collapsed": false 150 | }, 151 | "outputs": [ 152 | { 153 | "name": "stdout", 154 | "output_type": "stream", 155 | "text": [ 156 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 157 | "total 3853356\r\n", 158 | "drwxr-xr-x 2 root root 4096 Jun 17 19:36 .\r\n", 159 | "drwxr-xr-x 6 root root 4096 Jun 17 19:16 ..\r\n", 160 | "-rwxr-xr-x 1 root root 107374182400 Jun 17 19:36 base.img\r\n", 161 | "-rw-r--r-- 1 root root 2461 Jun 17 19:36 libvirt-base.xml\r\n", 162 | "\u001b[0m\r\n" 163 | ] 164 | } 165 | ], 166 | "source": [ 167 | "!ansible -b -a \"ls -la {image_base_dir}\" {target_group}" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "作成するVM名のリストを指定する。お手本では例として2つのVMを指定している。\n", 175 | "\n", 176 | "起動したいVM名をlistで指定すること。**既存のVMと重複してはならない。**" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 6, 182 | "metadata": { 183 | "collapsed": false 184 | }, 185 | "outputs": [ 186 | { 187 | "data": { 188 | "text/plain": [ 189 | "['testvm-001']" 190 | ] 191 | }, 192 | "execution_count": 6, 193 | "metadata": {}, 194 | "output_type": "execute_result" 195 | } 196 | ], 197 | "source": [ 198 | "vm_names = ['testvm-001']\n", 199 | "vm_names" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "# VMの作成\n", 207 | "\n", 208 | "VM用のファイルは以下のように作成される。\n", 209 | "\n", 210 | "- /mnt\n", 211 | " - (VM名).xml ... libvirtに与えるXML定義\n", 212 | " - (VM名).img ... VM用の仮想ディスク" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "## XML定義の生成\n", 220 | "\n", 221 | "基本となるXML定義を得る。" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 7, 227 | "metadata": { 228 | "collapsed": false 229 | }, 230 | "outputs": [ 231 | { 232 | "data": { 233 | "text/plain": [ 234 | "'/tmp/tmp1c6xt5'" 235 | ] 236 | }, 237 | "execution_count": 7, 238 | "metadata": {}, 239 | "output_type": "execute_result" 240 | } 241 | ], 242 | "source": [ 243 | "import tempfile\n", 244 | "work_dir = tempfile.mkdtemp()\n", 245 | "work_dir" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": 8, 251 | "metadata": { 252 | "collapsed": false 253 | }, 254 | "outputs": [ 255 | { 256 | "name": "stdout", 257 | "output_type": "stream", 258 | "text": [ 259 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 260 | " \"changed\": true, \r\n", 261 | " \"checksum\": \"544dfe399f5626ed3b316b9d3b0d3041fbc0b922\", \r\n", 262 | " \"dest\": \"/tmp/tmp1c6xt5/libvirt-base.xml\", \r\n", 263 | " \"md5sum\": \"6d84ff7f9786c472caa07f0ecdd3d74e\", \r\n", 264 | " \"remote_checksum\": \"544dfe399f5626ed3b316b9d3b0d3041fbc0b922\", \r\n", 265 | " \"remote_md5sum\": null\r\n", 266 | "}\u001b[0m\r\n" 267 | ] 268 | } 269 | ], 270 | "source": [ 271 | "!ansible -b -m fetch -a 'src={image_base_dir}/libvirt-base.xml dest={work_dir}/libvirt-base.xml flat=yes' {target_group}" 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": {}, 277 | "source": [ 278 | "基本のXML定義に基づいて、VM用定義を生成する。" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": 9, 284 | "metadata": { 285 | "collapsed": false 286 | }, 287 | "outputs": [ 288 | { 289 | "name": "stdout", 290 | "output_type": "stream", 291 | "text": [ 292 | "-rw-r--r-- 1 root root 2461 Jun 17 22:36 /tmp/tmp1c6xt5/libvirt-base.xml\r\n", 293 | "-rw-r--r-- 1 root root 2512 Jun 17 22:36 /tmp/tmp1c6xt5/testvm-001.xml\r\n" 294 | ] 295 | } 296 | ], 297 | "source": [ 298 | "import xml.etree.ElementTree as ET\n", 299 | "import virtinst.util\n", 300 | "import os\n", 301 | "\n", 302 | "for n in vm_names:\n", 303 | " vmxml = ET.parse(os.path.join(work_dir, 'libvirt-base.xml')).getroot()\n", 304 | " vmxml.find('name').text = n\n", 305 | " vmxml.find('devices').find('disk').find('source').attrib['file'] = os.path.join('/mnt', n + '.img')\n", 306 | " vmxml.find('devices').find('interface').find('mac').attrib['address'] = virtinst.util.randomMAC()\n", 307 | " ET.ElementTree(vmxml).write(os.path.join(work_dir, n + '.xml'))\n", 308 | "!ls -la {work_dir}/*.xml" 309 | ] 310 | }, 311 | { 312 | "cell_type": "markdown", 313 | "metadata": {}, 314 | "source": [ 315 | "ホストに定義ファイルをコピーする。" 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": 10, 321 | "metadata": { 322 | "collapsed": false 323 | }, 324 | "outputs": [ 325 | { 326 | "name": "stdout", 327 | "output_type": "stream", 328 | "text": [ 329 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 330 | " \"changed\": true, \r\n", 331 | " \"checksum\": \"aff0775772be017583b5155fe38993ea0a46cd9f\", \r\n", 332 | " \"dest\": \"/mnt/testvm-001.xml\", \r\n", 333 | " \"gid\": 0, \r\n", 334 | " \"group\": \"root\", \r\n", 335 | " \"md5sum\": \"01081a332cce845696c84fd1d12fde21\", \r\n", 336 | " \"mode\": \"0644\", \r\n", 337 | " \"owner\": \"root\", \r\n", 338 | " \"size\": 2512, \r\n", 339 | " \"src\": \"/home/ansible/.ansible/tmp/ansible-tmp-1466170607.85-174997417957451/source\", \r\n", 340 | " \"state\": \"file\", \r\n", 341 | " \"uid\": 0\r\n", 342 | "}\u001b[0m\r\n" 343 | ] 344 | } 345 | ], 346 | "source": [ 347 | "for n in vm_names:\n", 348 | " !ansible -b -m copy -a 'src={work_dir}/{n}.xml dest=/mnt/{n}.xml' {target_group}" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "## イメージファイルのコピー\n", 356 | "\n", 357 | "イメージファイルをVM用に複製する。" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": 11, 363 | "metadata": { 364 | "collapsed": false 365 | }, 366 | "outputs": [ 367 | { 368 | "name": "stdout", 369 | "output_type": "stream", 370 | "text": [ 371 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 372 | "\r\n", 373 | "\u001b[0m\r\n" 374 | ] 375 | } 376 | ], 377 | "source": [ 378 | "for n in vm_names:\n", 379 | " !ansible -b -a 'cp {image_base_dir}/base.img /mnt/{n}.img' {target_group}" 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": {}, 385 | "source": [ 386 | "## VMの起動\n", 387 | "\n", 388 | "XMLファイル、仮想ディスクファイルがあるかどうかを確認する。" 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": 12, 394 | "metadata": { 395 | "collapsed": false 396 | }, 397 | "outputs": [ 398 | { 399 | "name": "stdout", 400 | "output_type": "stream", 401 | "text": [ 402 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 403 | "-rwxr-xr-x 1 root root 107374182400 Jun 17 22:38 /mnt/testvm-001.img\r\n", 404 | "-rw-r--r-- 1 root root 2512 Jun 17 22:36 /mnt/testvm-001.xml\r\n", 405 | "\u001b[0m\r\n" 406 | ] 407 | } 408 | ], 409 | "source": [ 410 | "for n in vm_names:\n", 411 | " !ansible -a 'ls -la /mnt/{n}.img /mnt/{n}.xml' {target_group}" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": {}, 417 | "source": [ 418 | "VMを起動する。" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": 13, 424 | "metadata": { 425 | "collapsed": false 426 | }, 427 | "outputs": [ 428 | { 429 | "name": "stdout", 430 | "output_type": "stream", 431 | "text": [ 432 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 433 | "Domain testvm-001 created from /mnt/testvm-001.xml\r\n", 434 | "\u001b[0m\r\n" 435 | ] 436 | } 437 | ], 438 | "source": [ 439 | "import time\n", 440 | "for n in vm_names:\n", 441 | " !ansible -b -a 'virsh create /mnt/{n}.xml' {target_group}\n", 442 | " time.sleep(60)" 443 | ] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "metadata": {}, 448 | "source": [ 449 | "VMに設定されたIPアドレスを確認する。" 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": 14, 455 | "metadata": { 456 | "collapsed": false 457 | }, 458 | "outputs": [], 459 | "source": [ 460 | "import re\n", 461 | "def get_mac_address(vmname):\n", 462 | " domiflist_stdio = !ansible -b -a \"virsh domiflist {vmname}\" {target_group}\n", 463 | " mac_pattern = re.compile(r'.*bridge.*\\s([0-9a-f\\:]+)\\s*')\n", 464 | " vmmac = [mac_pattern.match(line).group(1) for line in domiflist_stdio if mac_pattern.match(line)][0]\n", 465 | " return vmmac\n", 466 | "\n", 467 | "def get_ip_address(vmmac):\n", 468 | " leases_stdio = !ansible -b -a \"grep {vmmac} /var/lib/dnsmasq/dnsmasq.leases\" {target_group}\n", 469 | " ip_pattern = re.compile(r'.*\\s([0-9a-f\\:]+)\\s+([0-9\\.]+)\\s.*')\n", 470 | " ipaddr = [ip_pattern.match(line).group(2) for line in leases_stdio if ip_pattern.match(line)][0]\n", 471 | " return ipaddr" 472 | ] 473 | }, 474 | { 475 | "cell_type": "code", 476 | "execution_count": 15, 477 | "metadata": { 478 | "collapsed": false 479 | }, 480 | "outputs": [ 481 | { 482 | "data": { 483 | "text/plain": [ 484 | "[('testvm-001', 'XXX.XXX.XXX.66')]" 485 | ] 486 | }, 487 | "execution_count": 15, 488 | "metadata": {}, 489 | "output_type": "execute_result" 490 | } 491 | ], 492 | "source": [ 493 | "vmdescs = zip(vm_names, map(lambda mac: get_ip_address(mac), map(lambda n: get_mac_address(n), vm_names)))\n", 494 | "vmdescs" 495 | ] 496 | }, 497 | { 498 | "cell_type": "markdown", 499 | "metadata": {}, 500 | "source": [ 501 | "# Inventoryの更新\n", 502 | "\n", 503 | "Inventoryに、作成したマシンのIPアドレスを追加する。変更する前に、現在の内容をコピーしておく。" 504 | ] 505 | }, 506 | { 507 | "cell_type": "code", 508 | "execution_count": 16, 509 | "metadata": { 510 | "collapsed": true 511 | }, 512 | "outputs": [], 513 | "source": [ 514 | "!cp inventory {work_dir}/inventory-old" 515 | ] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "metadata": {}, 520 | "source": [ 521 | "[Inventory](../edit/inventory) を修正する。" 522 | ] 523 | }, 524 | { 525 | "cell_type": "code", 526 | "execution_count": 17, 527 | "metadata": { 528 | "collapsed": false 529 | }, 530 | "outputs": [ 531 | { 532 | "name": "stdout", 533 | "output_type": "stream", 534 | "text": [ 535 | "--- /tmp/tmp1c6xt5/inventory-old\t2016-06-17 22:39:22.008979291 +0900\r\n", 536 | "+++ inventory\t2016-06-17 22:40:16.505428369 +0900\r\n", 537 | "@@ -1,2 +1,5 @@\r\n", 538 | " [test-hypervisor]\r\n", 539 | " XXX.XXX.XXX.105\r\n", 540 | "+\r\n", 541 | "+[test-vm]\r\n", 542 | "+XXX.XXX.XXX.66\r\n" 543 | ] 544 | } 545 | ], 546 | "source": [ 547 | "!diff -ur {work_dir}/inventory-old inventory" 548 | ] 549 | }, 550 | { 551 | "cell_type": "markdown", 552 | "metadata": {}, 553 | "source": [ 554 | "追加したグループ名でpingが通じるかどうかを確認する。" 555 | ] 556 | }, 557 | { 558 | "cell_type": "code", 559 | "execution_count": 18, 560 | "metadata": { 561 | "collapsed": true 562 | }, 563 | "outputs": [], 564 | "source": [ 565 | "target_vmgroup = 'test-vm'" 566 | ] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": 19, 571 | "metadata": { 572 | "collapsed": false 573 | }, 574 | "outputs": [ 575 | { 576 | "name": "stdout", 577 | "output_type": "stream", 578 | "text": [ 579 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS => {\r\n", 580 | " \"changed\": false, \r\n", 581 | " \"ping\": \"pong\"\r\n", 582 | "}\u001b[0m\r\n" 583 | ] 584 | } 585 | ], 586 | "source": [ 587 | "!ansible -m ping {target_vmgroup}" 588 | ] 589 | }, 590 | { 591 | "cell_type": "markdown", 592 | "metadata": { 593 | "collapsed": true 594 | }, 595 | "source": [ 596 | "# 後始末\n", 597 | "\n", 598 | "一時ディレクトリを削除する。" 599 | ] 600 | }, 601 | { 602 | "cell_type": "code", 603 | "execution_count": 20, 604 | "metadata": { 605 | "ExecuteTime": { 606 | "end_time": "2016-04-26T08:14:39.273093", 607 | "start_time": "2016-04-26T08:14:39.103511" 608 | }, 609 | "collapsed": true 610 | }, 611 | "outputs": [], 612 | "source": [ 613 | "!rm -fr {work_dir}" 614 | ] 615 | }, 616 | { 617 | "cell_type": "code", 618 | "execution_count": null, 619 | "metadata": { 620 | "collapsed": true 621 | }, 622 | "outputs": [], 623 | "source": [] 624 | } 625 | ], 626 | "metadata": { 627 | "kernelspec": { 628 | "display_name": "Python 2", 629 | "language": "python", 630 | "name": "python2" 631 | }, 632 | "language_info": { 633 | "codemirror_mode": { 634 | "name": "ipython", 635 | "version": 2 636 | }, 637 | "file_extension": ".py", 638 | "mimetype": "text/x-python", 639 | "name": "python", 640 | "nbconvert_exporter": "python", 641 | "pygments_lexer": "ipython2", 642 | "version": "2.7.12" 643 | }, 644 | "toc": { 645 | "toc_cell": false, 646 | "toc_number_sections": true, 647 | "toc_threshold": 6, 648 | "toc_window_display": false 649 | } 650 | }, 651 | "nbformat": 4, 652 | "nbformat_minor": 0 653 | } 654 | -------------------------------------------------------------------------------- /D90_Postscript - Operational Policy Settings; Security etc. (to be elaborated).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: Postscript - Operational Policy Settings; Security etc.\n", 8 | "運用ポリシーに基づく共通設定 - セキュリティなど\n", 9 | "\n", 10 | "---\n", 11 | "Each computing host should be applied common settings based on operational policies; for example security, networking, etc. \n", 12 | "\n", 13 | "This notebook is just a sample at our private cloud. Note that this is not exhaustive manuscript. However, **here we claim \"Operational Policy Settings\" should be clearly described and explicitly shared as a notebook. **\n", 14 | "\n", 15 | "確保したマシンに対して共通のポリシーを適用する。 ここでは我々のプライベートなクラウド「研究クラウド/AIC」でマシンを確保した場合に実施している、基本的なセキュリティ設定をサンプルとして示す。\n", 16 | "\n", 17 | "**このNotebookをここに掲載しているのは、運用プロセスの一環としてセキュリティに関するタスクが明示的に書かれかつその内容を運用者が共有すべきであるということを主張のためです。**\n", 18 | "\n", 19 | "**そのため、こんな感じで書けるというナイーブな参考です。**" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## *Operation Note*\n", 27 | "\n", 28 | "*This is a cell for your own recording. ここに経緯を記述*" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "# Notebookと設定対象とのBinding\n", 36 | "\n", 37 | "セキュリティ設定対象を確認する。\n", 38 | "Inventory中のgroup名でBind対象を指示する。" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 1, 44 | "metadata": { 45 | "ExecuteTime": { 46 | "end_time": "2016-04-12T16:07:49.340426", 47 | "start_time": "2016-04-12T16:07:49.331924" 48 | }, 49 | "collapsed": true 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "target_group = 'test'" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "Bind対象への疎通状態を確認する。" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 2, 66 | "metadata": { 67 | "ExecuteTime": { 68 | "end_time": "2016-04-12T16:08:37.211114", 69 | "start_time": "2016-04-12T16:08:36.062517" 70 | }, 71 | "collapsed": false 72 | }, 73 | "outputs": [ 74 | { 75 | "name": "stdout", 76 | "output_type": "stream", 77 | "text": [ 78 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 79 | " \"changed\": false, \r\n", 80 | " \"ping\": \"pong\"\r\n", 81 | "}\u001b[0m\r\n" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "!ansible -m ping {target_group}" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "# セキュリティ設定タスクの実行\n", 94 | "\n", 95 | "セキュリティ設定はAnsible Roleとして定義している。(NII所内からのみ/登録ユーザのみアクセス可能なRepositoryに配置)\n", 96 | "\n", 97 | "http://xxxxxx/nii-security-setting" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "## Ansible用ファイルの準備\n", 105 | "\n", 106 | "運用上必要な標準的セキュリティ設定が整備されているという前提です。ここでは、NIIクラウド担当での管理作業手順を例示的に示しています。\n", 107 | "\n", 108 | "**具体的な設定の内容は参考です。**" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 3, 114 | "metadata": { 115 | "ExecuteTime": { 116 | "end_time": "2016-04-12T16:11:16.863204", 117 | "start_time": "2016-04-12T16:11:16.832931" 118 | }, 119 | "collapsed": false 120 | }, 121 | "outputs": [ 122 | { 123 | "data": { 124 | "text/plain": [ 125 | "'/tmp/tmpz1R2lo'" 126 | ] 127 | }, 128 | "execution_count": 3, 129 | "metadata": {}, 130 | "output_type": "execute_result" 131 | } 132 | ], 133 | "source": [ 134 | "import tempfile\n", 135 | "temp_dir = tempfile.mkdtemp()\n", 136 | "temp_dir" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 4, 142 | "metadata": { 143 | "ExecuteTime": { 144 | "end_time": "2016-04-12T16:11:36.123878", 145 | "start_time": "2016-04-12T16:11:35.252622" 146 | }, 147 | "collapsed": false 148 | }, 149 | "outputs": [ 150 | { 151 | "name": "stdout", 152 | "output_type": "stream", 153 | "text": [ 154 | "Cloning into '/tmp/tmpz1R2lo/roles/nii-security-setting'...\n", 155 | "remote: Counting objects: 27, done.\u001b[K\n", 156 | "remote: Compressing objects: 100% (24/24), done.\u001b[K\n", 157 | "remote: Total 27 (delta 2), reused 0 (delta 0)\u001b[K\n", 158 | "Receiving objects: 100% (27/27), 5.04 KiB | 0 bytes/s, done.\n", 159 | "Resolving deltas: 100% (2/2), done.\n", 160 | "Checking connectivity... done.\n", 161 | "/tmp/tmpz1R2lo\n", 162 | "└── roles\n", 163 | " └── nii-security-setting\n", 164 | " ├── files\n", 165 | " │   └── yazawa.keys\n", 166 | " ├── handlers\n", 167 | " │   ├── main.yml\n", 168 | " │   └── sshd.yml\n", 169 | " ├── tasks\n", 170 | " │   ├── login-users.yml\n", 171 | " │   ├── main.yml\n", 172 | " │   ├── sshd.yml\n", 173 | " │   └── sudoers.yml\n", 174 | " └── vars\n", 175 | " ├── CentOS.yml\n", 176 | " └── Ubuntu.yml\n", 177 | "\n", 178 | "6 directories, 15 files\n" 179 | ] 180 | } 181 | ], 182 | "source": [ 183 | "!mkdir {temp_dir}/roles\n", 184 | "!git clone ssh://git@xxxxxx/nii-security-setting.git \\\n", 185 | " {temp_dir}/roles/nii-security-setting\n", 186 | "!tree {temp_dir}" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "Gitからcloneしたroleをどの対象に適用するかはPlaybookにより記述する。" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 5, 199 | "metadata": { 200 | "ExecuteTime": { 201 | "end_time": "2016-04-12T16:14:17.306428", 202 | "start_time": "2016-04-12T16:14:17.125031" 203 | }, 204 | "collapsed": false 205 | }, 206 | "outputs": [ 207 | { 208 | "name": "stdout", 209 | "output_type": "stream", 210 | "text": [ 211 | "- hosts: test\r\n", 212 | " become: yes\r\n", 213 | " roles:\r\n", 214 | " - nii-security-setting" 215 | ] 216 | } 217 | ], 218 | "source": [ 219 | "import os\n", 220 | "with open(os.path.join(temp_dir, 'site.yml'), 'w') as f:\n", 221 | " f.write('''- hosts: {}\n", 222 | " become: yes\n", 223 | " roles:\n", 224 | " - nii-security-setting'''.format(target_group))\n", 225 | "!cat {temp_dir}/site.yml" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": 6, 231 | "metadata": { 232 | "ExecuteTime": { 233 | "end_time": "2016-04-12T16:16:52.109511", 234 | "start_time": "2016-04-12T16:16:51.936315" 235 | }, 236 | "collapsed": true 237 | }, 238 | "outputs": [], 239 | "source": [ 240 | "!mkdir {temp_dir}/group_vars" 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "セキュリティ設定は group_vars により与える。**環境に合わせて適切に設定すること!**" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 7, 253 | "metadata": { 254 | "ExecuteTime": { 255 | "end_time": "2016-04-12T16:28:55.006501", 256 | "start_time": "2016-04-12T16:28:54.993087" 257 | }, 258 | "collapsed": false 259 | }, 260 | "outputs": [ 261 | { 262 | "name": "stdout", 263 | "output_type": "stream", 264 | "text": [ 265 | "Writing /tmp/tmpz1R2lo/group_vars/test\n" 266 | ] 267 | } 268 | ], 269 | "source": [ 270 | "%%writefile {temp_dir}/group_vars/{target_group}\n", 271 | "login_users:\n", 272 | " - { name: yazawa, comment: \"Satoshi Yazawa\", key: \"yazawa.keys\" }\n", 273 | " \n", 274 | "sshd_allow_hosts:\n", 275 | " - \"XXX.XXX.XXX.0/24\"\n", 276 | " - \"XXX.XXX.XXX.0/24\"\n", 277 | " - \"XXX.XXX.XXX.0/23\"\n", 278 | " - \"XXX.XXX.XXX.0/24\"\n", 279 | " - \"XXX.XXX.XXX.106\"\n", 280 | " - \"XXX.XXX.XXX.110\"\n", 281 | " - \"XXX.XXX.XXX.192/26\"" 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "metadata": {}, 287 | "source": [ 288 | "## Ansibleの実行" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": {}, 294 | "source": [ 295 | "まずはCheck modeで動作確認してみる。" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": 8, 301 | "metadata": { 302 | "ExecuteTime": { 303 | "end_time": "2016-04-12T16:24:07.874771", 304 | "start_time": "2016-04-12T16:24:01.829058" 305 | }, 306 | "collapsed": false, 307 | "scrolled": true 308 | }, 309 | "outputs": [ 310 | { 311 | "name": "stdout", 312 | "output_type": "stream", 313 | "text": [ 314 | "Using /etc/ansible/ansible.cfg as config file\n", 315 | "\n", 316 | "PLAY [test] *********************************************************\n", 317 | "\n", 318 | "TASK [setup] *******************************************************************\n", 319 | "\u001b[0;32mok: [XXX.XXX.XXX.105]\u001b[0m\n", 320 | "\n", 321 | "TASK [nii-security-setting : Gather OS Specific Variables] *********************\n", 322 | "\u001b[0;32mok: [XXX.XXX.XXX.105] => (item=/tmp/tmpz1R2lo/roles/nii-security-setting/vars/CentOS.yml) => {\"ansible_facts\": {\"sshd_service\": \"sshd\"}, \"item\": \"/tmp/tmpz1R2lo/roles/nii-security-setting/vars/CentOS.yml\"}\u001b[0m\n", 323 | "\n", 324 | "TASK [nii-security-setting : include] ******************************************\n", 325 | "\u001b[0;36mincluded: /tmp/tmpz1R2lo/roles/nii-security-setting/tasks/sudoers.yml for XXX.XXX.XXX.105\u001b[0m\n", 326 | "\n", 327 | "TASK [nii-security-setting : Prepare /etc/sudoers.d] ***************************\n", 328 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => {\"changed\": true, \"gid\": 0, \"group\": \"root\", \"mode\": \"0750\", \"owner\": \"root\", \"path\": \"/etc/sudoers.d\", \"size\": 4096, \"state\": \"directory\", \"uid\": 0}\u001b[0m\n", 329 | "\u001b[0;31m--- before\n", 330 | "\u001b[0m\u001b[0;32m+++ after\n", 331 | "\u001b[0m\u001b[0;36m@@ -1,4 +1,4 @@\n", 332 | "\u001b[0m {\n", 333 | "\u001b[0;31m- \"mode\": \"0750\", \n", 334 | "\u001b[0m\u001b[0;32m+ \"mode\": \"0755\", \n", 335 | "\u001b[0m \"path\": \"/etc/sudoers.d\"\n", 336 | " }\n", 337 | "\n", 338 | "TASK [nii-security-setting : Add #includedir to /etc/sudoers] ******************\n", 339 | "\u001b[0;32mok: [XXX.XXX.XXX.105] => {\"backup\": \"\", \"changed\": false, \"msg\": \"\"}\u001b[0m\n", 340 | "\n", 341 | "TASK [nii-security-setting : include] ******************************************\n", 342 | "\u001b[0;36mincluded: /tmp/tmpz1R2lo/roles/nii-security-setting/tasks/login-users.yml for XXX.XXX.XXX.105\u001b[0m\n", 343 | "\n", 344 | "TASK [nii-security-setting : Add user] *****************************************\n", 345 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item={u'comment': u'Satoshi Yazawa', u'name': u'yazawa', u'key': u'yazawa.keys'}) => {\"changed\": true, \"item\": {\"comment\": \"Satoshi Yazawa\", \"key\": \"yazawa.keys\", \"name\": \"yazawa\"}}\u001b[0m\n", 346 | "\n", 347 | "TASK [nii-security-setting : Prepare authorized_keys] **************************\n", 348 | "\u001b[0;31mfailed: [XXX.XXX.XXX.105] (item={u'comment': u'Satoshi Yazawa', u'name': u'yazawa', u'key': u'yazawa.keys'}) => {\"failed\": true, \"item\": {\"comment\": \"Satoshi Yazawa\", \"key\": \"yazawa.keys\", \"name\": \"yazawa\"}, \"msg\": \"Either user must exist or you must provide full path to key file in check mode\"}\u001b[0m\n", 349 | "\tto retry, use: --limit @/tmp/tmpz1R2lo/site.retry\n", 350 | "\n", 351 | "PLAY RECAP *********************************************************************\n", 352 | "\u001b[0;31mXXX.XXX.XXX.105\u001b[0m : \u001b[0;32mok\u001b[0m\u001b[0;32m=\u001b[0m\u001b[0;32m7\u001b[0m \u001b[0;33mchanged\u001b[0m\u001b[0;33m=\u001b[0m\u001b[0;33m2\u001b[0m unreachable=0 \u001b[0;31mfailed\u001b[0m\u001b[0;31m=\u001b[0m\u001b[0;31m1\u001b[0m \n", 353 | "\n" 354 | ] 355 | } 356 | ], 357 | "source": [ 358 | "!ansible-playbook -CDv {temp_dir}/site.yml" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": {}, 364 | "source": [ 365 | "問題なさそう。適用する。" 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "execution_count": 9, 371 | "metadata": { 372 | "ExecuteTime": { 373 | "end_time": "2016-04-12T16:24:17.556852", 374 | "start_time": "2016-04-12T16:24:11.654809" 375 | }, 376 | "collapsed": false, 377 | "scrolled": true 378 | }, 379 | "outputs": [ 380 | { 381 | "name": "stdout", 382 | "output_type": "stream", 383 | "text": [ 384 | "\n", 385 | "PLAY [test] *********************************************************\n", 386 | "\n", 387 | "TASK [setup] *******************************************************************\n", 388 | "\u001b[0;32mok: [XXX.XXX.XXX.105]\u001b[0m\n", 389 | "\n", 390 | "TASK [nii-security-setting : Gather OS Specific Variables] *********************\n", 391 | "\u001b[0;32mok: [XXX.XXX.XXX.105] => (item=/tmp/tmpz1R2lo/roles/nii-security-setting/vars/CentOS.yml)\u001b[0m\n", 392 | "\n", 393 | "TASK [nii-security-setting : include] ******************************************\n", 394 | "\u001b[0;36mincluded: /tmp/tmpz1R2lo/roles/nii-security-setting/tasks/sudoers.yml for XXX.XXX.XXX.105\u001b[0m\n", 395 | "\n", 396 | "TASK [nii-security-setting : Prepare /etc/sudoers.d] ***************************\n", 397 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105]\u001b[0m\n", 398 | "\n", 399 | "TASK [nii-security-setting : Add #includedir to /etc/sudoers] ******************\n", 400 | "\u001b[0;32mok: [XXX.XXX.XXX.105]\u001b[0m\n", 401 | "\n", 402 | "TASK [nii-security-setting : include] ******************************************\n", 403 | "\u001b[0;36mincluded: /tmp/tmpz1R2lo/roles/nii-security-setting/tasks/login-users.yml for XXX.XXX.XXX.105\u001b[0m\n", 404 | "\n", 405 | "TASK [nii-security-setting : Add user] *****************************************\n", 406 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item={u'comment': u'Satoshi Yazawa', u'name': u'yazawa', u'key': u'yazawa.keys'})\u001b[0m\n", 407 | "\n", 408 | "TASK [nii-security-setting : Prepare authorized_keys] **************************\n", 409 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item={u'comment': u'Satoshi Yazawa', u'name': u'yazawa', u'key': u'yazawa.keys'})\u001b[0m\n", 410 | "\n", 411 | "TASK [nii-security-setting : Prepare sudoers.d/operators] **********************\n", 412 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item={u'comment': u'Satoshi Yazawa', u'name': u'yazawa', u'key': u'yazawa.keys'})\u001b[0m\n", 413 | "\n", 414 | "TASK [nii-security-setting : include] ******************************************\n", 415 | "\u001b[0;36mincluded: /tmp/tmpz1R2lo/roles/nii-security-setting/tasks/sshd.yml for XXX.XXX.XXX.105\u001b[0m\n", 416 | "\n", 417 | "TASK [nii-security-setting : Disable root SSH login] ***************************\n", 418 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105]\u001b[0m\n", 419 | "\n", 420 | "TASK [nii-security-setting : Set AllowUsers] ***********************************\n", 421 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item=XXX.XXX.XXX.0/24)\u001b[0m\n", 422 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item=XXX.XXX.XXX.0/24)\u001b[0m\n", 423 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item=XXX.XXX.XXX.0/23)\u001b[0m\n", 424 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item=XXX.XXX.XXX.0/24)\u001b[0m\n", 425 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item=XXX.XXX.XXX.106)\u001b[0m\n", 426 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item=XXX.XXX.XXX.110)\u001b[0m\n", 427 | "\u001b[0;33mchanged: [XXX.XXX.XXX.105] => (item=XXX.XXX.XXX.192/26)\u001b[0m\n", 428 | "\n", 429 | "PLAY RECAP *********************************************************************\n", 430 | "\u001b[0;33mXXX.XXX.XXX.105\u001b[0m : \u001b[0;32mok\u001b[0m\u001b[0;32m=\u001b[0m\u001b[0;32m12\u001b[0m \u001b[0;33mchanged\u001b[0m\u001b[0;33m=\u001b[0m\u001b[0;33m6\u001b[0m unreachable=0 failed=0 \n", 431 | "\n" 432 | ] 433 | } 434 | ], 435 | "source": [ 436 | "!ansible-playbook {temp_dir}/site.yml" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": { 442 | "ExecuteTime": { 443 | "end_time": "2016-04-08T16:29:42.708319", 444 | "start_time": "2016-04-08T16:29:42.695868" 445 | }, 446 | "collapsed": true 447 | }, 448 | "source": [ 449 | "SSHでアクセスできることを確認し、クローズとする。" 450 | ] 451 | }, 452 | { 453 | "cell_type": "markdown", 454 | "metadata": { 455 | "collapsed": true 456 | }, 457 | "source": [ 458 | "# 後始末\n", 459 | "\n", 460 | "一時ディレクトリを削除する。" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": 10, 466 | "metadata": { 467 | "ExecuteTime": { 468 | "end_time": "2016-04-12T16:29:28.868947", 469 | "start_time": "2016-04-12T16:29:28.696544" 470 | }, 471 | "collapsed": true 472 | }, 473 | "outputs": [], 474 | "source": [ 475 | "!rm -fr {temp_dir}" 476 | ] 477 | }, 478 | { 479 | "cell_type": "code", 480 | "execution_count": null, 481 | "metadata": { 482 | "collapsed": true 483 | }, 484 | "outputs": [], 485 | "source": [] 486 | } 487 | ], 488 | "metadata": { 489 | "kernelspec": { 490 | "display_name": "Python 2", 491 | "language": "python", 492 | "name": "python2" 493 | }, 494 | "language_info": { 495 | "codemirror_mode": { 496 | "name": "ipython", 497 | "version": 2 498 | }, 499 | "file_extension": ".py", 500 | "mimetype": "text/x-python", 501 | "name": "python", 502 | "nbconvert_exporter": "python", 503 | "pygments_lexer": "ipython2", 504 | "version": "2.7.12" 505 | }, 506 | "toc": { 507 | "toc_cell": false, 508 | "toc_number_sections": true, 509 | "toc_threshold": 6, 510 | "toc_window_display": false 511 | } 512 | }, 513 | "nbformat": 4, 514 | "nbformat_minor": 0 515 | } 516 | -------------------------------------------------------------------------------- /D01_GCE - Set! Go! (Google Compute Engine).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: GCE - Set! Go! (Google Compute Engine)\n", 8 | "\n", 9 | "---\n", 10 | "\n", 11 | "Get and set a virtual machine, aka compute instance of \"Google Compute Engine\" with specified machine type and OS image. *[Here are just set and go becasue GCE is always ready for you..]*\n", 12 | "\n", 13 | "Google Compute Engine上で仮想マシンの確保する。" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## *Operation Note*\n", 21 | "\n", 22 | "*This is a cell for your own recording. ここに経緯を記述*" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "# 動作環境の確認\n", 30 | "\n", 31 | "このNotebookは、 [Google Python Client Library](https://github.com/google/google-api-python-client) を使ってマシンの確保を行います。\n", 32 | "\n", 33 | "このNotebook環境にGoogle Python Client Libraryがインストールされている必要があります。インストールされていない場合は、以下のセル実行に失敗し、 `ImportError` となります。" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 1, 39 | "metadata": { 40 | "collapsed": false 41 | }, 42 | "outputs": [], 43 | "source": [ 44 | "from googleapiclient import discovery" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "もし、インストールされていない場合は、[Using the Python Client Library](https://cloud.google.com/compute/docs/tutorials/python-guide)を参考にインストールしてください。" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 2, 57 | "metadata": { 58 | "collapsed": false, 59 | "scrolled": true 60 | }, 61 | "outputs": [], 62 | "source": [ 63 | "#!pip2 install --upgrade google-api-python-client\n", 64 | "#from googleapiclient import discovery" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "# 設定の定義\n", 72 | "\n", 73 | "どのようなマシンを確保するか?を定義していく。" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "## Credentialの指定\n", 81 | "\n", 82 | "Google Compute EngineにアクセスするためのCredentialを指示する。\n", 83 | "\n", 84 | "- JSONフォーマットのService Account情報\n", 85 | "- プロジェクトID\n", 86 | "\n", 87 | "を用意しておく。" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 3, 93 | "metadata": { 94 | "collapsed": true 95 | }, 96 | "outputs": [], 97 | "source": [ 98 | "creds = '~/.keys/xxxxxxxx.json'\n", 99 | "target_project_id = 'my_project_id'" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "computeサービスを取得しておく。" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 4, 112 | "metadata": { 113 | "collapsed": false 114 | }, 115 | "outputs": [ 116 | { 117 | "data": { 118 | "text/plain": [ 119 | "" 120 | ] 121 | }, 122 | "execution_count": 4, 123 | "metadata": {}, 124 | "output_type": "execute_result" 125 | } 126 | ], 127 | "source": [ 128 | "import os\n", 129 | "from oauth2client.client import GoogleCredentials\n", 130 | "\n", 131 | "credentials = GoogleCredentials.from_stream(os.path.expanduser(creds))\n", 132 | "compute = discovery.build('compute', 'v1', credentials=credentials)\n", 133 | "compute" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "## ゾーンの設定\n", 141 | "\n", 142 | "どのZoneにインスタンスを確保するかを定義しておく。" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 5, 148 | "metadata": { 149 | "collapsed": true 150 | }, 151 | "outputs": [], 152 | "source": [ 153 | "target_zone = 'us-central1-f'" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "## マシン名の決定\n", 161 | "\n", 162 | "マシン名を決める。まず、現在のインスタンス名の一覧を確認する。" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 6, 168 | "metadata": { 169 | "collapsed": false 170 | }, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/plain": [ 175 | "[]" 176 | ] 177 | }, 178 | "execution_count": 6, 179 | "metadata": {}, 180 | "output_type": "execute_result" 181 | } 182 | ], 183 | "source": [ 184 | "instances = compute.instances().list(zone=target_zone, project=target_project_id).execute()\n", 185 | "map(lambda i: i['name'], instances['items'] if 'items' in instances else [])" 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "metadata": {}, 191 | "source": [ 192 | "すでにあるインスタンスとは重複しないような名前を設定する。" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 7, 198 | "metadata": { 199 | "collapsed": true 200 | }, 201 | "outputs": [], 202 | "source": [ 203 | "machine_name = 'test-gce'" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "## マシンタイプの指定\n", 211 | "\n", 212 | "まず、このZoneで利用可能なMachine Typeを取得する。" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 8, 218 | "metadata": { 219 | "collapsed": false 220 | }, 221 | "outputs": [ 222 | { 223 | "data": { 224 | "text/plain": [ 225 | "[(u'f1-micro', u'1 vCPU (shared physical core) and 0.6 GB RAM'),\n", 226 | " (u'g1-small', u'1 vCPU (shared physical core) and 1.7 GB RAM'),\n", 227 | " (u'n1-highcpu-16', u'16 vCPUs, 14.4 GB RAM'),\n", 228 | " (u'n1-highcpu-2', u'2 vCPUs, 1.8 GB RAM'),\n", 229 | " (u'n1-highcpu-32', u'32 vCPUs, 28.8 GB RAM'),\n", 230 | " (u'n1-highcpu-4', u'4 vCPUs, 3.6 GB RAM'),\n", 231 | " (u'n1-highcpu-8', u'8 vCPUs, 7.2 GB RAM'),\n", 232 | " (u'n1-highmem-16', u'16 vCPUs, 104 GB RAM'),\n", 233 | " (u'n1-highmem-2', u'2 vCPUs, 13 GB RAM'),\n", 234 | " (u'n1-highmem-32', u'32 vCPUs, 208 GB RAM'),\n", 235 | " (u'n1-highmem-4', u'4 vCPUs, 26 GB RAM'),\n", 236 | " (u'n1-highmem-8', u'8 vCPUs, 52 GB RAM'),\n", 237 | " (u'n1-standard-1', u'1 vCPU, 3.75 GB RAM'),\n", 238 | " (u'n1-standard-16', u'16 vCPUs, 60 GB RAM'),\n", 239 | " (u'n1-standard-2', u'2 vCPUs, 7.5 GB RAM'),\n", 240 | " (u'n1-standard-32', u'32 vCPUs, 120 GB RAM'),\n", 241 | " (u'n1-standard-4', u'4 vCPUs, 15 GB RAM'),\n", 242 | " (u'n1-standard-8', u'8 vCPUs, 30 GB RAM')]" 243 | ] 244 | }, 245 | "execution_count": 8, 246 | "metadata": {}, 247 | "output_type": "execute_result" 248 | } 249 | ], 250 | "source": [ 251 | "machineTypes = compute.machineTypes().list(project=target_project_id, zone=target_zone).execute()['items']\n", 252 | "map(lambda t: (t['name'], t['description']), machineTypes)" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "利用したいマシンタイプ名を設定する。" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 9, 265 | "metadata": { 266 | "collapsed": true 267 | }, 268 | "outputs": [], 269 | "source": [ 270 | "machine_type = 'f1-micro'" 271 | ] 272 | }, 273 | { 274 | "cell_type": "markdown", 275 | "metadata": {}, 276 | "source": [ 277 | "## イメージの指定\n", 278 | "\n", 279 | "イメージの一覧を確認する。`project`には、利用したいイメージの所属プロジェクトを指定する。" 280 | ] 281 | }, 282 | { 283 | "cell_type": "code", 284 | "execution_count": 10, 285 | "metadata": { 286 | "collapsed": false 287 | }, 288 | "outputs": [ 289 | { 290 | "data": { 291 | "text/plain": [ 292 | "[(u'ubuntu-1204-precise-v20160610a',\n", 293 | " u'https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1204-precise-v20160610a'),\n", 294 | " (u'ubuntu-1404-trusty-v20160610',\n", 295 | " u'https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20160610'),\n", 296 | " (u'ubuntu-1510-wily-v20160610',\n", 297 | " u'https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1510-wily-v20160610'),\n", 298 | " (u'ubuntu-1604-xenial-v20160610',\n", 299 | " u'https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1604-xenial-v20160610')]" 300 | ] 301 | }, 302 | "execution_count": 10, 303 | "metadata": {}, 304 | "output_type": "execute_result" 305 | } 306 | ], 307 | "source": [ 308 | "images = compute.images().list(project='ubuntu-os-cloud').execute()['items']\n", 309 | "images = filter(lambda i: i['status'] == 'READY' and 'deprecated' not in i, images)\n", 310 | "map(lambda i: (i['name'], i['selfLink']), images)" 311 | ] 312 | }, 313 | { 314 | "cell_type": "markdown", 315 | "metadata": {}, 316 | "source": [ 317 | "利用したいイメージのURLを設定する。" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": 11, 323 | "metadata": { 324 | "collapsed": true 325 | }, 326 | "outputs": [], 327 | "source": [ 328 | "source_disk_image = 'https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20160610'" 329 | ] 330 | }, 331 | { 332 | "cell_type": "markdown", 333 | "metadata": {}, 334 | "source": [ 335 | "## キーペアの設定\n", 336 | "\n", 337 | "現在のSSHキーの一覧を取得する。" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 12, 343 | "metadata": { 344 | "collapsed": false 345 | }, 346 | "outputs": [ 347 | { 348 | "data": { 349 | "text/plain": [ 350 | "[u'ansible:ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ansible@XXXXXXXXXXXX',\n", 351 | " u'']" 352 | ] 353 | }, 354 | "execution_count": 12, 355 | "metadata": {}, 356 | "output_type": "execute_result" 357 | } 358 | ], 359 | "source": [ 360 | "projectMetadata = compute.projects().get(project=target_project_id).execute()\n", 361 | "currentSSHKeys = filter(lambda metadata: metadata['key'] == 'sshKeys', projectMetadata['commonInstanceMetadata']['items']) \\\n", 362 | " if 'commonInstanceMetadata' in projectMetadata and 'items' in projectMetadata['commonInstanceMetadata'] else []\n", 363 | "currentSSHKeys = currentSSHKeys[0]['value'].split('\\n') if currentSSHKeys else []\n", 364 | "currentSSHKeys" 365 | ] 366 | }, 367 | { 368 | "cell_type": "markdown", 369 | "metadata": {}, 370 | "source": [ 371 | "SSHのキー一覧にこのNotebook環境のキーがなければ、追加する。" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": 13, 377 | "metadata": { 378 | "collapsed": false 379 | }, 380 | "outputs": [ 381 | { 382 | "data": { 383 | "text/plain": [ 384 | "[u'ansible:ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ansible@XXXXXXXXXXXX',\n", 385 | " u'']" 386 | ] 387 | }, 388 | "execution_count": 13, 389 | "metadata": {}, 390 | "output_type": "execute_result" 391 | } 392 | ], 393 | "source": [ 394 | "pub_key = None\n", 395 | "with open(os.path.expanduser('~/.ssh/ansible_id_rsa.pub'), 'r') as f:\n", 396 | " pub_key = f.readlines()[0].strip()\n", 397 | "\n", 398 | "if not filter(lambda k: k.endswith(pub_key), currentSSHKeys):\n", 399 | " currentSSHKeys.append('ansible:' + pub_key)\n", 400 | "currentSSHKeys" 401 | ] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": {}, 406 | "source": [ 407 | "Metadataに反映する。" 408 | ] 409 | }, 410 | { 411 | "cell_type": "code", 412 | "execution_count": 14, 413 | "metadata": { 414 | "collapsed": false 415 | }, 416 | "outputs": [ 417 | { 418 | "data": { 419 | "text/plain": [ 420 | "{u'id': u'8047257766995800862',\n", 421 | " u'insertTime': u'2016-06-17T07:49:21.362-07:00',\n", 422 | " u'kind': u'compute#operation',\n", 423 | " u'name': u'operation-1466174961053-5357a75bb6d49-9190a416-d7c15055',\n", 424 | " u'operationType': u'setMetadata',\n", 425 | " u'progress': 0,\n", 426 | " u'selfLink': u'https://www.googleapis.com/compute/v1/projects/my_project_id/global/operations/operation-1466174961053-5357a75bb6d49-9190a416-d7c15055',\n", 427 | " u'status': u'PENDING',\n", 428 | " u'targetId': u'201294258281',\n", 429 | " u'targetLink': u'https://www.googleapis.com/compute/v1/projects/my_project_id',\n", 430 | " u'user': u'me@developer.gserviceaccount.com'}" 431 | ] 432 | }, 433 | "execution_count": 14, 434 | "metadata": {}, 435 | "output_type": "execute_result" 436 | } 437 | ], 438 | "source": [ 439 | "compute.projects().setCommonInstanceMetadata(project=target_project_id,\n", 440 | " body={'items': [{'key': 'sshKeys',\n", 441 | " 'value': '\\n'.join(currentSSHKeys)}]}).execute()" 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "metadata": {}, 447 | "source": [ 448 | "# マシンの確保" 449 | ] 450 | }, 451 | { 452 | "cell_type": "markdown", 453 | "metadata": {}, 454 | "source": [ 455 | "## インスタンスの起動\n", 456 | "\n", 457 | "設定した情報を用いてマシンを確保する。" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": 15, 463 | "metadata": { 464 | "collapsed": false 465 | }, 466 | "outputs": [ 467 | { 468 | "data": { 469 | "text/plain": [ 470 | "{u'id': u'6984323738569912558',\n", 471 | " u'insertTime': u'2016-06-17T07:49:37.670-07:00',\n", 472 | " u'kind': u'compute#operation',\n", 473 | " u'name': u'operation-1466174976616-5357a76a8e641-b2fbf6a0-940630b9',\n", 474 | " u'operationType': u'insert',\n", 475 | " u'progress': 0,\n", 476 | " u'selfLink': u'https://www.googleapis.com/compute/v1/projects/my_project_id/zones/us-central1-f/operations/operation-1466174976616-5357a76a8e641-b2fbf6a0-940630b9',\n", 477 | " u'status': u'PENDING',\n", 478 | " u'targetId': u'7960517509428916462',\n", 479 | " u'targetLink': u'https://www.googleapis.com/compute/v1/projects/my_project_id/zones/us-central1-f/instances/test-gce',\n", 480 | " u'user': u'me@developer.gserviceaccount.com',\n", 481 | " u'zone': u'https://www.googleapis.com/compute/v1/projects/my_project_id/zones/us-central1-f'}" 482 | ] 483 | }, 484 | "execution_count": 15, 485 | "metadata": {}, 486 | "output_type": "execute_result" 487 | } 488 | ], 489 | "source": [ 490 | "config = {\n", 491 | " 'name': machine_name,\n", 492 | " 'machineType': \"zones/{}/machineTypes/{}\".format(target_zone, machine_type),\n", 493 | " 'disks': [\n", 494 | " {\n", 495 | " 'boot': True,\n", 496 | " 'autoDelete': True,\n", 497 | " 'initializeParams': {\n", 498 | " 'sourceImage': source_disk_image,\n", 499 | " }\n", 500 | " }\n", 501 | " ],\n", 502 | " 'networkInterfaces': [{\n", 503 | " 'network': 'global/networks/default',\n", 504 | " 'accessConfigs': [\n", 505 | " {'type': 'ONE_TO_ONE_NAT', 'name': 'External NAT'}\n", 506 | " ]\n", 507 | " }],\n", 508 | " 'serviceAccounts': [{\n", 509 | " 'email': 'default',\n", 510 | " 'scopes': [\n", 511 | " 'https://www.googleapis.com/auth/devstorage.read_write',\n", 512 | " 'https://www.googleapis.com/auth/logging.write'\n", 513 | " ]\n", 514 | " }],\n", 515 | " 'metadata': {\n", 516 | " 'items': []\n", 517 | " }\n", 518 | " }\n", 519 | "\n", 520 | "new_vm = compute.instances().insert(project=target_project_id, zone=target_zone, body=config).execute()\n", 521 | "new_vm" 522 | ] 523 | }, 524 | { 525 | "cell_type": "markdown", 526 | "metadata": {}, 527 | "source": [ 528 | "インスタンスがRunningになるまで待つ。" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": 17, 534 | "metadata": { 535 | "collapsed": false 536 | }, 537 | "outputs": [], 538 | "source": [ 539 | "import time\n", 540 | "status = compute.instances().get(project=target_project_id, zone=target_zone,\n", 541 | " instance=machine_name).execute()['status']\n", 542 | "while status == 'PROVISIONING' or status == 'STAGING':\n", 543 | " time.sleep(30)\n", 544 | " status = compute.instances().get(project=target_project_id, zone=target_zone,\n", 545 | " instance=machine_name).execute()['status']\n", 546 | "assert(status == 'RUNNING')" 547 | ] 548 | }, 549 | { 550 | "cell_type": "markdown", 551 | "metadata": {}, 552 | "source": [ 553 | "## IPアドレスの取得" 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "execution_count": 18, 559 | "metadata": { 560 | "collapsed": false 561 | }, 562 | "outputs": [ 563 | { 564 | "data": { 565 | "text/plain": [ 566 | "u'XXX.XXX.XXX.59'" 567 | ] 568 | }, 569 | "execution_count": 18, 570 | "metadata": {}, 571 | "output_type": "execute_result" 572 | } 573 | ], 574 | "source": [ 575 | "vm_desc = compute.instances().get(project=target_project_id, zone=target_zone, instance=machine_name).execute()\n", 576 | "ip_address = vm_desc['networkInterfaces'][0]['accessConfigs'][0]['natIP']\n", 577 | "ip_address" 578 | ] 579 | }, 580 | { 581 | "cell_type": "markdown", 582 | "metadata": {}, 583 | "source": [ 584 | "pingが通ることを確認。" 585 | ] 586 | }, 587 | { 588 | "cell_type": "code", 589 | "execution_count": 21, 590 | "metadata": { 591 | "collapsed": false 592 | }, 593 | "outputs": [ 594 | { 595 | "name": "stdout", 596 | "output_type": "stream", 597 | "text": [ 598 | "PING XXX.XXX.XXX.59 (XXX.XXX.XXX.59) 56(84) bytes of data.\n", 599 | "64 bytes from XXX.XXX.XXX.59: icmp_seq=1 ttl=43 time=133 ms\n", 600 | "64 bytes from XXX.XXX.XXX.59: icmp_seq=2 ttl=43 time=132 ms\n", 601 | "64 bytes from XXX.XXX.XXX.59: icmp_seq=3 ttl=43 time=132 ms\n", 602 | "64 bytes from XXX.XXX.XXX.59: icmp_seq=4 ttl=43 time=132 ms\n", 603 | "\n", 604 | "--- XXX.XXX.XXX.59 ping statistics ---\n", 605 | "4 packets transmitted, 4 received, 0% packet loss, time 3002ms\n", 606 | "rtt min/avg/max/mdev = 132.871/133.058/133.458/0.233 ms\n" 607 | ] 608 | } 609 | ], 610 | "source": [ 611 | "!ping -c 4 {ip_address}" 612 | ] 613 | }, 614 | { 615 | "cell_type": "markdown", 616 | "metadata": { 617 | "collapsed": true 618 | }, 619 | "source": [ 620 | "## Ansibleから操作可能であることを確認\n", 621 | "\n", 622 | "[キーペアの設定](#キーペアの設定-3.6)でansibleユーザとしてこの環境の公開鍵をInjectionしている。" 623 | ] 624 | }, 625 | { 626 | "cell_type": "code", 627 | "execution_count": 22, 628 | "metadata": { 629 | "collapsed": false 630 | }, 631 | "outputs": [ 632 | { 633 | "data": { 634 | "text/plain": [ 635 | "'/tmp/tmp9BOSSw'" 636 | ] 637 | }, 638 | "execution_count": 22, 639 | "metadata": {}, 640 | "output_type": "execute_result" 641 | } 642 | ], 643 | "source": [ 644 | "import tempfile\n", 645 | "work_dir = tempfile.mkdtemp()\n", 646 | "work_dir" 647 | ] 648 | }, 649 | { 650 | "cell_type": "code", 651 | "execution_count": 23, 652 | "metadata": { 653 | "collapsed": false 654 | }, 655 | "outputs": [ 656 | { 657 | "name": "stdout", 658 | "output_type": "stream", 659 | "text": [ 660 | "XXX.XXX.XXX.59\r\n" 661 | ] 662 | } 663 | ], 664 | "source": [ 665 | "import os\n", 666 | "snapshot_hosts = os.path.join(work_dir, 'hosts')\n", 667 | "with open(snapshot_hosts, 'w') as f:\n", 668 | " f.write('{address}\\n'.format(address=ip_address)) \n", 669 | "!cat { snapshot_hosts }" 670 | ] 671 | }, 672 | { 673 | "cell_type": "markdown", 674 | "metadata": {}, 675 | "source": [ 676 | "Ansible経由でansibleユーザとしてSSHできることを確認する。\n", 677 | "\n", 678 | "> インスタンス起動直後の場合、UNREACHABLEとなるおそれがある。その場合は再度実行すること。" 679 | ] 680 | }, 681 | { 682 | "cell_type": "code", 683 | "execution_count": 24, 684 | "metadata": { 685 | "collapsed": false 686 | }, 687 | "outputs": [ 688 | { 689 | "name": "stdout", 690 | "output_type": "stream", 691 | "text": [ 692 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS | rc=0 >>\r\n", 693 | "ansible\r\n", 694 | "\u001b[0m\r\n" 695 | ] 696 | } 697 | ], 698 | "source": [ 699 | "!ansible -a 'whoami' -i { snapshot_hosts } all" 700 | ] 701 | }, 702 | { 703 | "cell_type": "markdown", 704 | "metadata": {}, 705 | "source": [ 706 | "# Inventoryの更新\n", 707 | "\n", 708 | "Inventoryに、作成したマシンのIPアドレスを追加する。変更する前に、現在の内容をコピーしておく。" 709 | ] 710 | }, 711 | { 712 | "cell_type": "code", 713 | "execution_count": 25, 714 | "metadata": { 715 | "collapsed": true 716 | }, 717 | "outputs": [], 718 | "source": [ 719 | "!cp inventory {work_dir}/inventory-old" 720 | ] 721 | }, 722 | { 723 | "cell_type": "markdown", 724 | "metadata": {}, 725 | "source": [ 726 | "[Inventory](../edit/inventory) を修正する。" 727 | ] 728 | }, 729 | { 730 | "cell_type": "code", 731 | "execution_count": 26, 732 | "metadata": { 733 | "collapsed": false 734 | }, 735 | "outputs": [ 736 | { 737 | "name": "stdout", 738 | "output_type": "stream", 739 | "text": [ 740 | "--- /tmp/tmp9BOSSw/inventory-old\t2016-06-17 23:52:39.873366095 +0900\r\n", 741 | "+++ inventory\t2016-06-17 23:52:56.545503890 +0900\r\n", 742 | "@@ -3,3 +3,6 @@\r\n", 743 | " \r\n", 744 | " [test-vm]\r\n", 745 | " XXX.XXX.XXX.66\r\n", 746 | "+\r\n", 747 | "+[test-gce]\r\n", 748 | "+XXX.XXX.XXX.59\r\n" 749 | ] 750 | } 751 | ], 752 | "source": [ 753 | "!diff -ur {work_dir}/inventory-old inventory" 754 | ] 755 | }, 756 | { 757 | "cell_type": "markdown", 758 | "metadata": {}, 759 | "source": [ 760 | "追加したグループ名で、ansibleのpingモジュールが実行可能かどうかを確認する。" 761 | ] 762 | }, 763 | { 764 | "cell_type": "code", 765 | "execution_count": 27, 766 | "metadata": { 767 | "collapsed": true 768 | }, 769 | "outputs": [], 770 | "source": [ 771 | "target_group = 'test-gce'" 772 | ] 773 | }, 774 | { 775 | "cell_type": "code", 776 | "execution_count": 28, 777 | "metadata": { 778 | "collapsed": false 779 | }, 780 | "outputs": [ 781 | { 782 | "name": "stdout", 783 | "output_type": "stream", 784 | "text": [ 785 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS => {\r\n", 786 | " \"changed\": false, \r\n", 787 | " \"ping\": \"pong\"\r\n", 788 | "}\u001b[0m\r\n" 789 | ] 790 | } 791 | ], 792 | "source": [ 793 | "!ansible -m ping {target_group}" 794 | ] 795 | }, 796 | { 797 | "cell_type": "markdown", 798 | "metadata": {}, 799 | "source": [ 800 | "特にエラーとならなければOK。これで完了とする。" 801 | ] 802 | }, 803 | { 804 | "cell_type": "markdown", 805 | "metadata": {}, 806 | "source": [ 807 | "# 後始末\n", 808 | "\n", 809 | "一時ディレクトリを削除する。" 810 | ] 811 | }, 812 | { 813 | "cell_type": "code", 814 | "execution_count": 29, 815 | "metadata": { 816 | "collapsed": true 817 | }, 818 | "outputs": [], 819 | "source": [ 820 | "!rm -fr {work_dir}" 821 | ] 822 | }, 823 | { 824 | "cell_type": "code", 825 | "execution_count": null, 826 | "metadata": { 827 | "collapsed": true 828 | }, 829 | "outputs": [], 830 | "source": [] 831 | } 832 | ], 833 | "metadata": { 834 | "kernelspec": { 835 | "display_name": "Python 2", 836 | "language": "python", 837 | "name": "python2" 838 | }, 839 | "language_info": { 840 | "codemirror_mode": { 841 | "name": "ipython", 842 | "version": 2 843 | }, 844 | "file_extension": ".py", 845 | "mimetype": "text/x-python", 846 | "name": "python", 847 | "nbconvert_exporter": "python", 848 | "pygments_lexer": "ipython2", 849 | "version": "2.7.12" 850 | }, 851 | "toc": { 852 | "toc_cell": false, 853 | "toc_number_sections": true, 854 | "toc_threshold": 6, 855 | "toc_window_display": false 856 | } 857 | }, 858 | "nbformat": 4, 859 | "nbformat_minor": 0 860 | } 861 | -------------------------------------------------------------------------------- /D00_Prerequisits for Literate Computing via Notebooks.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: Prerequisits for Literate Computing via Notebooks\n", 8 | "文芸的機械化の準備 - Notebookでね\n", 9 | "\n", 10 | "----\n", 11 | "\n", 12 | "This notebook describes enviromental prerequisits in order to utilize the set of notebooks under this directory.\n", 13 | "Prerequisits cover both for the originate host where notebooks operate and for the target hosts manipulated by notebooks.\n", 14 | "\n", 15 | "ここでは本ディレクトリに含まれるNotebookを利用するために必要となる環境条件を明示的に定義している。\n", 16 | "環境条件には、Notebookを動作させているホストマシンの環境、およびNotebookで操作しようとしている対象ホスト群の環境が含まれる。\n", 17 | "\n", 18 | "----\n", 19 | "\n", 20 | "自身のNotebook環境にNotebookをコピーした後、読み進めることで条件を満たしているか確認できる。\n", 21 | "\n", 22 | "条件には推奨の度合いによりレベルがあり、 **A=必須、B=推奨、C=任意** というレベルを設定してある。\n", 23 | "\n", 24 | "このNotebookを確認することで、Notebookで操作しようとしている捜査対象ホスト群が **Seedingが済んだ状態である** ことを確認できる。" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "# Ansibleの設定\n", 32 | "\n", 33 | "Notebook環境は、以下の条件を満たしている必要がある。\n", 34 | "\n", 35 | "## Ansibleがインストールされていること - A\n", 36 | "\n", 37 | "以下のコマンド実行がエラーとならず、インストールされているAnsibleのバージョンが表示されること。" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 1, 43 | "metadata": { 44 | "collapsed": false 45 | }, 46 | "outputs": [ 47 | { 48 | "name": "stdout", 49 | "output_type": "stream", 50 | "text": [ 51 | "ansible 2.1.0.0\r\n", 52 | " config file = /etc/ansible/ansible.cfg\r\n", 53 | " configured module search path = Default w/o overrides\r\n" 54 | ] 55 | } 56 | ], 57 | "source": [ 58 | "!ansible --version" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "ansibleコマンドがインストールされていない場合は、[Installing the Control Machine](http://docs.ansible.com/ansible/intro_installation.html#installing-the-control-machine)を参考に、Ansibleをインストールする。" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "## AnsibleでDefault Moduleが利用可能であること - A\n", 73 | "\n", 74 | "DefaultのModuleが使えればよい。特にカスタマイズすべき項目はなし。*...将来は腐っているモジュールは差し替えたい。*" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "# Ansible - Binding対象間の設定\n", 82 | "\n", 83 | "このNotebook環境にインストールされているAnsibleは、Binding対象となるホストに対して通信、操作可能な状態になっている必要がある。" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "## Default Inventoryが設定されていること - B\n", 91 | "\n", 92 | "以下のコマンド実行がエラーとならず、 `inventory = (Default Inventoryのパス)` が設定されていること。\n", 93 | "\n", 94 | "> これを指定しないと、運用Notebookのansible実行ごとにInventoryを指定する必要がある・・・あまりNotebookには **Notebookが動作する環境に依存した項目を書きたくない** ので必須としている。" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 2, 100 | "metadata": { 101 | "collapsed": false 102 | }, 103 | "outputs": [ 104 | { 105 | "name": "stdout", 106 | "output_type": "stream", 107 | "text": [ 108 | "private_key_file = /root/.ssh/ansible_id_rsa\r\n", 109 | "ansible_ssh_user = ansible\r\n", 110 | "\u001b[01;31m\u001b[Kinventory\u001b[m\u001b[K = /notebooks/inventory\r\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "!cat ${ANSIBLE_CONFIG:-/etc/ansible/ansible.cfg} | grep --color=always -A 2 -B 2 \"^inventory\"" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": {}, 121 | "source": [ 122 | "[Configuration file](http://docs.ansible.com/ansible/intro_configuration.html) を参考にしながら、Default Inventoryを指定しておく。Default Inventoryを指定しない場合は、対象とのBindingの際にInventoryの指定を加えること。" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "## Inventoryに指定されたホストに到達可能なこと - A\n", 130 | "\n", 131 | "AnsibleのInventoryに指定された全ホストに対して、この環境からアクセス可能であること。\n", 132 | "\n", 133 | "- 以下のコマンド実行がエラーもしくは警告(`provided hosts list is empty`)とならないこと\n", 134 | "- Inventoryに登録されているホストに関して、すべてSUCCESSとなること" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 3, 140 | "metadata": { 141 | "collapsed": false, 142 | "scrolled": true 143 | }, 144 | "outputs": [ 145 | { 146 | "name": "stdout", 147 | "output_type": "stream", 148 | "text": [ 149 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS => {\n", 150 | " \"changed\": false, \n", 151 | " \"ping\": \"pong\"\n", 152 | "}\u001b[0m\n", 153 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS => {\n", 154 | " \"changed\": false, \n", 155 | " \"ping\": \"pong\"\n", 156 | "}\u001b[0m\n", 157 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS => {\n", 158 | " \"changed\": false, \n", 159 | " \"ping\": \"pong\"\n", 160 | "}\u001b[0m\n" 161 | ] 162 | } 163 | ], 164 | "source": [ 165 | "!ansible -m ping all" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "このNotebook環境のAnsibleから、ホストへの認証方法については特に問わない。\n", 173 | "\n", 174 | "> 我々(NIIクラウド運用チーム)の環境ではkeypairによる認証を使ってAnsibleから各ホストにアクセスをしている。\n", 175 | "> そのため、以下のpublic keyをBinding対象ホストの `~/.ssh/authorized_keys` に設定している。" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 4, 181 | "metadata": { 182 | "collapsed": false 183 | }, 184 | "outputs": [ 185 | { 186 | "name": "stdout", 187 | "output_type": "stream", 188 | "text": [ 189 | "ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ansible@XXXXXXXXXXXX\r\n" 190 | ] 191 | } 192 | ], 193 | "source": [ 194 | "!cat ~/.ssh/ansible_id_rsa.pub" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": {}, 200 | "source": [ 201 | "## 適切なユーザで認証されること - B\n", 202 | "\n", 203 | "Ansibleからホストへの認証は、一般ユーザとして認証されるようにしておく。\n", 204 | "\n", 205 | "> 我々(NIIクラウド運用チーム)の環境では、ユーザ ansible を作って対応している。" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 5, 211 | "metadata": { 212 | "collapsed": false 213 | }, 214 | "outputs": [ 215 | { 216 | "name": "stdout", 217 | "output_type": "stream", 218 | "text": [ 219 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS | rc=0 >>\n", 220 | "ansible\n", 221 | "\u001b[0m\n", 222 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 223 | "ansible\n", 224 | "\u001b[0m\n", 225 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS | rc=0 >>\n", 226 | "ansible\n", 227 | "\u001b[0m\n" 228 | ] 229 | } 230 | ], 231 | "source": [ 232 | "!ansible -a 'whoami' all" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "## Binding対象ホストにてsudo可能であること - A\n", 240 | "\n", 241 | "Inventoryに記述された全ホストに関してbecome, become-userが可能であることを確認しておく。\n", 242 | "\n", 243 | "- 以下のコマンド実行がエラーとならないこと\n", 244 | "- Inventoryに登録されているホストに関して、すべてSUCCESSとなり、結果がrootとなること" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 6, 250 | "metadata": { 251 | "collapsed": false 252 | }, 253 | "outputs": [ 254 | { 255 | "name": "stdout", 256 | "output_type": "stream", 257 | "text": [ 258 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS | rc=0 >>\n", 259 | "root\n", 260 | "\u001b[0m\n", 261 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 262 | "root\n", 263 | "\u001b[0m\n", 264 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS | rc=0 >>\n", 265 | "root\n", 266 | "\u001b[0m\n" 267 | ] 268 | } 269 | ], 270 | "source": [ 271 | "!ansible --become --become-user root -a 'whoami' all" 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": {}, 277 | "source": [ 278 | "以上の項目が満たされていればOK。" 279 | ] 280 | }, 281 | { 282 | "cell_type": "markdown", 283 | "metadata": { 284 | "collapsed": true 285 | }, 286 | "source": [ 287 | "# Binding対象の設定\n", 288 | "\n", 289 | "Binding対象に関して、Notebook + Ansibleによる管理を適用する上で適切な設定になっていることを確認する。" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": {}, 295 | "source": [ 296 | "## Ansibleによる操作ログが記録されていること - B\n", 297 | "\n", 298 | "NotebookからBinding対象の操作をおこなった際に、過去おこなった操作を適切に参照できる必要がある。\n", 299 | "\n", 300 | "> 我々の環境では、ansibleによる操作は `/var/log/secure` (CentOSの場合) `/var/log/auth.log` (Ubuntuの場合) に記録されている" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 7, 306 | "metadata": { 307 | "collapsed": false, 308 | "scrolled": true 309 | }, 310 | "outputs": [ 311 | { 312 | "name": "stdout", 313 | "output_type": "stream", 314 | "text": [ 315 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS | rc=0 >>\n", 316 | "/var/log/auth.log:Jun 17 23:02:22 ubuntu sudo: ansible : TTY=pts/3 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-bvlvlprywjftoofuddcvycadctvlxlpt; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466172142.43-67445864722369/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466172142.43-67445864722369/\" > /dev/null 2>&1\n", 317 | "/var/log/auth.log:Jun 17 23:02:27 ubuntu sudo: ansible : TTY=pts/3 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-hsyrmwfqwdfrolqqfinhmdyyuhavfoxt; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466172147.34-2143486004272/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466172147.34-2143486004272/\" > /dev/null 2>&1\n", 318 | "/var/log/auth.log:Jun 17 23:02:48 ubuntu sudo: ansible : TTY=pts/3 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-mxsxkcatoymbqtkztylihcmzvrtojhfm; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466172168.91-89050317978936/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466172168.91-89050317978936/\" > /dev/null 2>&1\n", 319 | "/var/log/auth.log:Jun 17 23:03:28 ubuntu sudo: ansible : TTY=pts/3 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-ijfudwqvkvukapwfjfzscttekfakqqga; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466172208.56-41142209901880/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466172208.56-41142209901880/\" > /dev/null 2>&1\n", 320 | "/var/log/auth.log:Jun 17 23:04:35 ubuntu sudo: ansible : TTY=pts/3 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-rnqksrucrmjvuifahrsctvnybqwztuib; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466172275.03-71458913933404/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466172275.03-71458913933404/\" > /dev/null 2>&1\n", 321 | "/var/log/auth.log:Jun 17 23:04:36 ubuntu sudo: ansible : TTY=pts/3 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-drzncmgimjjzclorbmkuhviliimfbswz; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466172276.7-230635552177736/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466172276.7-230635552177736/\" > /dev/null 2>&1\n", 322 | "/var/log/auth.log:Jun 18 05:11:15 ubuntu sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-xxrkycqngznjzewnhbhvfshvweujiogs; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194275.75-105543248348241/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194275.75-105543248348241/\" > /dev/null 2>&1\n", 323 | "/var/log/auth.log:Jun 18 05:11:20 ubuntu sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-wmkklcyqzbchcmiteajsnvtdrrgiiwaw; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194280.78-272715919485435/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194280.78-272715919485435/\" > /dev/null 2>&1\n", 324 | "/var/log/auth.log:Jun 18 05:12:36 ubuntu sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-eqngnkckbfosgyxywvjwfauxvprzatfe; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194356.37-75282088642744/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194356.37-75282088642744/\" > /dev/null 2>&1\n", 325 | "/var/log/auth.log:Jun 18 05:12:39 ubuntu sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-cynxivtppjgfjulafxdpmhsckaaveroc; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194359.68-240994693197277/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194359.68-240994693197277/\" > /dev/null 2>&1grep: /var/log/apt: Is a directory\n", 326 | "grep: /var/log/dist-upgrade: Is a directory\n", 327 | "grep: /var/log/fsck: Is a directory\n", 328 | "grep: /var/log/installer: Is a directory\n", 329 | "grep: /var/log/upstart: Is a directory\n", 330 | "\u001b[0m\n", 331 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 332 | "/var/log/secure-20160617:Jun 17 19:36:34 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-kmroumtqkayqddvxjbnwirpkxjwebmyl; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159794.58-266465335406742/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159794.58-266465335406742/\" > /dev/null 2>&1\n", 333 | "/var/log/secure-20160617:Jun 17 19:36:41 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-quosdkmlocwcvvmfssnhhzertcobjlye; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159801.27-212059753868173/stat; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159801.27-212059753868173/\" > /dev/null 2>&1\n", 334 | "/var/log/secure-20160617:Jun 17 19:36:41 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-hidfgrlcpybgawrakutekkkyrmqaepdz; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159801.56-237769564436842/stat; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159801.56-237769564436842/\" > /dev/null 2>&1\n", 335 | "/var/log/secure-20160617:Jun 17 19:36:42 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-pkzupeeebnqsddiwatekahiftymodwmn; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159801.84-99574324761647/copy; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159801.84-99574324761647/\" > /dev/null 2>&1\n", 336 | "/var/log/secure-20160617:Jun 17 19:36:46 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-ppeybtifsylmeuqaicgvzgzvkuyddhee; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159806.28-167110350100969/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159806.28-167110350100969/\" > /dev/null 2>&1\n", 337 | "/var/log/secure-20160617:Jun 17 19:36:50 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-lrjmcjfsiwbmwqpoojybolnbdqrcykbb; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159810.11-161945655218922/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159810.11-161945655218922/\" > /dev/null 2>&1\n", 338 | "/var/log/secure-20160617:Jun 17 19:36:59 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-samwvofjmnvzzogdemvtwnymdzftorjq; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159819.22-88649871534645/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159819.22-88649871534645/\" > /dev/null 2>&1\n", 339 | "/var/log/secure-20160617:Jun 17 19:37:10 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-yhqfhlbjqcockhthjalncaomduqdthdi; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159830.78-99911443277887/lineinfile; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159830.78-99911443277887/\" > /dev/null 2>&1\n", 340 | "/var/log/secure-20160617:Jun 17 19:37:17 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-pvsyhhmmlkvxlmtnexipaxuccsyhvpwd; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159837.85-95957916103138/service; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159837.85-95957916103138/\" > /dev/null 2>&1\n", 341 | "/var/log/secure-20160617:Jun 17 19:37:23 cn05061101 sudo: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-wjeimoqanuoaclufvmczgpistutaygkz; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466159843.79-223428378392159/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466159843.79-223428378392159/\" > /dev/null 2>&1grep: /var/log/audit: Is a directory\n", 342 | "grep: /var/log/ConsoleKit: Is a directory\n", 343 | "grep: /var/log/cups: Is a directory\n", 344 | "grep: /var/log/dodai: Is a directory\n", 345 | "grep: /var/log/glusterfs: Is a directory\n", 346 | "grep: /var/log/libvirt: Is a directory\n", 347 | "grep: /var/log/munin: Is a directory\n", 348 | "grep: /var/log/munin-node: Is a directory\n", 349 | "grep: /var/log/ntpstats: Is a directory\n", 350 | "grep: /var/log/sa: Is a directory\n", 351 | "\u001b[0m\n", 352 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS | rc=0 >>\n", 353 | "/var/log/auth.log:Jun 17 20:11:17 test-gce sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-ouskckebabmkosvpgydwsisisoqjrrfz; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194275.75-263740296786235/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194275.75-263740296786235/\" > /dev/null 2>&1\n", 354 | "/var/log/auth.log:Jun 17 20:11:22 test-gce sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-kwnwvlfutvoiarokypzluxtjtkgpcwtx; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194280.78-148070716328231/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194280.78-148070716328231/\" > /dev/null 2>&1\n", 355 | "/var/log/auth.log:Jun 17 20:12:15 test-gce sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-hucsybekcdrwxkadjpzydlxqiyixavfm; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194334.1-66155513789460/apt; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194334.1-66155513789460/\" > /dev/null 2>&1\n", 356 | "/var/log/auth.log:Jun 17 20:12:38 test-gce sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-wjqvmgceixswptdxsdwatspowlfwgwny; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194356.37-8229784321158/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194356.37-8229784321158/\" > /dev/null 2>&1\n", 357 | "/var/log/auth.log:Jun 17 20:12:41 test-gce sudo: ansible : TTY=pts/0 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-hggqvgmlxuopuayueadupmzgtuayzoun; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/ansible/.ansible/tmp/ansible-tmp-1466194359.68-252788100125235/command; rm -rf \"/home/ansible/.ansible/tmp/ansible-tmp-1466194359.68-252788100125235/\" > /dev/null 2>&1grep: /var/log/apt: Is a directory\n", 358 | "grep: /var/log/dist-upgrade: Is a directory\n", 359 | "grep: /var/log/fsck: Is a directory\n", 360 | "grep: /var/log/landscape: Is a directory\n", 361 | "grep: /var/log/ntpstats: Is a directory\n", 362 | "grep: /var/log/unattended-upgrades: Is a directory\n", 363 | "grep: /var/log/upstart: Is a directory\n", 364 | "\u001b[0m\n" 365 | ] 366 | } 367 | ], 368 | "source": [ 369 | "!ansible -b -m shell -a 'grep ansible- /var/log/* | tail' all" 370 | ] 371 | }, 372 | { 373 | "cell_type": "markdown", 374 | "metadata": {}, 375 | "source": [ 376 | "### ログファイルが適切にrotateされていること - B\n", 377 | "\n", 378 | "操作の記録がディスクを圧迫しないことを確認しておく。ログは短期的な(Ansibleによる)操作の記録として参照可能にしておくべきものという前提。長期的には、Notebookを証跡としてアーカイブしておくべき。" 379 | ] 380 | }, 381 | { 382 | "cell_type": "markdown", 383 | "metadata": {}, 384 | "source": [ 385 | "## 共通の方法でホストの状況が確認できること - C\n", 386 | "\n", 387 | "Notebookによる操作実施時は、適当なチェックポイントごとに状況を記録することで、後戻りしたくなった時に戻れる(戻ったことが確認できる)ようにする。" 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": {}, 393 | "source": [ 394 | "### OSのバージョン確認\n", 395 | "\n", 396 | "lsb_releaseを使う。" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 8, 402 | "metadata": { 403 | "collapsed": false 404 | }, 405 | "outputs": [ 406 | { 407 | "name": "stdout", 408 | "output_type": "stream", 409 | "text": [ 410 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS | rc=0 >>\n", 411 | "Distributor ID:\tUbuntu\n", 412 | "Description:\tUbuntu 14.04.4 LTS\n", 413 | "Release:\t14.04\n", 414 | "Codename:\ttrustyNo LSB modules are available.\n", 415 | "\u001b[0m\n", 416 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 417 | "LSB Version:\t:base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch\n", 418 | "Distributor ID:\tCentOS\n", 419 | "Description:\tCentOS release 6.8 (Final)\n", 420 | "Release:\t6.8\n", 421 | "Codename:\tFinal\n", 422 | "\u001b[0m\n", 423 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS | rc=0 >>\n", 424 | "Distributor ID:\tUbuntu\n", 425 | "Description:\tUbuntu 14.04.4 LTS\n", 426 | "Release:\t14.04\n", 427 | "Codename:\ttrustyNo LSB modules are available.\n", 428 | "\u001b[0m\n" 429 | ] 430 | } 431 | ], 432 | "source": [ 433 | "!ansible -a 'lsb_release -a' all" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "### ファイル構成の確認\n", 441 | "\n", 442 | "treeコマンドを使う。" 443 | ] 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": 9, 448 | "metadata": { 449 | "collapsed": false 450 | }, 451 | "outputs": [ 452 | { 453 | "name": "stdout", 454 | "output_type": "stream", 455 | "text": [ 456 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS | rc=0 >>\n", 457 | "/tmp\n", 458 | "└── ansible_bxzXfW\n", 459 | " ├── ansible_modlib.zip\n", 460 | " └── ansible_module_command.py\n", 461 | "\n", 462 | "1 directory, 2 files\n", 463 | "\u001b[0m\n", 464 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 465 | "/tmp\n", 466 | "├── ansible_DgRpfV\n", 467 | "│   ├── ansible_modlib.zip\n", 468 | "│   └── ansible_module_command.py\n", 469 | "├── CentOS-6.8-x86_64-minimal.iso\n", 470 | "├── centos6.ks.cfg\n", 471 | "└── ubuntu14.04.ks.cfg\n", 472 | "\n", 473 | "1 directory, 5 files\n", 474 | "\u001b[0m\n", 475 | "\u001b[0;32mXXX.XXX.XXX.59 | SUCCESS | rc=0 >>\n", 476 | "/tmp\n", 477 | "└── ansible_BYOvNB\n", 478 | " ├── ansible_modlib.zip\n", 479 | " └── ansible_module_command.py\n", 480 | "\n", 481 | "1 directory, 2 files\n", 482 | "\u001b[0m\n" 483 | ] 484 | } 485 | ], 486 | "source": [ 487 | "!ansible -a 'tree /tmp' all" 488 | ] 489 | }, 490 | { 491 | "cell_type": "markdown", 492 | "metadata": {}, 493 | "source": [ 494 | "## リソースの使用状況変化をモニタリングしていること - C\n", 495 | "\n", 496 | "Binding対象を操作した際に、予期せぬディスク使用量増加やサービス異常終了に気づけるよう、状況変化をモニタリングしていることが望ましい。" 497 | ] 498 | }, 499 | { 500 | "cell_type": "code", 501 | "execution_count": null, 502 | "metadata": { 503 | "collapsed": true 504 | }, 505 | "outputs": [], 506 | "source": [] 507 | } 508 | ], 509 | "metadata": { 510 | "kernelspec": { 511 | "display_name": "Python 2", 512 | "language": "python", 513 | "name": "python2" 514 | }, 515 | "language_info": { 516 | "codemirror_mode": { 517 | "name": "ipython", 518 | "version": 2 519 | }, 520 | "file_extension": ".py", 521 | "mimetype": "text/x-python", 522 | "name": "python", 523 | "nbconvert_exporter": "python", 524 | "pygments_lexer": "ipython2", 525 | "version": "2.7.12" 526 | }, 527 | "toc": { 528 | "toc_cell": false, 529 | "toc_number_sections": true, 530 | "toc_threshold": 6, 531 | "toc_window_display": false 532 | } 533 | }, 534 | "nbformat": 4, 535 | "nbformat_minor": 0 536 | } 537 | -------------------------------------------------------------------------------- /D03b_KVM - Set! Ubuntu 14.04.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About: KVM - Set! Ubuntu 14.04\n", 8 | "\n", 9 | "---\n", 10 | "\n", 11 | "Prepare Ubuntu 14.04 image for KVM using libvirt. KVM and libvirt has been installed already.\n", 12 | "\n", 13 | "Ubuntu 14.04 VMイメージを作成するためのNotebook。" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## Operation Note\n", 21 | "\n", 22 | "*ここに経緯を記述*" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "# Notebookと環境のBinding\n", 30 | "\n", 31 | "Inventory中のgroup名でBind対象を指示する。\n", 32 | "\n", 33 | "**VMを起動したいホスト(KVMなどがインストールされた物理マシン)**を示すInventory中の名前を以下に指定する。" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 1, 39 | "metadata": { 40 | "ExecuteTime": { 41 | "end_time": "2016-04-26T08:07:17.170145", 42 | "start_time": "2016-04-26T08:07:17.162322" 43 | }, 44 | "collapsed": true 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "target_group = 'test-hypervisor'" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "Bind対象への疎通状態を確認する。" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 2, 61 | "metadata": { 62 | "ExecuteTime": { 63 | "end_time": "2016-04-26T08:07:19.352910", 64 | "start_time": "2016-04-26T08:07:18.027874" 65 | }, 66 | "collapsed": false 67 | }, 68 | "outputs": [ 69 | { 70 | "name": "stdout", 71 | "output_type": "stream", 72 | "text": [ 73 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 74 | " \"changed\": false, \r\n", 75 | " \"ping\": \"pong\"\r\n", 76 | "}\u001b[0m\r\n" 77 | ] 78 | } 79 | ], 80 | "source": [ 81 | "!ansible -m ping {target_group}" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "Bind対象は以下の条件を満たしている必要がある。**満たしていない場合は、このお手本の操作をBind対象にそのまま適用することはできず、適宜セルの改変が必要。**" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "## 仮想マシン用ブリッジが作成されていること\n", 96 | "\n", 97 | "仮想マシン用のブリッジが作成されていること。お手本を作成している環境においては、以下のようなインタフェース構成となることを想定している。\n", 98 | "\n", 99 | "- ブリッジ br-eth1 インタフェース ... ここにはサービス用IPアドレスが設定される\n", 100 | "- eth1インタフェース ... Promiscuousモードでサービス用NICと対応付け、br-eth1インタフェースに接続される" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 3, 106 | "metadata": { 107 | "collapsed": true 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "external_nic = 'eth1'\n", 112 | "bridge_nic = 'br-eth1'" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 4, 118 | "metadata": { 119 | "ExecuteTime": { 120 | "end_time": "2016-04-26T08:07:25.847812", 121 | "start_time": "2016-04-26T08:07:22.127652" 122 | }, 123 | "collapsed": false 124 | }, 125 | "outputs": [ 126 | { 127 | "name": "stdout", 128 | "output_type": "stream", 129 | "text": [ 130 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 131 | "10: br-eth1: mtu 1500 qdisc noqueue state UNKNOWN \n", 132 | " link/ether XX:XX:XX:XX:XX:XX brd XX:XX:XX:XX:XX:XX\n", 133 | " inet XXX.XXX.XXX.105/26 brd XXX.XXX.XXX.127 scope global br-eth1\n", 134 | " inet6 XX:XX:XX:XX:XX:XX/64 scope link \n", 135 | " valid_lft forever preferred_lft forever\n", 136 | "\u001b[0m\n", 137 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 138 | "2: eth1: mtu 1500 qdisc mq state UP qlen 1000\n", 139 | " link/ether XX:XX:XX:XX:XX:XX brd XX:XX:XX:XX:XX:XX\n", 140 | " inet6 XX:XX:XX:XX:XX:XX/64 scope link \n", 141 | " valid_lft forever preferred_lft forever\n", 142 | "\u001b[0m\n", 143 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\n", 144 | "bridge name\tbridge id\t\tSTP enabled\tinterfaces\n", 145 | "br-eth1\t\t8000.246e960db538\tno\t\teth1\n", 146 | "\u001b[0m\n" 147 | ] 148 | } 149 | ], 150 | "source": [ 151 | "!ansible -a \"/sbin/ip addr show {bridge_nic}\" {target_group}\n", 152 | "!ansible -a \"/sbin/ip addr show {external_nic}\" {target_group}\n", 153 | "!ansible -a \"/usr/sbin/brctl show {bridge_nic}\" {target_group}" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "ブリッジ用NIC名として br-eth1 を利用する。" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "**br-eth1, eth1が定義されており、br-eth1にサービス用IPアドレスが定義されていれば**OK。" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "## libvirtのNetwork設定が無効化されていること\n", 175 | "\n", 176 | "defaultのNetwork設定が無効化されているかどうかを確認する。" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 5, 182 | "metadata": { 183 | "ExecuteTime": { 184 | "end_time": "2016-04-26T08:07:32.682995", 185 | "start_time": "2016-04-26T08:07:31.432732" 186 | }, 187 | "collapsed": false 188 | }, 189 | "outputs": [ 190 | { 191 | "name": "stdout", 192 | "output_type": "stream", 193 | "text": [ 194 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 195 | "Name State Autostart Persistent\r\n", 196 | "--------------------------------------------------\r\n", 197 | "default inactive no yes\r\n", 198 | "\u001b[0m\r\n" 199 | ] 200 | } 201 | ], 202 | "source": [ 203 | "!ansible -b -a 'virsh net-list --all' {target_group}" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "**defaultのstateがinactiveになっていて、かつautostartがnoになっていれば**OK。" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "## dnsmasqが起動していること\n", 218 | "\n", 219 | "同じホストで、IPアドレス配布用のdnsmasqが実行されていることを前提としている。" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 6, 225 | "metadata": { 226 | "ExecuteTime": { 227 | "end_time": "2016-04-26T08:07:36.086188", 228 | "start_time": "2016-04-26T08:07:34.768858" 229 | }, 230 | "collapsed": false 231 | }, 232 | "outputs": [ 233 | { 234 | "name": "stdout", 235 | "output_type": "stream", 236 | "text": [ 237 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 238 | "dnsmasq (pid 102166) is running...dnsdomainname: Unknown host\r\n", 239 | "\u001b[0m\r\n" 240 | ] 241 | } 242 | ], 243 | "source": [ 244 | "!ansible -b -a 'service dnsmasq status' {target_group}" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "**dnsmasq (pid XXXXX) is running と表示されれば**OK。" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "## libvirtが動作していること\n", 259 | "\n", 260 | "libvirtが動作しており、仮想マシン一覧が取得できるかどうかを確認する。" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": 7, 266 | "metadata": { 267 | "ExecuteTime": { 268 | "end_time": "2016-04-26T08:07:39.755701", 269 | "start_time": "2016-04-26T08:07:38.482204" 270 | }, 271 | "collapsed": false 272 | }, 273 | "outputs": [ 274 | { 275 | "name": "stdout", 276 | "output_type": "stream", 277 | "text": [ 278 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 279 | " Id Name State\r\n", 280 | "----------------------------------------------------\r\n", 281 | "\u001b[0m\r\n" 282 | ] 283 | } 284 | ], 285 | "source": [ 286 | "!ansible -b -a 'virsh list' {target_group}" 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "metadata": {}, 292 | "source": [ 293 | "**エラーメッセージが表示されなければ**OK。" 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": {}, 299 | "source": [ 300 | "## virt-installがインストールされていること\n", 301 | "\n", 302 | "仮想マシンの作成には、virt-installコマンドを利用する。" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": 8, 308 | "metadata": { 309 | "collapsed": false 310 | }, 311 | "outputs": [ 312 | { 313 | "name": "stdout", 314 | "output_type": "stream", 315 | "text": [ 316 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 317 | "/usr/bin/virt-install\r\n", 318 | "\u001b[0m\r\n" 319 | ] 320 | } 321 | ], 322 | "source": [ 323 | "!ansible -a 'which virt-install' {target_group}" 324 | ] 325 | }, 326 | { 327 | "cell_type": "markdown", 328 | "metadata": {}, 329 | "source": [ 330 | "**エラーメッセージが表示されなければ**OK。" 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": {}, 336 | "source": [ 337 | "# パラメータの決定\n", 338 | "\n", 339 | "イメージ作成により、以下の2つのファイルがBinding対象ホストに作成される。\n", 340 | "\n", 341 | "- base.img\n", 342 | "- libvirt-base.xml\n", 343 | "\n", 344 | "このファイルを作成するディレクトリのパスと、イメージのサイズ(GB)を指定する。" 345 | ] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "execution_count": 9, 350 | "metadata": { 351 | "collapsed": true 352 | }, 353 | "outputs": [], 354 | "source": [ 355 | "image_base_dir = '/mnt/ubuntu14.04-base-vm'\n", 356 | "size_gb = 100" 357 | ] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "metadata": {}, 362 | "source": [ 363 | "`size_gb` で指定した空き容量がBind対象ホストにあるかどうかを確認する。" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": 10, 369 | "metadata": { 370 | "collapsed": false 371 | }, 372 | "outputs": [ 373 | { 374 | "name": "stdout", 375 | "output_type": "stream", 376 | "text": [ 377 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 378 | "Filesystem Size Used Avail Use% Mounted on\r\n", 379 | "/dev/sda2 14G 3.3G 11G 25% /\r\n", 380 | "tmpfs 68G 0 68G 0% /dev/shm\r\n", 381 | "/dev/sda5 1.7T 2.9G 1.7T 1% /mnt\r\n", 382 | "\u001b[0m\r\n" 383 | ] 384 | } 385 | ], 386 | "source": [ 387 | "!ansible -a 'df -H' {target_group} " 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": {}, 393 | "source": [ 394 | "# イメージ取得用VMの新規作成\n", 395 | "\n", 396 | "Binding対象ホストにイメージ保存用のディレクトリを作成する。" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 11, 402 | "metadata": { 403 | "collapsed": false 404 | }, 405 | "outputs": [ 406 | { 407 | "name": "stdout", 408 | "output_type": "stream", 409 | "text": [ 410 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 411 | " \"changed\": true, \r\n", 412 | " \"gid\": 0, \r\n", 413 | " \"group\": \"root\", \r\n", 414 | " \"mode\": \"0755\", \r\n", 415 | " \"owner\": \"root\", \r\n", 416 | " \"path\": \"/mnt/ubuntu14.04-base-vm\", \r\n", 417 | " \"size\": 4096, \r\n", 418 | " \"state\": \"directory\", \r\n", 419 | " \"uid\": 0\r\n", 420 | "}\u001b[0m\r\n" 421 | ] 422 | } 423 | ], 424 | "source": [ 425 | "!ansible -b -m file -a 'path={image_base_dir} state=directory' {target_group} " 426 | ] 427 | }, 428 | { 429 | "cell_type": "markdown", 430 | "metadata": {}, 431 | "source": [ 432 | "スナップショット用のVM名を決める。" 433 | ] 434 | }, 435 | { 436 | "cell_type": "code", 437 | "execution_count": 12, 438 | "metadata": { 439 | "collapsed": true 440 | }, 441 | "outputs": [], 442 | "source": [ 443 | "new_vmname = 'snapshot-vm-20160617'" 444 | ] 445 | }, 446 | { 447 | "cell_type": "markdown", 448 | "metadata": {}, 449 | "source": [ 450 | "## Kickstartファイルの準備\n", 451 | "\n", 452 | "インストール手順は Kickstartを使って定義する。\n", 453 | "\n", 454 | "念のため、VMにはansibleユーザにパスワードを指定しておく。このパスワードはスナップショット処理の最後にロックする。" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": 13, 460 | "metadata": { 461 | "collapsed": false 462 | }, 463 | "outputs": [ 464 | { 465 | "name": "stdout", 466 | "output_type": "stream", 467 | "text": [ 468 | "········\n" 469 | ] 470 | } 471 | ], 472 | "source": [ 473 | "from getpass import getpass\n", 474 | "ubuntupw = getpass()" 475 | ] 476 | }, 477 | { 478 | "cell_type": "markdown", 479 | "metadata": {}, 480 | "source": [ 481 | "CentOS6のインストールをおこない、public keyをInjectionするようなKickstartファイルを生成する。\n", 482 | "\n", 483 | "まずローカルに一時ディレクトリを作り、そこにファイルを作成する。" 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": 14, 489 | "metadata": { 490 | "ExecuteTime": { 491 | "end_time": "2016-04-26T08:08:00.452166", 492 | "start_time": "2016-04-26T08:08:00.427056" 493 | }, 494 | "collapsed": false 495 | }, 496 | "outputs": [ 497 | { 498 | "data": { 499 | "text/plain": [ 500 | "'/tmp/tmpEnRDp5'" 501 | ] 502 | }, 503 | "execution_count": 14, 504 | "metadata": {}, 505 | "output_type": "execute_result" 506 | } 507 | ], 508 | "source": [ 509 | "import tempfile\n", 510 | "work_dir = tempfile.mkdtemp()\n", 511 | "work_dir" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 15, 517 | "metadata": { 518 | "collapsed": false, 519 | "run_control": { 520 | "marked": false 521 | } 522 | }, 523 | "outputs": [ 524 | { 525 | "name": "stdout", 526 | "output_type": "stream", 527 | "text": [ 528 | "#version=Ubuntu14.04\r\n", 529 | "\r\n", 530 | "text\r\n", 531 | "cdrom\r\n", 532 | "install\r\n", 533 | "\r\n", 534 | "lang en_US.UTF-8\r\n", 535 | "keyboard jp106\r\n", 536 | "\r\n", 537 | "network --device eth0 --onboot yes --bootproto dhcp --noipv6\r\n", 538 | "\r\n", 539 | "zerombr yes\r\n", 540 | "# --append=\"crashkernel=auto rhgb quiet\"\r\n", 541 | "bootloader --location=mbr\r\n", 542 | "\r\n", 543 | "clearpart --all --initlabel\r\n", 544 | "part /boot --fstype=ext4 --size=512 --asprimary\r\n", 545 | "part pv.1 --grow --size=1 --asprimary\r\n", 546 | "volgroup vg0 --pesize=4096 pv.1\r\n", 547 | "logvol / --fstype=ext4 --name=root --vgname=vg0 --size=99840\r\n", 548 | "logvol swap --name=swap --vgname=vg0 --size=2048 --maxsize=2048\r\n", 549 | "\r\n", 550 | "rootpw --disabled\r\n", 551 | "authconfig --enableshadow --passalgo=sha512\r\n", 552 | "selinux --disabled\r\n", 553 | "firewall --disabled --trust=eth0 --ssh\r\n", 554 | "firstboot --disabled\r\n", 555 | "timezone --utc Asia/Tokyo\r\n", 556 | "\r\n", 557 | "poweroff\r\n", 558 | "\r\n", 559 | "%packages\r\n", 560 | "ca-certificates\r\n", 561 | "openssl\r\n", 562 | "python\r\n", 563 | "openssh-server\r\n", 564 | "curl\r\n", 565 | "\r\n", 566 | "%post\r\n", 567 | "\r\n", 568 | "install -d -m 0755 -o root -g root /home/ansible/.ssh\r\n", 569 | "cat >> /home/ansible/.ssh/authorized_keys << \"PUBLIC_KEY\"\r\n", 570 | "ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ansible@XXXXXXXXXXXX\r\n", 571 | "PUBLIC_KEY\r\n", 572 | "echo \"ansible ALL=(ALL) NOPASSWD: ALL\" > /etc/sudoers.d/ansible\r\n" 573 | ] 574 | } 575 | ], 576 | "source": [ 577 | "import os\n", 578 | "pub_key = None\n", 579 | "with open(os.path.expanduser('~/.ssh/ansible_id_rsa.pub'), 'r') as f:\n", 580 | " pub_key = f.readlines()[0].strip()\n", 581 | "\n", 582 | "with open(os.path.join(work_dir, 'ubuntu14.04.ks.cfg'), 'w') as f:\n", 583 | " f.write('''#version=Ubuntu14.04\n", 584 | "\n", 585 | "text\n", 586 | "cdrom\n", 587 | "install\n", 588 | "\n", 589 | "lang en_US.UTF-8\n", 590 | "keyboard jp106\n", 591 | "\n", 592 | "network --device eth0 --onboot yes --bootproto dhcp --noipv6\n", 593 | "\n", 594 | "zerombr yes\n", 595 | "# --append=\"crashkernel=auto rhgb quiet\"\n", 596 | "bootloader --location=mbr\n", 597 | "\n", 598 | "clearpart --all --initlabel\n", 599 | "part /boot --fstype=ext4 --size=512 --asprimary\n", 600 | "part pv.1 --grow --size=1 --asprimary\n", 601 | "volgroup vg0 --pesize=4096 pv.1\n", 602 | "logvol / --fstype=ext4 --name=root --vgname=vg0 --size={rootsize_mb}\n", 603 | "logvol swap --name=swap --vgname=vg0 --size=2048 --maxsize=2048\n", 604 | "\n", 605 | "rootpw --disabled\n", 606 | "user ansible --fullname \"Ansible User\" --password {ubuntupw}\n", 607 | "authconfig --enableshadow --passalgo=sha512\n", 608 | "selinux --disabled\n", 609 | "firewall --disabled --trust=eth0 --ssh\n", 610 | "firstboot --disabled\n", 611 | "timezone --utc Asia/Tokyo\n", 612 | "\n", 613 | "poweroff\n", 614 | "\n", 615 | "%packages\n", 616 | "ca-certificates\n", 617 | "openssl\n", 618 | "python\n", 619 | "openssh-server\n", 620 | "curl\n", 621 | "\n", 622 | "%post\n", 623 | "\n", 624 | "install -d -m 0755 -o root -g root /home/ansible/.ssh\n", 625 | "cat >> /home/ansible/.ssh/authorized_keys << \"PUBLIC_KEY\"\n", 626 | "{pub_key}\n", 627 | "PUBLIC_KEY\n", 628 | "echo \"ansible ALL=(ALL) NOPASSWD: ALL\" > /etc/sudoers.d/ansible\n", 629 | "'''.format(ubuntupw=ubuntupw, rootsize_mb=size_gb * 1024 - 512 - 2048, pub_key=pub_key))\n", 630 | "\n", 631 | "!grep -v password {work_dir}/ubuntu14.04.ks.cfg" 632 | ] 633 | }, 634 | { 635 | "cell_type": "markdown", 636 | "metadata": {}, 637 | "source": [ 638 | "なお、Kickstartの設定では、最後にpoweroffすることでインストール成功後、VMを停止するようにしている。" 639 | ] 640 | }, 641 | { 642 | "cell_type": "markdown", 643 | "metadata": {}, 644 | "source": [ 645 | "Bind対象にアップロードする。" 646 | ] 647 | }, 648 | { 649 | "cell_type": "code", 650 | "execution_count": 16, 651 | "metadata": { 652 | "ExecuteTime": { 653 | "end_time": "2016-04-26T08:08:35.049343", 654 | "start_time": "2016-04-26T08:08:33.654121" 655 | }, 656 | "collapsed": false 657 | }, 658 | "outputs": [ 659 | { 660 | "name": "stdout", 661 | "output_type": "stream", 662 | "text": [ 663 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 664 | " \"changed\": true, \r\n", 665 | " \"checksum\": \"ea5ba336b9fd9f8a1082da4ab7430fdeabe7231d\", \r\n", 666 | " \"dest\": \"/tmp/ubuntu14.04.ks.cfg\", \r\n", 667 | " \"gid\": 0, \r\n", 668 | " \"group\": \"root\", \r\n", 669 | " \"md5sum\": \"13efbd37b5f4cfea9c6fa1df5a0f8cd5\", \r\n", 670 | " \"mode\": \"0644\", \r\n", 671 | " \"owner\": \"root\", \r\n", 672 | " \"size\": 1393, \r\n", 673 | " \"src\": \"/home/ansible/.ansible/tmp/ansible-tmp-1466158608.33-154527480642747/source\", \r\n", 674 | " \"state\": \"file\", \r\n", 675 | " \"uid\": 0\r\n", 676 | "}\u001b[0m\r\n" 677 | ] 678 | } 679 | ], 680 | "source": [ 681 | "!ansible -b -m copy -a 'src={work_dir}/ubuntu14.04.ks.cfg dest=/tmp/ubuntu14.04.ks.cfg' {target_group}" 682 | ] 683 | }, 684 | { 685 | "cell_type": "markdown", 686 | "metadata": {}, 687 | "source": [ 688 | "## インストールの実行\n", 689 | "\n", 690 | "virt-installを実行する。なお、AnsibleのSSH処理の関係で、 `process.error: Cannot run interactive console without a controlling TTY` と出力されるが、ここでは無視する。" 691 | ] 692 | }, 693 | { 694 | "cell_type": "code", 695 | "execution_count": 17, 696 | "metadata": { 697 | "ExecuteTime": { 698 | "end_time": "2016-04-26T08:12:36.256773", 699 | "start_time": "2016-04-26T08:09:01.260627" 700 | }, 701 | "collapsed": false 702 | }, 703 | "outputs": [ 704 | { 705 | "name": "stdout", 706 | "output_type": "stream", 707 | "text": [ 708 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 709 | "\r\n", 710 | "Starting install...\r\n", 711 | "\r", 712 | "Retrieving file MANIFEST... | 12 kB 00:00 ... \r\n", 713 | "\r", 714 | "Retrieving file MANIFEST... | 12 kB 00:00 ... \r\n", 715 | "Retrieving file linux... 0% [ ] 0.0 B/s | 41 kB --:-- ETA \r", 716 | "Retrieving file linux... 2% [ ] 89 kB/s | 126 kB 01:02 ETA \r", 717 | "Retrieving file linux... 3% [- ] 99 kB/s | 221 kB 00:55 ETA \r", 718 | "Retrieving file linux... 7% [= ] 129 kB/s | 415 kB 00:41 ETA \r", 719 | "Retrieving file linux... 12% [=- ] 173 kB/s | 696 kB 00:28 ETA \r", 720 | "Retrieving file linux... 18% [==- ] 233 kB/s | 1.1 MB 00:19 ETA \r", 721 | "Retrieving file linux... 28% [==== ] 325 kB/s | 1.6 MB 00:12 ETA \r", 722 | "Retrieving file linux... 43% [======- ] 463 kB/s | 2.4 MB 00:06 ETA \r", 723 | "Retrieving file linux... 66% [=========- ] 675 kB/s | 3.7 MB 00:02 ETA \r", 724 | "Retrieving file linux... 99% [==============-] 984 kB/s | 5.5 MB 00:00 ETA \r", 725 | "\r", 726 | "Retrieving file linux... | 11 MB 00:04 ... \r\n", 727 | "Retrieving file initrd.gz 7% [= ] 0.0 B/s | 2.7 MB --:-- ETA \r", 728 | "Retrieving file initrd.gz 17% [==- ] 4.0 MB/s | 6.5 MB 00:07 ETA \r", 729 | "Retrieving file initrd.gz 30% [====- ] 4.7 MB/s | 12 MB 00:05 ETA \r", 730 | "Retrieving file initrd.gz 45% [======- ] 5.4 MB/s | 17 MB 00:03 ETA \r", 731 | "Retrieving file initrd.gz 60% [========= ] 6.0 MB/s | 23 MB 00:02 ETA \r", 732 | "Retrieving file initrd.gz 75% [=========== ] 6.6 MB/s | 29 MB 00:01 ETA \r", 733 | "Retrieving file initrd.gz 91% [=============- ] 7.1 MB/s | 35 MB 00:00 ETA \r", 734 | "\r", 735 | "Retrieving file initrd.gz... | 76 MB 00:03 ... \r\n", 736 | "\r", 737 | "Creating storage file base.img | 100 GB 00:00 \r\n", 738 | "\r", 739 | "Creating domain... | 0 B 00:00 \r\n", 740 | "\r\n", 741 | "Domain installation still in progress. You can reconnect to \r\n", 742 | "the console to complete the installation process.error: Cannot run interactive console without a controlling TTY\r\n", 743 | "\u001b[0m\r\n" 744 | ] 745 | } 746 | ], 747 | "source": [ 748 | "!ansible -b -a 'virt-install --name {new_vmname} \\\n", 749 | " --hvm \\\n", 750 | " --virt-type kvm \\\n", 751 | " --ram 1024 \\\n", 752 | " --vcpus 1 \\\n", 753 | " --arch x86_64 \\\n", 754 | " --os-type linux \\\n", 755 | " --boot hd \\\n", 756 | " --disk path\\={image_base_dir}/base.img,size\\={size_gb},format\\=raw \\\n", 757 | " --network bridge\\={bridge_nic} \\\n", 758 | " --graphics none \\\n", 759 | " --serial pty \\\n", 760 | " --console pty \\\n", 761 | " --noreboot \\\n", 762 | " --location http://archive.ubuntu.com/ubuntu/dists/trusty-updates/main/installer-amd64/ \\\n", 763 | " --initrd-inject /tmp/ubuntu14.04.ks.cfg \\\n", 764 | " --extra-args \"ks\\=file:/ubuntu14.04.ks.cfg console\\=ttyS0\"' {target_group}" 765 | ] 766 | }, 767 | { 768 | "cell_type": "markdown", 769 | "metadata": {}, 770 | "source": [ 771 | "VMの状態確認は以下で行える。" 772 | ] 773 | }, 774 | { 775 | "cell_type": "code", 776 | "execution_count": 18, 777 | "metadata": { 778 | "collapsed": false 779 | }, 780 | "outputs": [ 781 | { 782 | "name": "stdout", 783 | "output_type": "stream", 784 | "text": [ 785 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 786 | "State: running\r\n", 787 | "\u001b[0m\r\n" 788 | ] 789 | } 790 | ], 791 | "source": [ 792 | "!ansible -b -m shell -a 'virsh dominfo {new_vmname} | grep State' {target_group}" 793 | ] 794 | }, 795 | { 796 | "cell_type": "markdown", 797 | "metadata": {}, 798 | "source": [ 799 | "具体的なコンソール出力の確認は、 `virsh console ${new_vmname}` でもおこなえる。" 800 | ] 801 | }, 802 | { 803 | "cell_type": "markdown", 804 | "metadata": {}, 805 | "source": [ 806 | "poweroffされるまで待つ・・・" 807 | ] 808 | }, 809 | { 810 | "cell_type": "code", 811 | "execution_count": 19, 812 | "metadata": { 813 | "collapsed": false 814 | }, 815 | "outputs": [], 816 | "source": [ 817 | "vm_status = !ansible -b -m shell -a 'virsh dominfo {new_vmname} | grep State' {target_group}\n", 818 | "\n", 819 | "import time\n", 820 | "while vm_status[1].split()[-1] == 'running':\n", 821 | " time.sleep(60)\n", 822 | " vm_status = !ansible -b -m shell -a 'virsh dominfo {new_vmname} | grep State' {target_group}" 823 | ] 824 | }, 825 | { 826 | "cell_type": "markdown", 827 | "metadata": {}, 828 | "source": [ 829 | "以下の出力が `shut off` となっていればOK。" 830 | ] 831 | }, 832 | { 833 | "cell_type": "code", 834 | "execution_count": 20, 835 | "metadata": { 836 | "collapsed": false 837 | }, 838 | "outputs": [ 839 | { 840 | "name": "stdout", 841 | "output_type": "stream", 842 | "text": [ 843 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 844 | "State: shut off\r\n", 845 | "\u001b[0m\r\n" 846 | ] 847 | } 848 | ], 849 | "source": [ 850 | "!ansible -b -m shell -a 'virsh dominfo {new_vmname} | grep State' {target_group}" 851 | ] 852 | }, 853 | { 854 | "cell_type": "markdown", 855 | "metadata": {}, 856 | "source": [ 857 | "起動してみる。" 858 | ] 859 | }, 860 | { 861 | "cell_type": "code", 862 | "execution_count": 21, 863 | "metadata": { 864 | "collapsed": false 865 | }, 866 | "outputs": [ 867 | { 868 | "name": "stdout", 869 | "output_type": "stream", 870 | "text": [ 871 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 872 | "Domain snapshot-vm-20160617 started\r\n", 873 | "\u001b[0m\r\n" 874 | ] 875 | } 876 | ], 877 | "source": [ 878 | "!ansible -b -a 'virsh start {new_vmname}' {target_group}" 879 | ] 880 | }, 881 | { 882 | "cell_type": "markdown", 883 | "metadata": {}, 884 | "source": [ 885 | "## 仮想マシンの情報確認\n", 886 | "\n", 887 | "VMにふられたIPアドレスの確認" 888 | ] 889 | }, 890 | { 891 | "cell_type": "code", 892 | "execution_count": 22, 893 | "metadata": { 894 | "ExecuteTime": { 895 | "end_time": "2016-04-26T08:13:00.376011", 896 | "start_time": "2016-04-26T08:12:59.085499" 897 | }, 898 | "collapsed": false 899 | }, 900 | "outputs": [ 901 | { 902 | "name": "stdout", 903 | "output_type": "stream", 904 | "text": [ 905 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 906 | "Interface Type Source Model MAC\r\n", 907 | "-------------------------------------------------------\r\n", 908 | "vnet0 bridge br-eth1 - XX:XX:XX:XX:XX:XX\r\n", 909 | "\u001b[0m\r\n" 910 | ] 911 | } 912 | ], 913 | "source": [ 914 | "!ansible -b -a \"virsh domiflist {new_vmname}\" {target_group}" 915 | ] 916 | }, 917 | { 918 | "cell_type": "markdown", 919 | "metadata": {}, 920 | "source": [ 921 | "上記で確認できたMACアドレスを、以下の変数に代入。" 922 | ] 923 | }, 924 | { 925 | "cell_type": "code", 926 | "execution_count": 23, 927 | "metadata": { 928 | "ExecuteTime": { 929 | "end_time": "2016-04-26T08:13:03.615817", 930 | "start_time": "2016-04-26T08:13:02.448966" 931 | }, 932 | "collapsed": false 933 | }, 934 | "outputs": [ 935 | { 936 | "data": { 937 | "text/plain": [ 938 | "'XX:XX:XX:XX:XX:XX'" 939 | ] 940 | }, 941 | "execution_count": 23, 942 | "metadata": {}, 943 | "output_type": "execute_result" 944 | } 945 | ], 946 | "source": [ 947 | "import re\n", 948 | "domiflist_stdio = !ansible -b -a \"virsh domiflist {new_vmname}\" {target_group}\n", 949 | "mac_pattern = re.compile(r'.*bridge.*\\s([0-9a-f\\:]+)\\s*')\n", 950 | "vmmac = [mac_pattern.match(line).group(1) for line in domiflist_stdio if mac_pattern.match(line)][0]\n", 951 | "vmmac" 952 | ] 953 | }, 954 | { 955 | "cell_type": "markdown", 956 | "metadata": {}, 957 | "source": [ 958 | "dnsmasqのlease情報を確認する。" 959 | ] 960 | }, 961 | { 962 | "cell_type": "code", 963 | "execution_count": 24, 964 | "metadata": { 965 | "ExecuteTime": { 966 | "end_time": "2016-04-26T08:13:06.950890", 967 | "start_time": "2016-04-26T08:13:04.713763" 968 | }, 969 | "collapsed": false 970 | }, 971 | "outputs": [ 972 | { 973 | "name": "stdout", 974 | "output_type": "stream", 975 | "text": [ 976 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 977 | "1466202938 XX:XX:XX:XX:XX:XX XXX.XXX.XXX.66 ubuntu *\r\n", 978 | "\u001b[0m\r\n" 979 | ] 980 | } 981 | ], 982 | "source": [ 983 | "!ansible -b -a \"grep {vmmac} /var/lib/dnsmasq/dnsmasq.leases\" {target_group}" 984 | ] 985 | }, 986 | { 987 | "cell_type": "code", 988 | "execution_count": 25, 989 | "metadata": { 990 | "ExecuteTime": { 991 | "end_time": "2016-04-26T08:13:10.809418", 992 | "start_time": "2016-04-26T08:13:09.652344" 993 | }, 994 | "collapsed": false 995 | }, 996 | "outputs": [ 997 | { 998 | "data": { 999 | "text/plain": [ 1000 | "'XXX.XXX.XXX.66'" 1001 | ] 1002 | }, 1003 | "execution_count": 25, 1004 | "metadata": {}, 1005 | "output_type": "execute_result" 1006 | } 1007 | ], 1008 | "source": [ 1009 | "leases_stdio = !ansible -b -a \"grep {vmmac} /var/lib/dnsmasq/dnsmasq.leases\" {target_group}\n", 1010 | "ip_pattern = re.compile(r'.*\\s([0-9a-f\\:]+)\\s+([0-9\\.]+)\\s.*')\n", 1011 | "ipaddr = [ip_pattern.match(line).group(2) for line in leases_stdio if ip_pattern.match(line)][0]\n", 1012 | "ipaddr" 1013 | ] 1014 | }, 1015 | { 1016 | "cell_type": "markdown", 1017 | "metadata": {}, 1018 | "source": [ 1019 | "このIPアドレスに対して操作すればよい・・・疎通しているか、確認する。\n", 1020 | "\n", 1021 | "(VMには、このNotebook環境から疎通するIPアドレスが振られることを想定している。)" 1022 | ] 1023 | }, 1024 | { 1025 | "cell_type": "code", 1026 | "execution_count": 26, 1027 | "metadata": { 1028 | "ExecuteTime": { 1029 | "end_time": "2016-04-26T08:13:14.661634", 1030 | "start_time": "2016-04-26T08:13:11.494709" 1031 | }, 1032 | "collapsed": false 1033 | }, 1034 | "outputs": [ 1035 | { 1036 | "name": "stdout", 1037 | "output_type": "stream", 1038 | "text": [ 1039 | "PING XXX.XXX.XXX.66 (XXX.XXX.XXX.66) 56(84) bytes of data.\n", 1040 | "64 bytes from XXX.XXX.XXX.66: icmp_seq=1 ttl=63 time=7.12 ms\n", 1041 | "64 bytes from XXX.XXX.XXX.66: icmp_seq=2 ttl=63 time=0.433 ms\n", 1042 | "64 bytes from XXX.XXX.XXX.66: icmp_seq=3 ttl=63 time=0.413 ms\n", 1043 | "64 bytes from XXX.XXX.XXX.66: icmp_seq=4 ttl=63 time=0.406 ms\n", 1044 | "\n", 1045 | "--- XXX.XXX.XXX.66 ping statistics ---\n", 1046 | "4 packets transmitted, 4 received, 0% packet loss, time 3001ms\n", 1047 | "rtt min/avg/max/mdev = 0.406/2.093/7.122/2.903 ms\n" 1048 | ] 1049 | } 1050 | ], 1051 | "source": [ 1052 | "!ping -c 4 {ipaddr}" 1053 | ] 1054 | }, 1055 | { 1056 | "cell_type": "markdown", 1057 | "metadata": {}, 1058 | "source": [ 1059 | "## 仮想マシンの設定変更\n", 1060 | "\n" 1061 | ] 1062 | }, 1063 | { 1064 | "cell_type": "markdown", 1065 | "metadata": {}, 1066 | "source": [ 1067 | "### Ansible操作用ユーザの作成\n", 1068 | "\n", 1069 | "ユーザ `ansible` でAnsibleの操作が可能なよう、設定変更をおこなう。" 1070 | ] 1071 | }, 1072 | { 1073 | "cell_type": "code", 1074 | "execution_count": 27, 1075 | "metadata": { 1076 | "ExecuteTime": { 1077 | "end_time": "2016-04-26T08:13:20.068089", 1078 | "start_time": "2016-04-26T08:13:19.903098" 1079 | }, 1080 | "collapsed": false 1081 | }, 1082 | "outputs": [ 1083 | { 1084 | "name": "stdout", 1085 | "output_type": "stream", 1086 | "text": [ 1087 | "XXX.XXX.XXX.66\r\n" 1088 | ] 1089 | } 1090 | ], 1091 | "source": [ 1092 | "import os\n", 1093 | "snapshot_hosts = os.path.join(work_dir, 'init-hosts')\n", 1094 | "with open(snapshot_hosts, 'w') as f:\n", 1095 | " f.write('{address}\\n'.format(address=ipaddr)) \n", 1096 | "!cat { snapshot_hosts }" 1097 | ] 1098 | }, 1099 | { 1100 | "cell_type": "markdown", 1101 | "metadata": {}, 1102 | "source": [ 1103 | "Ansible経由でpingできるかの確認をする。" 1104 | ] 1105 | }, 1106 | { 1107 | "cell_type": "code", 1108 | "execution_count": 28, 1109 | "metadata": { 1110 | "ExecuteTime": { 1111 | "end_time": "2016-04-26T08:13:22.208916", 1112 | "start_time": "2016-04-26T08:13:20.827441" 1113 | }, 1114 | "collapsed": false 1115 | }, 1116 | "outputs": [ 1117 | { 1118 | "name": "stdout", 1119 | "output_type": "stream", 1120 | "text": [ 1121 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS => {\r\n", 1122 | " \"changed\": false, \r\n", 1123 | " \"ping\": \"pong\"\r\n", 1124 | "}\u001b[0m\r\n" 1125 | ] 1126 | } 1127 | ], 1128 | "source": [ 1129 | "!ansible -m ping -i { snapshot_hosts } all" 1130 | ] 1131 | }, 1132 | { 1133 | "cell_type": "markdown", 1134 | "metadata": {}, 1135 | "source": [ 1136 | "設定変更用のPlaybookを生成する。" 1137 | ] 1138 | }, 1139 | { 1140 | "cell_type": "code", 1141 | "execution_count": 29, 1142 | "metadata": { 1143 | "ExecuteTime": { 1144 | "end_time": "2016-04-26T08:13:32.667236", 1145 | "start_time": "2016-04-26T08:13:32.485913" 1146 | }, 1147 | "collapsed": false 1148 | }, 1149 | "outputs": [ 1150 | { 1151 | "name": "stdout", 1152 | "output_type": "stream", 1153 | "text": [ 1154 | "- hosts: all\r\n", 1155 | " become: yes\r\n", 1156 | " tasks:\r\n", 1157 | " - command: chown ansible:ansible -R /home/ansible/.ssh\r\n", 1158 | " - command: passwd -l ansible\r\n" 1159 | ] 1160 | } 1161 | ], 1162 | "source": [ 1163 | "pub_key = None\n", 1164 | "with open(os.path.expanduser('~/.ssh/ansible_id_rsa.pub'), 'r') as f:\n", 1165 | " pub_key = f.readlines()[0].strip()\n", 1166 | "\n", 1167 | "playbook_inject_key = os.path.join(work_dir, 'playbook_inject-key.yml')\n", 1168 | "with open(playbook_inject_key, 'w') as f:\n", 1169 | " f.write('- hosts: all\\n')\n", 1170 | " f.write(' become: yes\\n')\n", 1171 | " f.write(' tasks:\\n')\n", 1172 | " f.write(' - command: chown ansible:ansible -R /home/ansible/.ssh\\n')\n", 1173 | " f.write(' - command: passwd -l ansible\\n')\n", 1174 | " \n", 1175 | "!cat { playbook_inject_key }" 1176 | ] 1177 | }, 1178 | { 1179 | "cell_type": "markdown", 1180 | "metadata": {}, 1181 | "source": [ 1182 | "Playbookを実行する。" 1183 | ] 1184 | }, 1185 | { 1186 | "cell_type": "code", 1187 | "execution_count": 30, 1188 | "metadata": { 1189 | "ExecuteTime": { 1190 | "end_time": "2016-04-26T08:13:47.462042", 1191 | "start_time": "2016-04-26T08:13:45.208484" 1192 | }, 1193 | "collapsed": false, 1194 | "run_control": { 1195 | "marked": false 1196 | } 1197 | }, 1198 | "outputs": [ 1199 | { 1200 | "name": "stdout", 1201 | "output_type": "stream", 1202 | "text": [ 1203 | "\n", 1204 | "PLAY [all] *********************************************************************\n", 1205 | "\n", 1206 | "TASK [setup] *******************************************************************\n", 1207 | "\u001b[0;32mok: [XXX.XXX.XXX.66]\u001b[0m\n", 1208 | "\n", 1209 | "TASK [command] *****************************************************************\n", 1210 | "\u001b[0;33mchanged: [XXX.XXX.XXX.66]\u001b[0m\n", 1211 | "\u001b[1;35m [WARNING]: Consider using file module with owner rather than running chown\n", 1212 | "\u001b[0m\n", 1213 | "\n", 1214 | "TASK [command] *****************************************************************\n", 1215 | "\u001b[0;33mchanged: [XXX.XXX.XXX.66]\u001b[0m\n", 1216 | "\n", 1217 | "PLAY RECAP *********************************************************************\n", 1218 | "\u001b[0;33mXXX.XXX.XXX.66\u001b[0m : \u001b[0;32mok\u001b[0m\u001b[0;32m=\u001b[0m\u001b[0;32m3\u001b[0m \u001b[0;33mchanged\u001b[0m\u001b[0;33m=\u001b[0m\u001b[0;33m2\u001b[0m unreachable=0 failed=0 \n", 1219 | "\n" 1220 | ] 1221 | } 1222 | ], 1223 | "source": [ 1224 | "!ansible-playbook -i { snapshot_hosts } { playbook_inject_key }" 1225 | ] 1226 | }, 1227 | { 1228 | "cell_type": "markdown", 1229 | "metadata": {}, 1230 | "source": [ 1231 | "これで、ユーザ `ansible` でパスワードログインできない状態になった。" 1232 | ] 1233 | }, 1234 | { 1235 | "cell_type": "markdown", 1236 | "metadata": {}, 1237 | "source": [ 1238 | "### udevのネットワーク定義の修正\n", 1239 | "\n", 1240 | "udevの定義が存在しないことを確認しておく。" 1241 | ] 1242 | }, 1243 | { 1244 | "cell_type": "code", 1245 | "execution_count": 31, 1246 | "metadata": { 1247 | "collapsed": false 1248 | }, 1249 | "outputs": [ 1250 | { 1251 | "name": "stdout", 1252 | "output_type": "stream", 1253 | "text": [ 1254 | "\u001b[0;31mXXX.XXX.XXX.66 | FAILED | rc=1 >>\r\n", 1255 | "cat: /etc/udev/rules.d/70-persistent-net.rules: No such file or directory\r\n", 1256 | "\u001b[0m\r\n" 1257 | ] 1258 | } 1259 | ], 1260 | "source": [ 1261 | "!ansible -a 'cat /etc/udev/rules.d/70-persistent-net.rules' -i { snapshot_hosts } all" 1262 | ] 1263 | }, 1264 | { 1265 | "cell_type": "markdown", 1266 | "metadata": {}, 1267 | "source": [ 1268 | "## VMイメージファイルへの同期" 1269 | ] 1270 | }, 1271 | { 1272 | "cell_type": "code", 1273 | "execution_count": 32, 1274 | "metadata": { 1275 | "collapsed": false 1276 | }, 1277 | "outputs": [ 1278 | { 1279 | "name": "stdout", 1280 | "output_type": "stream", 1281 | "text": [ 1282 | "\u001b[0;32mXXX.XXX.XXX.66 | SUCCESS | rc=0 >>\r\n", 1283 | "\r\n", 1284 | "\u001b[0m\r\n" 1285 | ] 1286 | } 1287 | ], 1288 | "source": [ 1289 | "!ansible -a 'sync' -i { snapshot_hosts } all" 1290 | ] 1291 | }, 1292 | { 1293 | "cell_type": "markdown", 1294 | "metadata": {}, 1295 | "source": [ 1296 | "# VM定義の保存\n", 1297 | "\n", 1298 | "VM複製用に、XML定義を得ておく。" 1299 | ] 1300 | }, 1301 | { 1302 | "cell_type": "code", 1303 | "execution_count": 33, 1304 | "metadata": { 1305 | "collapsed": false, 1306 | "scrolled": true 1307 | }, 1308 | "outputs": [ 1309 | { 1310 | "name": "stdout", 1311 | "output_type": "stream", 1312 | "text": [ 1313 | "\r\n", 1314 | " \r\n", 1315 | " 1048576\r\n", 1316 | " 1048576\r\n", 1317 | " 1\r\n", 1318 | " \r\n", 1319 | " hvm\r\n", 1320 | " \r\n", 1321 | " \r\n", 1322 | " \r\n", 1323 | " \r\n", 1324 | " \r\n", 1325 | " \r\n", 1326 | " \r\n", 1327 | " \r\n", 1328 | " destroy\r\n", 1329 | " restart\r\n", 1330 | " restart\r\n", 1331 | " \r\n", 1332 | " /usr/libexec/qemu-kvm\r\n", 1333 | " \r\n", 1334 | " \r\n", 1335 | " \r\n", 1336 | " \r\n", 1337 | " \r\n", 1338 | "
\r\n", 1339 | " \r\n", 1340 | " \r\n", 1341 | " \r\n", 1342 | "
\r\n", 1343 | " \r\n", 1344 | " \r\n", 1345 | " \r\n", 1346 | " \r\n", 1347 | "
\r\n", 1348 | " \r\n", 1349 | " \r\n", 1350 | " \r\n", 1351 | " \r\n", 1352 | "
\r\n", 1353 | " \r\n", 1354 | " \r\n", 1355 | " \r\n", 1356 | " \r\n", 1357 | "
\r\n", 1358 | " \r\n", 1359 | " \r\n", 1360 | " \r\n", 1361 | "
\r\n", 1362 | " \r\n", 1363 | " \r\n", 1364 | " \r\n", 1365 | " \r\n", 1366 | "
\r\n", 1367 | " \r\n", 1368 | " \r\n", 1369 | " \r\n", 1370 | " \r\n", 1371 | " \r\n", 1372 | " \r\n", 1373 | " \r\n", 1374 | " \r\n", 1375 | " \r\n", 1376 | " \r\n", 1377 | " \r\n", 1378 | " \r\n", 1379 | " \r\n", 1380 | "
\r\n", 1381 | " \r\n", 1382 | " \r\n", 1383 | "" 1384 | ] 1385 | } 1386 | ], 1387 | "source": [ 1388 | "import xml.etree.ElementTree as ET\n", 1389 | "vmxml_s = !ansible -b -a \"virsh dumpxml {new_vmname}\" {target_group}\n", 1390 | "vmxml_s = vmxml_s[1:]\n", 1391 | "vmxml = ET.fromstring('\\n'.join(vmxml_s))\n", 1392 | "\n", 1393 | "del vmxml.attrib['id']\n", 1394 | "vmxml.remove(vmxml.find('uuid'))\n", 1395 | "intrElem = vmxml.find('devices').find('interface')\n", 1396 | "intrElem.remove(intrElem.find('target'))\n", 1397 | "intrElem.remove(intrElem.find('alias'))\n", 1398 | "\n", 1399 | "vmxml.find('name').text = ''\n", 1400 | "vmxml.find('devices').find('disk').find('source').attrib['file'] = ''\n", 1401 | "vmxml.find('devices').find('interface').find('mac').attrib['address'] = ''\n", 1402 | "\n", 1403 | "ET.ElementTree(vmxml).write(os.path.join(work_dir, 'libvirt-base.xml'))\n", 1404 | "!cat {work_dir}/libvirt-base.xml" 1405 | ] 1406 | }, 1407 | { 1408 | "cell_type": "markdown", 1409 | "metadata": {}, 1410 | "source": [ 1411 | "リモートのイメージと同じパスに保存しておく。" 1412 | ] 1413 | }, 1414 | { 1415 | "cell_type": "code", 1416 | "execution_count": 34, 1417 | "metadata": { 1418 | "collapsed": false 1419 | }, 1420 | "outputs": [ 1421 | { 1422 | "name": "stdout", 1423 | "output_type": "stream", 1424 | "text": [ 1425 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 1426 | " \"changed\": true, \r\n", 1427 | " \"checksum\": \"544dfe399f5626ed3b316b9d3b0d3041fbc0b922\", \r\n", 1428 | " \"dest\": \"/mnt/ubuntu14.04-base-vm/libvirt-base.xml\", \r\n", 1429 | " \"gid\": 0, \r\n", 1430 | " \"group\": \"root\", \r\n", 1431 | " \"md5sum\": \"6d84ff7f9786c472caa07f0ecdd3d74e\", \r\n", 1432 | " \"mode\": \"0644\", \r\n", 1433 | " \"owner\": \"root\", \r\n", 1434 | " \"size\": 2461, \r\n", 1435 | " \"src\": \"/home/ansible/.ansible/tmp/ansible-tmp-1466159801.84-99574324761647/source\", \r\n", 1436 | " \"state\": \"file\", \r\n", 1437 | " \"uid\": 0\r\n", 1438 | "}\u001b[0m\r\n" 1439 | ] 1440 | } 1441 | ], 1442 | "source": [ 1443 | "!ansible -b -m copy -a 'src={work_dir}/libvirt-base.xml dest={image_base_dir}' {target_group}" 1444 | ] 1445 | }, 1446 | { 1447 | "cell_type": "markdown", 1448 | "metadata": {}, 1449 | "source": [ 1450 | "# イメージ取得用VMの停止\n", 1451 | "\n", 1452 | "停止してBaseの作業完了・・・" 1453 | ] 1454 | }, 1455 | { 1456 | "cell_type": "code", 1457 | "execution_count": 35, 1458 | "metadata": { 1459 | "ExecuteTime": { 1460 | "end_time": "2016-04-26T08:14:08.449313", 1461 | "start_time": "2016-04-26T08:14:07.153775" 1462 | }, 1463 | "collapsed": false 1464 | }, 1465 | "outputs": [ 1466 | { 1467 | "name": "stdout", 1468 | "output_type": "stream", 1469 | "text": [ 1470 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 1471 | "Domain snapshot-vm-20160617 destroyed\r\n", 1472 | "\u001b[0m\r\n" 1473 | ] 1474 | } 1475 | ], 1476 | "source": [ 1477 | "!ansible -b -a \"virsh destroy {new_vmname}\" {target_group}" 1478 | ] 1479 | }, 1480 | { 1481 | "cell_type": "markdown", 1482 | "metadata": {}, 1483 | "source": [ 1484 | "しばらく待ってから再度 virsh listを実行すると、仮想マシンが停止してリストから消えたことがわかる。" 1485 | ] 1486 | }, 1487 | { 1488 | "cell_type": "code", 1489 | "execution_count": 36, 1490 | "metadata": { 1491 | "ExecuteTime": { 1492 | "end_time": "2016-04-26T08:14:12.198745", 1493 | "start_time": "2016-04-26T08:14:10.927748" 1494 | }, 1495 | "collapsed": false 1496 | }, 1497 | "outputs": [ 1498 | { 1499 | "name": "stdout", 1500 | "output_type": "stream", 1501 | "text": [ 1502 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 1503 | " Id Name State\r\n", 1504 | "----------------------------------------------------\r\n", 1505 | "\u001b[0m\r\n" 1506 | ] 1507 | } 1508 | ], 1509 | "source": [ 1510 | "!ansible -b -a \"virsh list\" {target_group}" 1511 | ] 1512 | }, 1513 | { 1514 | "cell_type": "markdown", 1515 | "metadata": {}, 1516 | "source": [ 1517 | "VMの定義も削除しておく。" 1518 | ] 1519 | }, 1520 | { 1521 | "cell_type": "code", 1522 | "execution_count": 37, 1523 | "metadata": { 1524 | "collapsed": false 1525 | }, 1526 | "outputs": [ 1527 | { 1528 | "name": "stdout", 1529 | "output_type": "stream", 1530 | "text": [ 1531 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 1532 | "Domain snapshot-vm-20160617 has been undefined\r\n", 1533 | "\u001b[0m\r\n" 1534 | ] 1535 | } 1536 | ], 1537 | "source": [ 1538 | "!ansible -b -a \"virsh undefine {new_vmname}\" {target_group}" 1539 | ] 1540 | }, 1541 | { 1542 | "cell_type": "markdown", 1543 | "metadata": {}, 1544 | "source": [ 1545 | "## dnsmasqの後始末\n", 1546 | "\n", 1547 | "dnsmasqのリース情報の後始末。VM用IPアドレスが潤沢にある場合は不要。" 1548 | ] 1549 | }, 1550 | { 1551 | "cell_type": "code", 1552 | "execution_count": 38, 1553 | "metadata": { 1554 | "ExecuteTime": { 1555 | "end_time": "2016-04-26T08:14:20.386679", 1556 | "start_time": "2016-04-26T08:14:19.161372" 1557 | }, 1558 | "collapsed": false 1559 | }, 1560 | "outputs": [ 1561 | { 1562 | "name": "stdout", 1563 | "output_type": "stream", 1564 | "text": [ 1565 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 1566 | "1466202938 XX:XX:XX:XX:XX:XX XXX.XXX.XXX.66 ubuntu *\r\n", 1567 | "\u001b[0m\r\n" 1568 | ] 1569 | } 1570 | ], 1571 | "source": [ 1572 | "!ansible -a \"cat /var/lib/dnsmasq/dnsmasq.leases\" {target_group}" 1573 | ] 1574 | }, 1575 | { 1576 | "cell_type": "code", 1577 | "execution_count": 39, 1578 | "metadata": { 1579 | "ExecuteTime": { 1580 | "end_time": "2016-04-26T08:14:28.141800", 1581 | "start_time": "2016-04-26T08:14:26.902022" 1582 | }, 1583 | "collapsed": false 1584 | }, 1585 | "outputs": [ 1586 | { 1587 | "name": "stdout", 1588 | "output_type": "stream", 1589 | "text": [ 1590 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 1591 | " \"backup\": \"\", \r\n", 1592 | " \"changed\": true, \r\n", 1593 | " \"found\": 1, \r\n", 1594 | " \"msg\": \"1 line(s) removed\"\r\n", 1595 | "}\u001b[0m\r\n" 1596 | ] 1597 | } 1598 | ], 1599 | "source": [ 1600 | "!ansible -b -m lineinfile -a \"dest=/var/lib/dnsmasq/dnsmasq.leases regexp='^.*\\s+{ ipaddr }\\s+.*' state=absent\" {target_group}" 1601 | ] 1602 | }, 1603 | { 1604 | "cell_type": "code", 1605 | "execution_count": 40, 1606 | "metadata": { 1607 | "ExecuteTime": { 1608 | "end_time": "2016-04-26T08:14:29.858483", 1609 | "start_time": "2016-04-26T08:14:28.634401" 1610 | }, 1611 | "collapsed": false 1612 | }, 1613 | "outputs": [ 1614 | { 1615 | "name": "stdout", 1616 | "output_type": "stream", 1617 | "text": [ 1618 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 1619 | "\r\n", 1620 | "\u001b[0m\r\n" 1621 | ] 1622 | } 1623 | ], 1624 | "source": [ 1625 | "!ansible -a \"cat /var/lib/dnsmasq/dnsmasq.leases\" {target_group}" 1626 | ] 1627 | }, 1628 | { 1629 | "cell_type": "code", 1630 | "execution_count": 41, 1631 | "metadata": { 1632 | "ExecuteTime": { 1633 | "end_time": "2016-04-26T08:14:34.415883", 1634 | "start_time": "2016-04-26T08:14:32.872087" 1635 | }, 1636 | "collapsed": false 1637 | }, 1638 | "outputs": [ 1639 | { 1640 | "name": "stdout", 1641 | "output_type": "stream", 1642 | "text": [ 1643 | "\u001b[0;33mXXX.XXX.XXX.105 | SUCCESS => {\r\n", 1644 | " \"changed\": true, \r\n", 1645 | " \"name\": \"dnsmasq\", \r\n", 1646 | " \"state\": \"started\"\r\n", 1647 | "}\u001b[0m\r\n" 1648 | ] 1649 | } 1650 | ], 1651 | "source": [ 1652 | "!ansible -b -m service -a \"name=dnsmasq state=restarted\" {target_group}" 1653 | ] 1654 | }, 1655 | { 1656 | "cell_type": "markdown", 1657 | "metadata": {}, 1658 | "source": [ 1659 | "# イメージファイルの確認\n", 1660 | "\n", 1661 | "イメージファイルとXML定義が生成されていることを確認する。以下の2つのファイルがホストに作成されていればOK。\n", 1662 | "\n", 1663 | "- base.img\n", 1664 | "- libvirt-base.xml" 1665 | ] 1666 | }, 1667 | { 1668 | "cell_type": "code", 1669 | "execution_count": 42, 1670 | "metadata": { 1671 | "collapsed": false 1672 | }, 1673 | "outputs": [ 1674 | { 1675 | "name": "stdout", 1676 | "output_type": "stream", 1677 | "text": [ 1678 | "\u001b[0;32mXXX.XXX.XXX.105 | SUCCESS | rc=0 >>\r\n", 1679 | "total 3853356\r\n", 1680 | "drwxr-xr-x 2 root root 4096 Jun 17 19:36 .\r\n", 1681 | "drwxr-xr-x 6 root root 4096 Jun 17 19:16 ..\r\n", 1682 | "-rwxr-xr-x 1 root root 107374182400 Jun 17 19:36 base.img\r\n", 1683 | "-rw-r--r-- 1 root root 2461 Jun 17 19:36 libvirt-base.xml\r\n", 1684 | "\u001b[0m\r\n" 1685 | ] 1686 | } 1687 | ], 1688 | "source": [ 1689 | "!ansible -b -a \"ls -la {image_base_dir}\" {target_group}" 1690 | ] 1691 | }, 1692 | { 1693 | "cell_type": "markdown", 1694 | "metadata": {}, 1695 | "source": [ 1696 | "完了。" 1697 | ] 1698 | }, 1699 | { 1700 | "cell_type": "markdown", 1701 | "metadata": { 1702 | "collapsed": true 1703 | }, 1704 | "source": [ 1705 | "# 後始末\n", 1706 | "\n", 1707 | "一時ディレクトリを削除する。" 1708 | ] 1709 | }, 1710 | { 1711 | "cell_type": "code", 1712 | "execution_count": 43, 1713 | "metadata": { 1714 | "ExecuteTime": { 1715 | "end_time": "2016-04-26T08:14:39.273093", 1716 | "start_time": "2016-04-26T08:14:39.103511" 1717 | }, 1718 | "collapsed": true 1719 | }, 1720 | "outputs": [], 1721 | "source": [ 1722 | "!rm -fr {work_dir}" 1723 | ] 1724 | }, 1725 | { 1726 | "cell_type": "code", 1727 | "execution_count": null, 1728 | "metadata": { 1729 | "collapsed": true 1730 | }, 1731 | "outputs": [], 1732 | "source": [] 1733 | } 1734 | ], 1735 | "metadata": { 1736 | "kernelspec": { 1737 | "display_name": "Python 2", 1738 | "language": "python", 1739 | "name": "python2" 1740 | }, 1741 | "language_info": { 1742 | "codemirror_mode": { 1743 | "name": "ipython", 1744 | "version": 2 1745 | }, 1746 | "file_extension": ".py", 1747 | "mimetype": "text/x-python", 1748 | "name": "python", 1749 | "nbconvert_exporter": "python", 1750 | "pygments_lexer": "ipython2", 1751 | "version": "2.7.12" 1752 | }, 1753 | "toc": { 1754 | "toc_cell": false, 1755 | "toc_number_sections": true, 1756 | "toc_threshold": 6, 1757 | "toc_window_display": false 1758 | } 1759 | }, 1760 | "nbformat": 4, 1761 | "nbformat_minor": 0 1762 | } 1763 | --------------------------------------------------------------------------------