HEIF→JPEG変換

ブログへの写真アップロードの件で少し考えたのが、以前のスマートフォンをiPhoneに変えたときに写真の画像ファイルフォーマットがデフォルトではjpegではなくheifになっていたので、互換性を重視するためにということでjpegに変更していた事。コマンドラインで一括変換する仕組みを導入するにあたって、改めてheifフォーマットの利点を調べてみた。ウィキペディアなどで調べてみると、画質はそのままで圧縮率が2倍以上と、保存用途としてもかなり良いため再びheifでの保存に切り替えることとする。

で、必要になってくるものがpythonでheifを扱うためのライブラリ’pyheif’

とりあえずbrewでインストール出来るということで、まずは必要なライブラリから

$ brew install libffi libheif (libde265) # 括弧はWSL2の場合

その後にpipでpythonライブラリのインストール

$ pip install pyheif

と、WSLではうまく行ったのだが、自分の環境のMacOSではpipでインストールエラーとなった。どうやらclangのインクルードパスが/opt/local/includeを要求しているのに対し、それが無いためらしい。内容を調べてみると、/opt/homebrewというディレクトリが/opt/localであれば色々解決しそうなので、試しにシンボリックリンクを張ることとした。

$ sudo ln -s /opt/homebrew /opt/local

これで旨く/opt/local/includeを見つけることが出来、pyheifライブラリがインストール出来た。

次にサイトを参考に先日のアップロード準備用リサイズプログラムwpresizeにheifデコード機能を組み込んでみた。

from PIL import Image
import pyheif

heif_file = pyheif.read("sample.heic")
image = Image.frombytes(
    heif_file.mode, 
    heif_file.size, 
    heif_file.data,
    "raw",
    heif_file.mode,
    heif_file.stride,
    )
image.save("output.jpeg", "JPEG")

ただし上記はあくまでも画像を変換するだけで、Exif情報はコピーされない。そこでpyheifからExif情報を取り出して出力JPEGファイルに付加する方法を調べてみた。

heif_file(pyheifオブジェクト)にはその他にmetadataというプロパティがあり、メタデータを格納する辞書形式(のリスト)であると説明される。リストと言っても内容は一つしかなく、そのゼロ番目の内容を調べると

type: 'Exif'
data: xxxxxx(バイナリ情報)

であったため、このdataの中身が撮影画像のExif情報だということが分かる。これを保存時に付加することでExif付きで保存することが出来る。

.....
exif = heif_file.metadata[0]['data']
.....
image.save("output.jpeg", "JPEG", exif=exif)

GPS情報だけ消したり、Exif情報を編集したいのであれば、前回同様に一度辞書形式に変換してやれば編集可能となる。

exif = heif_file.metadata[0]['data']
exif_dict = piexif.load(exif)
del exif_dict['GPS']
exif = piexif.dump(exif_dict)
image.save("output.jpeg", "JPEG", exif=exif)

結局メタデータ自体が2重に辞書保管されるような形になっていたのが分かりにくかったです。

あともう一点注意すべき点は、Exifの記載されている画像の回転情報。カメラ構え方で画像の縦横を変えて表示したいが、オリジナル画像から縦横変換をしようとすると画素の並び順から変えなければならず、再圧縮からの点からも非効率的(勝手な想像)だと思うので、Exifの情報として画像をどの様に表示するのかの向き(回転)情報を保管している。サムネイルやビューワはこの情報をもとに画像を表示している(GIMPなどの編集ソフトはこの情報をもとにするが、向きに合わせてオリジナルのピクセル情報を並び替えるのかを聞いてくる)。

そういった意味かと思うが、heif_fileで変換されたイメージはExif情報は保存されないが、Exifの回転情報をもとにピクセルを並び替えているようで、オリジナルを右90度回転した縦長画像を変換しても、縦長画像として出力される。これに対して右90度回転したExif情報を付加すると、さらに画像回転情報が加わり横倒しになった画像になる。

この辺りの仕組みを変換時に制御出来るかどうか調べられなかったので、とりあえずは変換時にオリジナルの回転処理がされるものとして、保存するExif情報のうち回転情報のみ1(正転)に戻しておくこととした。

exif = heif_file.metadata[0]['data']
exif_dict = piexif.load(exif)
del exif_dict['GPS']
exif_dict['0th'][274] = 1  # 回転情報を保持するExifアドレス(右回転の場合6になってるので正転1とする)
exif = piexif.dump(exif_dict)
image.save("output.jpeg", "JPEG", exif=exif)

対処療法的ではあるが、とりあえずこれでExifを保管し画像の向きを保持して変換出来るようになった。

前回のwpresizeに組み込んだところがこちら(masterにコミットしてしまったので前回のリンクもheif対応の物になってますw)。

pyheifオブジェクトに関しては詳しい資料が見つからなかったので、自分で調べる形になってしまった。とりあえず結果オーライで暫く運用ということで

Amazon Prime Video のスクリーンショット

コンテンツ保護と私的目的の複製のイタチごっこはまだ続いていたのかー。

ビデオテープの時代が終わりあらゆるコンテンツがデジタルとなり、あらゆる私的複製が封じられて来ました。今残っているのはダビング10対応のビデオ、BD-Rなどの保護済みのメディアへの記録。動画配信では持ち出し用のダウンロードがあるがこれらもDRMでキッチリ管理されておりユーザーによる無尽蔵な複製は出来ないようになっている。

別に不満があるわけでもなく、この辺りで落ち着くのかなと思っていたら、さらにスクリーンショットまで保護される流れになってきているっぽい。今までチョットしたメモ代わりにスクリーンショットをしていたが最近はOSアップデートされる度にコッソリ制限が追加されているようで、現在ウチの環境はWindows11とMacOS12ですが、どちらもAmazonPrimeなどDRMコンテンツのスクリーンショットが撮れないようになっている。確か昔は出来ていたような気がするが。。。

スクリーンショットといってもやり方は色々あり、OSの機能からなる(Copyキーなど)や、ビデオボードメーカー等のサードバーティーによるゲームなど配信用のキャプチャ機能もあり、つい最近までPrimeVideoなどのキャプチャが出来ていたハズ。

恐らくであるが、コロナ禍によるテレワークやオンライン飲み会などでデスクトップキャプチャーを使う機会が増えており、コレによって低品質ではあるが著作物であるコンテンツが共有できてしまうことが明るみに出てくることになり問題視されてきたという経緯もあるのかも知れない。

キャプチャ方式昔(半年くらい前)現在
Windows Copyキー不明スクリーンショット不可
NVIDIA ゲーム配信用キャプチャスクリーンショット可スクリーンショット不可
ManyCamスクリーンショット可スクリーンショット不可

現在はあらゆる手段でスクリーンショットが撮れなくなっているので、モニタ画面をスマホで撮るくらいしか方法がないということのようだ。

あと試したやり方はVMwareなどの仮想マシンを使う方法。VMwareではゲストOSの画面をキャプチャする機能が備わっているが、それを使えばホストマシンからゲストマシンで起動したAmazonPrimeビデオのSSを撮ることが出来た。当然ゲストマシン内ではDRMが効くのでSSは撮れない。古いOSを入れればイケるのかなとも思ったが、そもそも古いOSでは最新のDRMコンテンツは再生出来ないが。

ホストマシンからはゲストで表示している画面がDRMかどうかを差し当たって知る術が無いためこのような事が出来るのだと思うが、この辺りも仮想化ソフト側で対応されてしまうことが考えられ、うっかりアップデートが出来ない状況だと言える。(仮想マシン内でもモニタはHDMIに相当するもので仮想的に接続することも出来るため、仮想化ソフトの方でそのあたりを実装することでDRMコンテンツが表示されているときにキャプチャを禁止するような機能が追加出来てしまう可能性がある)

ちょっとグレーゾーンなお話でした。著作物の保護は大事だけど、あまりやり過ぎるのはチョットね。利便性を享受したいならユーザーの方でもあまりやらかさないような配慮も必要かも。

2022年明けましての投稿

新年のネタは、3Dプリンタによる鏡餅。丁度白シルクのフィラメントがあったので8cmサイズで製作しました。ミカンは本物です。A4ペーパーを二つ折りにして、裏白はインクジェット用OHPシートに印刷してそのまま乗せてますw

昨日の投稿では旅行物だけあった写真が多く、貼り付ける写真について一枚一枚、GIMPで開いてサイズ変更して上書きセーブしてアップロードという手順を取っていたため非常に面倒で時間のかかる作業だった。前々から気になっていたが何となく惰性で過ごしていた感がある。

それに加えて、こちらの環境ではSafariとWorpressのライブラリの相性が悪く、非常に反応が遅く、下手するとハングアップしてしまうので、このブログ書きの時だけMicrosoftのEdgeを起動していたりします。

その部分は差し当たっては仕方の無いことで、追々と問題を解決しなければと思うところですが、とりあえず画像のサイズ変換アップロード環境だけは早急に何とかしないと、ということで、pythonスクリプトで画像サイズ変換プログラムを自作しました(リサイズだけでたいしたことはしていない為)。

Pillowという画像ライブラリとpiexifというExif編集ライブラリを使用しています。長手方向の幅を2000以下としてファイル名を変えて(-fオプションを付けるとオーバーライト)保存する単純なプログラム。

ついでにExif情報のうちGPS情報を削除します。

まぁ自分の一眼レフカメラはGPS持ってないし、iPhoneの写真をMacからアップするときは写真をフォルダコピーした段階でGPS情報は消されているっぽいので、普通には問題無いのだが、OneDriveのカメラロールに保存された写真とかたまにGPS情報がベッタリ貼られたまま流通しがちなので注意が必要です。

Pillowとpiexifを使用したExif編集の仕組みは以下の通り。

import piexif
from PIL import Image

img = Image.open('sample.jpeg')
exif = img.info['exif']
exif_dict = piexif.load(exif).  # Exif辞書情報の取り出し

# 取り出した辞書情報を編集します
del exif_dict['GPS']     # GPS情報を削除

# 編集後保存可能なバイナリデータに戻します
exif = piexif.dump(exif_dict)

# リサイズなどした新しい画像データの作成
img_new = img.resize([with, height], Image.BICUBIC)

# 編集したExifを適用して保存します
img_new.save('output.jpeg', quality=95, exif=exif)

久々のプログラミング。そういえばMacでの開発環境は作っただけで実践は初めてだった。。。いろいろライブラリとか入れ忘れてたりして意外とドタバタしました。

ゆるキャン△スタンプラリー浜名湖コース

※ 実は昨日続きの遠州コースに行ってきたのですが、まずは書き溜めていた浜名湖コースから…(遠州コースは後日に)

新年明けて一発目ですが、いきなり昨年末の話になります。11月に引き続き掲題ゆるキャン△スタンプラリーin静岡の2回目で浜名湖コースに行ってきました。日程は12月18〜19日。名古屋から出発してまずはⒶの佐久米駅(天竜浜名湖鉄道)の”喫茶かとれあ”からスタート。全部で5カ所だが、全開の富士山周回と比べて距離が近いので余裕の日程です。まずはアニメ本編にも出たうなぎ屋さんで豪華な昼食をして、次にⒷの奥浜名湖観光協会、ここは同じく天竜浜名湖鉄道の気賀駅でアニメ本編では大判焼きを買っていた所。偶然ゆるキャン△仕様のラッピング車両にも出合いました。次のⒸは大村酒店という普通の酒屋さんーこれも本編登場なのかな。

予定では一日目は以上の3カ所でしたが、早々に回ってしまったので、2日目に予定していた弁天島の舞阪町観光協会を夕方に訪れることにしました。ここで運の良いことに弁天島赤鳥居と夕日を同時に拝めるシチュエーションに出合うことが出来ました。ちゃんと予習をしていればそういうコースにしていたのですが、これはホントに偶然のラッキーでした。

浜松のホテルで一泊して二日目は残り一カ所で余裕ということで午前中は浜松の楽器博物館Ⓕに寄り道をして、5カ所目うなぎパイファクトリーⒼ。遅めの昼食でⒽさわやかハンバーグ(ズレた時間なのにメチャメチャ混んでいた)を経由して帰宅しました。


続きを読む…

ゆるキャン△スタンプラリー

久々の旅行ネタに本件のツアーに行ってきました。といっても適当に旅行計画をたてて宿を取ってとポイントを回ってくるだけですが、、、

富士山カレー赤

あまり時間的余裕も無いので、とりあえず一番遠いコース富士山麓周遊コースを強行。

富士山麓周遊コースは左の地図の5カ所で、⑤がゴールになるため①の朝霧高原から訪れることにした。名古屋からだと東名高速道路が圧倒的に早いのだけど、せっかく来たから攻めるので中央自動車道上り線を利用することとした。

甲府南IC手前のサービスエリア双葉にて昼食。

お子様ランチ的な見た目に騙されてはいけないほど辛いカレーでした(というか元々青いルーの物が普通のカレーで赤いルーは超辛口というオチ)。

続きを読む…

テスト印刷など

3Dプリンタ購入後、各種設定や印刷環境の整理を行い、テスト印刷などをボチボチ始めている。

ガチガチにサポート材が追加されてます

まずは定番として、FF11からの吸い出しモデルのタルタルを印刷。Metasequoiaに読み込んでSTL出力。それをプリント出力用のFlashPrintにてプリンタ出力。FlashPrintでは土台やサポート材も自動で追加してくれるので、複雑な形状もあまり気にせずに印刷出来そう。但し、あまりゴチャゴチャしたサポート材だとプリントアウト終了後にサポート材を取りはずす時にモデルを破壊してしまう可能性もあり、どこかで工夫が必要なような気がする。

とりあえず完成品

完成後の物が右の写真。サポート材を除去したときに色々折れてしまった所もあり、接着剤で補修してます。フィラメントは同時購入の物ではなく、フィギュアとか仕上がりのキレイなシルク仕上げのPLAを使用しました。

製作した防湿ケース

表面がキラキラしていてとても良い感じ。通常のPLA素材とは違った質感が良いです。

また、フィラメントもボチボチ増えてきてしまい、保管方法も考えなければと、色々調査し、

https://www.youtube.com/channel/UCn23JisBcbb-Nw-3IhofaNA

↑のサイト様を参考に、保管兼引き出し用の防湿ケースの設計と製作を行った。

ケースは参考のサイト様どおりにダイソーで買ってきた300円タッパにフィラメントリール用軸とケースを立てて使うための脚、湿度計取り付けホルダをFreeCADで設計して製作しました。

その他、フィラメント引き出し用のナイロンチューブの蓋とかも製作。またナイロンチューブのケース通し穴に関しては参考サイト様ではTPU素材で製作されていたけど、こちらではホームセンターで適当なサイズのゴムグロメットを買ってきて密閉用として代用してます。

いろいろ試しながら印刷条件を見つけている段階です。タルタルフィギュアもフィギュアサイズやサポート材の数、元のモデルの微調整やら印刷時温度設定など変えながら何度かやり直してます。
だいたいノリが分かってきた所で、再び実用品も作って行こうかと思います。

3Dプリンタ購入

以前から検討していた3Dプリンタを購入しました。動機の大半が興味本位でオモチャが欲しかった程度なんですが、生活用便利治具や自在なケース類、慣れてきたらプラモデルの補助パーツの作成を考えての事なので、方式としてはFDM、またあまり大きいと自部屋での作業に向かないのである程度小さい物(金属製ラックで運用出来る程度)。

ということで、FLASHFOGEのAdventure3に決定し。日本法人の公式ページでポチりました。最低一週間で納品とのことなのでとりあえず発注だけしてボチボチ準備をしようとしていたら、何と次の日に発送通知メールが届き、その次の日の午前に佐川さんが届けに来てくれました。「ちょ、Amazonより早くね?」ということで慌てて開封。

お早い到着で
ラックにもスッキリ収まるサイズ

ルミナスのシステムラック(25mm)でスッキリ収まったけど、天板の水平がイマイチ取れていなそうなのが心配。

マニュアルを見ながら初期設定(キャリブレーション)など行い、付属のフィラメントを導入して、内蔵メモリにある単純な直方体と、使い方の良く分からないNozzle_Remover(ノズルを取り外すための治具らしい)を印刷。

20mm四方の直方体は10分程度、治具は1時間半くらいかかりました。

次に当初の目的である生活治具で、天板固定治具の印刷を行った。これは部屋で使用しているコンパクトな作業台としてキャンプ用のキッチンテーブルを使用しているが、天板がアルミのロールテーブルになっているためコレをホームセンターで買ってきたパイン材に変更している。とりあえずはロールアルミテーブルの上に乗せて使っていたが、3Dプリンタで治具を作製してアルミパイプにしっかり取り付けるという目的。

↑3mmの木ネジ4個で天板に取り付け、アルミパイプを軽くホールド出来る治具を4個作製

アルミパイプに取り付けたところ、お試し試作でPLAなので割れないように少し緩めに製作。コレでも十分に実用的だが、あまりよく外れるようなら内側にテープを貼るか、きつめのABSで製作する必要があるかも知れない。

←は使用しているキッチンテーブル、古いヤツなので、これの収納ラックとか風除け板の付いていないヤツ。メインの天板を16mmのパイン材に交換しました(水性ニスをベタベタに塗ってツヤを出してます)。

いきなり目的のブツを作ってしまったという。。。それくらいお手軽に使えるのは想定外でした。次は何を作ろうかと、フリー素材でハロウィンのカボチャお化けのデータを取ってきて作ってみたら、目と口の穴のところで材料が飛んでしまい失敗(お試しでモデルを小さくしすぎてサポート材が入れられなかったのか)。
複雑な物を作るときはまだまだ勉強が必要ッぽい(データ作成用ソフトFlashPrintの使い方を含めて)。

EC6とDOMの件

EcmaScript6(javascript)の勉強の件で、DOMの記載に関して考えてみた。以前のjavascriptではjQueryなどのライブラリを使ってDOMを簡潔に記載していたが、EC6以降も豊富なライブラリを使う機会が増えているような感じ。

しかし、折角なので、ここはライブラリを使用せずに素のEC6でDOMを分かりやすく記述出来ないものかと考えてみた。通常、素のjavascriptでDOMを記載するときは、例えばBODYタグから下を全てDOMで記述する場合

const body = document.querySelector("body"); // まずBODYタグを見つける
var title = document.createElement("h1");    // タイトル用のH1タグを生成
title.innerHtml = "page title";              // タイトル文字を設定
body.appendChild(title);                     // タイトルをBODYの子要素とする
var content = document.createElement("p");   // 本文段落用タグを生成
content.innerHTML = "page content";          // 本文を設定
body.appendChild(content);                   // 本文をBODYの子要素とする

の様に、既存のタグを見つけるか、新規タグを生成して内容を設定し、親要素に接続する作業を繰り返して文書構造を組み立てていく形になるが、階層が深くなると何が何だか分からなくなってしまう。そのあたりをjQueryでは文書構造に沿った形で記述出来て便利なのだが、”$”関数の数珠つなぎとなりなんだか違う言語のような気がして、個人的には好きにはなれなかった。そこでEC6ではライブラリを一切用いずに次のように記載してみた。

((body) => {                            // bodyを引数とした矢印関数①
  body.className='hogehoge';
  body.appendChild(((title) => {        // bodyタグへのappendChildを同様に矢印関数で記述②
    title.innerHtml = "page title";
    return title;
  })(document.createElement('h1')));
  body.appendChild(((content) => {      // 同様に本文ダグも矢印関数で生成と同時にappendChild
    content.innerHtml = "page content";
    return content;
  })(document.createElement('p')));
  return body;                          // ①関数の戻り値(これが無いと結果が空になる)
})(document.querySelector('body'))      // ①関数の引数として body = document.querySelector('body')

同様の内容を文書構造を意識した記載方法にしてみた。矢印関数を用いてタグを生成(またはquery)し、タグの内容を関数内で記載。子要素も矢印関数内で新たに矢印関数で生成したタグをその場でappendChildする。

かなり冗長な記述になるが、文書構造がHTMLに近い形で多少は見やすくなるのではと考える。

というか、そもそも文書全体をDOM化しようとするからイケないんですけどねw
あと、Lispかっ?!てくらい括弧がメチャメチャ多くなるので、エディタの力を借りないと括弧が一個違うとかでバグ探しも大変なことになりそう。。。vscodeさんありがとう!

不正アクセスログ集計ツールその後

以前の記事で不正アクセスの疑いのあるIPアドレスを集計してアクセスブロックするツールを作成し運用していることと、docker導入に当たってブロックの仕組みを再検討して運用している記事を上げていましたが、もう一つポータルサイトのWEBアプリへの不正アクセス(django管理画面やwordpressへの不正ログイン)を集計する仕組みも紹介しました。実はこの仕組みに関してもdocker化で動作しなくなっていました。

データベースに保管された不正ログイン情報(Login_History)は別コンテナのデータベースサーバーにあるので、localhostからコンテナ名に変更するだけで良いのだが、ブロック済みのIPアドレスリストを取得して新規IPアドレスのみを表示する仕組みについては、ブロックリストがホストで管理されているためコンテナ内からはアクセス出来ない。ブロックリストを保管している/etc/ufw/before.rulesをコンテナに強引にマウントしてしまう方法もあるが、あまりスマートではない。

ということでこれまた興味本位の勉強を兼ねてホスト側からdockerブリッジネットワークへのサービスとしてソケット通信でブロックリストを公開する仕組みを作成した。

  • blocklist-server
#!/usr/bin/env python3
#--*-- coding: utf-8 --*--
# 対象ネットワーク: docker bridge network([shared] 192.168.29.0/24)
# ブロックリストホスト: 192.168.29.1
# 起動(su): /usr/local/sbin/blocklistsrv.py | /usr/bin/logger -i -t blocklistsrv 2>&1 &
# 停止: ps aux | grep blocklistsrv  -> kill -9
# ポート確認: lsof -i :8765  -> kill -9

import os, sys, socket
import psutil
import argparse
import re
import numpy as np

import daemon
from daemon import pidfile

pfile = '/var/run/blocklist-server.pid'

ipset_ufw = '/etc/ufw/before.rules'
ipset_start_line = 'blacklist-ipset-S'
ipset_end_line = 'blacklist-ipset-E'

svport = 8765
svhost = '192.168.29.1'

def getArgs():
    parser = argparse.ArgumentParser('blocklist service cmd')
    parser.add_argument('cmd', nargs='?', help='(start|stop|restart)')
    parser.add_argument('-l', '--syslog', action='store_true', help='enable syslog output')
    return parser.parse_args()

args = getArgs()

ーーー省略ーーー
def blserver():
    socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socket1.bind((svhost, svport))
    socket1.listen(5)

    logger.info('Start Blocklist server. port : %d', (svport))
    while True:
        try:
            clientsocket, address = socket1.accept()
            blist = []
            logger.info(f"Connection from {address} has been established!")
            with open(ipset_ufw, 'r', encoding='utf-8') as fi:
                for line in fi:
                    if re.search(ipset_start_line, line):
                        break
                for line in fi:
                    if res:= re.match('^-A ufw-before-input -s ([0-9./]+) .*', line):
                        #logger.debug(res[1])
                        blist.append(res[1])
                    if re.search(ipset_end_line, line):
                        break
        #except KeyboardInterrupt:
        #    #clientsocket.close()
        #    exit('exit with keyboard interrupt!')
        except FileNotFoundError:
            logger.error('file not found! : %s' % (ipset_ufw))
        except PermissionError:
            logger.error('can\'t read %s, please run as superuser!' % (ipset_ufw))
        finally:
            clientsocket.send('\n'.join(blist).encode('utf-8'))
            clientsocket.close()

        ## blist 送信 

def main():
    #args = getArgs()
    #logger = getLoggerConf(args.syslog)
    if args.cmd == 'start':
        pass
    elif args.cmd == 'stop' or args.cmd == 'restart':
        # デーモンストップ
        if os.path.exists(pfile):
            try:
                logger.info(pfile + " is exits")
                pid =  open(pfile, 'r').read().rstrip()
                proc = psutil.Process(int(pid))
                proc.terminate()
                os.remove(pfile)
                logger.info('pid : %s removed.' % pid)
            except PermissionError:
                logger.error('PID File, permission error, please run as super user.')
                exit(0)
            #except:
            #    logger.error('some error occured.')
        if args.cmd == 'stop':
            exit(0)
    else:
        logger.error('operation command is one of (start|stop|restart)')
        exit(1)

    # デーモン起動
    with daemon.DaemonContext(pidfile=pidfile.TimeoutPIDLockFile(pfile)) as context:
        logger.info(context.is_open)
        blserver()


    #exit(0)



if __name__ == '__main__':
    main()


start, stop, restart機能を持つデーモンスクリプトを作成し、ubuntuホストへサービス登録

  • /usr/lib/systemd/system/blocklist.service
[Unit]
Description=blocklistsrv: this is service of ufw blocking IP address list.
After=docker.service

[Service]
ExecStart=/usr/local/sbin/blocklist-service -l start
ExecStop=/usr/local/sbin/blocklist-service -l stop
ExecReload=/usr/local/sbin/blocklist-service -l restart
Restart=no
Type=simple

[Install]
WantedBy=multi-user.target

ここでsudo systemctl start blocklistでサービスが起動される。ちなみに恒久的に起動するようにするにはsudo systemctl emable blocklistとする。

クライアントであるdocker内のdjangoのコードも下記のようにソケット対応に変更。

blocklist_socket_server = ('192.168.29.1', 8765) 

def get_blocklist():                                                                                                    
    """                                                                                                                 
    root権限でブロックリスト取得コマンドを走らせて標準入力からブロックアドレスのリストを返す                            
    """                                                                                                                 
    socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)                                                         
    socket1.connect(blocklist_socket_server)                                                                            
    bipset = [ ipv4adrset(ips) for ips in socket1.recv(4096).decode('utf-8').split('\n') ]                              
    return bipset                                                                                                       

とりあえず動作するようになったので一安心。

名前付きなUNIXソケットでも良かったけど、またソケットファイルをコンテナにマウント〜となってしまうので、dockerネットワークのゲートウェイから見せるソケットサーバーとしました。こんな感じで良いのだろうか。まぁ特に重要なサービスでもないので結果オーライで運用中です。

カボンバ追加

ということで、水槽掃除のついでに再びモッサモサのカボンバを投入して、3匹ともワチャワチャしてますw

前回とは違い、さっそくつつきはじめてますが・・・さて、いつまで持つかな〜

水槽掃除は結構汚くなるまで放置気味なだったので反省。とりあえず半分程度もとの水を残して再投入する感じで砂利とガラス面その他を掃除しました。