ラズパイの状態診断サイトを作ってみる
ユースケース
- subprocessモジュールでLinuxの各種コマンドを実行し結果をWebスタイルで表示
- コマンドの実行結果をそのまま再現(可能な限り)
実行Linuxコマンド
コマンド | 内容 |
---|---|
ip addr | インターフェースのIPアドレス |
ss | ソケット |
route | ルーティングテーブル |
vcgencmd | ラズパイ SoC温度 |
lsblk & df | ディスク情報(ブロックとディレクトリ毎の容量) |
ファイル & ディレクトリ
webapp
├─ static
| ├─ css
| | └─ style.css
| ├─ images
| | └─ raspi-logo.png
| └─ temp
| ├─ get_disk.txt
| ├─ get_ip_addr.txt
| ├─ get_route.txt
| └─ get_socket.txt
├─ templates
| ├─ get_disk.html
| ├─ get_ip_addr.html
| ├─ get_route.html
| ├─ get_socket.html
| ├─ get_temp.html
| └─ index.html
└─ webapp.py
Flask コード
デコレータ以外のコード
webapp.py
import subprocess
import re
from flask import Flask, render_template, url_for, redirect, session, request
# Running up a instance as webapp
webapp = Flask(__name__)
~
@webapp.route() # => 別途記載 : 省略
~
# Debug mode on
if __name__ == ('__main__'):
webapp.run(debug = True)
トップページ
webapp.py
# TopPage
@webapp.route('/')
def index():
main_content = 'Diagnosis of Raspberry Pi'
return render_template('index.html', main_content=main_content)
index.html
<!--- "main"部分のみ記載 --->
<div class="main">
<h2> {{main_content}} </h2>
<ul>
<h3><a href="{{ url_for('get_ip_addr') }}">Interface IP Address</a></h3>
<h3><a href="{{ url_for('get_socket') }}">Socket</a></h3>
<h3><a href="{{ url_for('get_route') }}">IP Routing Table</a></h3>
<h3><a href="{{ url_for('get_temp') }}">Temparature</a></h3>
<h3><a href="{{ url_for('get_disk') }}">Disk Usage</a></h3>
</ul>
</div>
IPアドレス
webapp.py
#Get IP address via "ip -4 address" command
@webapp.route('/get_ip_addr')
def get_ip_addr():
ipv4_addrs = subprocess.run(['ip', '-4', 'address'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
ipv4_addrs_result = ipv4_addrs.stdout.decode('utf-8')
with open('/WebApp/static/temp/get_ip_addr.txt', 'w') as f:
f.write(ipv4_addrs_result)
result_list = []
with open('/WebApp/static/temp/get_ip_addr.txt', 'r') as f:
for line in f:
if re.search('^\s*inet', line):
line0 = line.lstrip(' ').rstrip('\n').split()
result_list.append(line0)
result_list1 = []
for result in result_list:
result_list1.append((result[-1], result[1]))
result_dict = dict(result_list1)
return render_template('get_ip_addr.html', result=result_dict)
get_ip_addr.html
<!--- "main"部分のみ記載 --->
<div class="main">
<h2>Interface IP Address</h2>
<ul>
{% for if, addr in result.items()%}
<h4>
<li>{{ if }} : {{ addr }}</li>
</h4>
{% endfor %}
</ul>
</div>
ソケット
webapp.py
#Get Socket info via "ss -tu" command
@webapp.route('/get_socket')
def get_socket():
get_socket = subprocess.run(['ss', '-tu'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
cmd_result = get_socket.stdout.decode('utf-8')
with open('/WebApp/static/temp/get_socket.txt', 'w') as f:
f.write(cmd_result)
socket_list = []
with open('/WebApp/static/temp/get_socket.txt', 'r') as f:
for read_line in f:
txt_value0 = re.sub(r'\s+', ',', read_line)
txt_value1 = txt_value0.split(',')
socket_list.append(txt_value1)
socket_list1 = []
for list_value in socket_list:
socket_list1.append(list_value)
if 'Netid' in list_value:
value0 = list_value[4] + '-' + list_value[5]
value1 = list_value[6] + '-' + list_value[7]
socket_list1[0].insert(4, value0)
socket_list1[0].insert(5, value1)
socket_list1[0].remove('Local')
socket_list1[0].remove('Peer')
socket_list1[0].remove('Address:Port')
socket_list1[0].remove('Address:Port')
del socket_list1[0][-1]
return render_template('get_socket.html', result=socket_list1)
get_socket.html
<!--- "main"部分のみ記載 --->
<div class="main">
<h2>Socket</h2>
<div class="main-list">
<ul>
<table>
<thead>
<tr>
<div class="main-list">
<th colspan="7">Socket Table</th>
</div>
</tr>
</thead>
<tbody>
{% for i in result %}
<tr>
<div class="main-list">
<td>{{ i[0] }}</td>
<td>{{ i[1] }}</td>
<td>{{ i[2] }}</td>
<td>{{ i[3] }}</td>
<td>{{ i[4] }}</td>
<td>{{ i[5] }}</td>
<td>{{ i[6] }}</td>
</div>
</tr>
{% endfor %}
</tbody>
</table>
</ul>
</div>
</div>
ルーティングテーブル
webapp.py
# Get IP routing table via "route" command
@webapp.route('/get_route')
def get_route():
get_route_table = subprocess.run(['route'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
func_result = get_route_table.stdout.decode('utf-8')
with open('/WebApp/static/temp/get_route.txt', 'w') as f:
f.write(func_result)
route_table = []
with open('/WebApp/static/temp/get_route.txt', 'r') as f:
for read_line in f:
txt_value = read_line.split()
route_table.append(txt_value)
del route_table[0]
return render_template('get_route.html', result=route_table)
get_route.html
<!--- "main"部分のみ記載 --->
<div class="main">
<h2>IP Routing Table</h2>
<div class="main-list">
<ul>
<table>
<thead>
<tr>
<div class="main-list">
<th colspan="8">Routing Table</th>
</div>
</tr>
</thead>
<tbody>
{% for i in result %}
<tr>
<div class="main-list">
<td>{{ i[0] }}</td>
<td>{{ i[1] }}</td>
<td>{{ i[2] }}</td>
<td>{{ i[3] }}</td>
<td>{{ i[4] }}</td>
<td>{{ i[5] }}</td>
<td>{{ i[6] }}</td>
<td>{{ i[7] }}</td>
</div>
</tr>
{% endfor %}
</tbody>
</table>
</ul>
</div>
</div>
温度
get_temp.py
#Get SoC temparature via "vcgencmd measure_temp" command
@webapp.route('/get_temp')
def get_temp():
get_temp = subprocess.run(['vcgencmd', 'measure_temp'],
stdout = subprocess.PIPE,
stderr=subprocess.PIPE)
value0 = get_temp.stdout.decode('utf-8')
value1 = value0.split('=')
value2 = value1[1][:-2]
cmd_result = f'SoC Temparature : { value2 }C \n'
return render_template('get_temp.html', result=cmd_result)
get_temp.html
<!--- "main"部分のみ記載 --->
<div class="main">
<h2>SoC Temparature</h2>
<ul>
<h4>
<li><h3>{{result}}</h3></li>
</h4>
</ul>
</div>
ディスク容量
webapp.py
#Get disk info via "lsbk" & "df -h" command
@webapp.route('/get_disk')
def get_disk():
#Get disk info via "lsblk" command
get_block_dev = subprocess.run(['lsblk'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
lsblk_result = get_block_dev.stdout.decode('utf-8')
with open('/WebApp/static/temp/get_lsblk.txt', 'w') as f:
f.write(lsblk_result)
result_list0 = []
with open('/WebApp/static/temp/get_lsblk.txt', 'r') as f:
for line in f:
line0 = line.rstrip('\n').split()
result_list0.append(line0)
#Get disk info via "df -h" command
get_disk_usage = subprocess.run(['df', '-h'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
df_result = get_disk_usage.stdout.decode('utf-8')
with open('/WebApp/static/temp/get_df.txt', 'w') as f:
f.write(df_result)
result_list1 = []
with open('/WebApp/static/temp/get_df.txt', 'r') as f:
for line in f:
line0 = line.rstrip('\n').split()
result_list1.append(line0)
result_list2 = []
for line in result_list1:
result_list2.append(line)
if 'Filesystem' in line:
h_list = line[5] + '-' + line[6]
result_list2[0].insert(5, h_list)
result_list2[0].remove('Mounted')
result_list2[0].remove('on')
return render_template('get_disk.html', lsblk_result=result_list0,
df_result=result_list2)
get_disk.html
<!--- "main"部分のみ記載 --->
<div class="main">
<h2>Storage Capacity</h2>
<h4>Block Device</h4>
<div class="main-list">
<ul>
<table>
<thead>
<tr>
<div class="main-list">
<th colspan="7">Block Device</th>
</div>
</tr>
</thead>
<tbody>
{% for i in lsblk_result %}
<tr>
<div class="main-list">
<td>{{ i[0] }}</td>
<td>{{ i[1] }}</td>
<td>{{ i[2] }}</td>
<td>{{ i[3] }}</td>
<td>{{ i[4] }}</td>
<td>{{ i[5] }}</td>
<td>{{ i[6] }}</td>
</div>
</tr>
{% endfor %}
</tbody>
</table>
</ul>
</div>
<h4>Disk Usage</h4>
<div class="main-list">
<ul>
<table>
<thead>
<tr>
<div class="main-list">
<th colspan="6">Disk Usage</th>
</div>
</tr>
</thead>
<tbody>
{% for i in df_result %}
<tr>
<div class="main-list">
<td>{{ i[0] }}</td>
<td>{{ i[1] }}</td>
<td>{{ i[2] }}</td>
<td>{{ i[3] }}</td>
<td>{{ i[4] }}</td>
<td>{{ i[5] }}</td>
</div>
</tr>
{% endfor %}
</tbody>
</table>
</ul>
</div>
</div>
ラズパイのブランドガイドライン
Raspberry Pi trademark rules and brand guidelines
- 上記ガイドラインを遵守してロゴなどを使わせていただきました。
※ロゴイメージからラズパイサイトへのリンクもちゃんと貼っています。(記載ないけど)
まとめ
- とりあえず、第一段階としてコマンド実行の結果を表示するところまで作ってみた。
- 今後やること
- HTML、CSSの見直し/ブラッシュアップ
- 実装したいことのロードマップを作る?
- Nginx、uWSGI上でのアプリ展開
- 対応コマンドの数を増やす
- HTMLテンプレートの継承
- ユーザー認証、DB連携など
- なにか楽しそうなもの 😄✨