2022/1/26

example : sshd-keygen , change host key location

系統第一次啟動的時候,會產生 ssh key..
OpenSSH %i Server Key Generation
這是 sshd-keygen@.service,其中有
ConditionFileNotEmpty=|!/data/ssh/ssh_host_%i_key
所以已經產生的話,不會再產生一次。
另外,產生 sshd-keygen 是 /usr/libexec/openssh/sshd-keygen
這個是 script,所以修改產生 sshd_host key 的位置。

2022/1/25

bearer auth in swagger-py-codegen, implemented with flask_jwt_extended

想要做的是 : RESTful 使用 bearer (jwt)。
用 swagger-py-codegen 產生 flask control code.
然後用 flask-jwt-extended 來完成需要的bearer function.

完成的 project 在 openapi.yaml 開始。
swagger-py-codegen --swagger-doc openapi.yaml example --ui --spec
產生 python code
browser 開啟http://localhost:5000/static/swagger-ui/index.html 就可以用 swagger 測試 flask server code

大概就是:
flask_jwt_extended 自己會處理 global , static 變數問題。
所以可以在任意module import, call create_access_token, jwt_required() .. 等。 只要確定在使用前,app 啟動時 call JWTManager() 初始化,和 設定 ['JWT_SECURITY_KEY']
diff --git a/example/example/__init__.py b/example/example/__init__.py
index fe02a18..ccf02a9 100644
--- a/example/example/__init__.py
+++ b/example/example/__init__.py
@@ -2,16 +2,21 @@
 from __future__ import absolute_import

 from flask import Flask
+from flask_jwt_extended import JWTManager

 import v1

 def create_app():
     app = Flask(__name__, static_folder='static')
     app.register_blueprint(
         v1.bp,
         url_prefix='/v1')
+    app.config["JWT_SECRET_KEY"] = "mysecret"
+    JWTManager(app)
     return app

另外,codegen 預計在 scopes[] 實做 security,因為不知道怎嘛用,所以只好把 schemas.py 中,scopes = { } 的內容刪除,讓 scope 是空 dictionary
否則有yaml 中有 security 的 path。一律回 permission deny
diff --git a/example/example/v1/schemas.py b/example/example/v1/schemas.py
index 4b20894..7e54c7c 100644
--- a/example/example/v1/schemas.py
+++ b/example/example/v1/schemas.py
@@ -61,7 +61,6 @@ filters = {
 }

 scopes = {
-    ('protect', 'GET'): ['secret'],
 }

 resolver = RefResolver.from_schema(definitions)
@@ -228,4 +227,4 @@ def normalize(schema, data, required_defaults=None, resolver=None):

         return funcs[type_](schema, data)

還有就是..用 postman 測試都 OK,用 swagger-ui 卻都是 fail,開啟 chrome debugger panel,可以看到 fail 的內容是 CORS。
diff --git a/example/example/__init__.py b/example/example/__init__.py
index fe02a18..ccf02a9 100644
--- a/example/example/__init__.py
+++ b/example/example/__init__.py
@@ -2,16 +2,21 @@
 from __future__ import absolute_import

 from flask import Flask
+from flask_cors import CORS

 import v1


 def create_app():
     app = Flask(__name__, static_folder='static')
+    CORS(app)



另外,flask 官方有範例,使用 jwt package,不用 flask_jwt_extened 來做的方法: 也是一樣,做一個 decorator @token_required.

swagger-py-codegen : TypeError: generate() got an unexpected keyword argument 'spec'

是因為版本的關係
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. 
This behaviour is the source of the following dependency conflicts.
swagger-py-codegen 0.4.0 requires click<7, but you have click 8.0.3 which is incompatible.
connexion 2.10.0 requires PyYAML<6,>=5.1, but you have pyyaml 4.2b1 which is incompatible.
但是 flask 又要 >7,所以 swagger-py-codegen 跟 flask 沒有板法一起。
只好用 conda create 兩個 env

不然,swagger-py-codegen 使用click 8.0.3 的話,會說 --spec 這個參數不認識。

2022/1/21

flask_jwt_extended, bearer authentication test with postman

一樣,用 flask_jwt_extended/example/simple.py 就可以。
這個 example有
  • /login -- 產生 token
  • /protected -- 使用 token 才能 access
run 起來後,開啟 postman:
POST: /login
body
{
  "username":"test",
  "password":"test"
}
send 後,得到 response:
{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY0Mjc1MjcyNiwianRpIjoiODk4MzUwNDQtNjAxMi00OTlmLWIxZTItZmMyMzIwMzg3NTY0IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6InRlc3QiLCJuYmYiOjE2NDI3NTI3MjYsImV4cCI6MTY0Mjc1MzYyNn0.B3F-9LJ20JvcUHNxTkrsQONVPH9DGGBaD2ggUGuPd9Q"
}
把 : 後面的字串 copy 下來,等下要對 /protected GET 時,要用...
GET: /protected
Authorization: TYPE: Bearer Token
 右邊的 Token: 把剛剛 copy 的字串 貼上
send 後,response 就是
{
    "logged_in_as": "test"
}

2022/1/20

flask_jwt_extended , login example

app.py

from flask_jwt_extended import JWTManager
from flask_jwt_extended import create_access_token
from flask import Flask, jsonify, request

jwt = JWTManager()

app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'this-should-be-change'
jwt.init_app(app)

@app.route('/login',methods=['POST'])
def login():
    username = request.json.get('username',None)
    password = request.json.get('password',None)

    print( username, password)

    if username != 'test' or password != 'test':
        return jsonify({"msg":"Bad username or password"}), 401

    access_token = create_access_token(identity=username)
    print(access_token)
    return jsonify(access_token)

if __name__ == '__main__':
    app.run()

這個就是各教學文中,最簡單的範例。
在 flask-jwt-extended source 的 example 中,最簡單的 simple.py

用 python app.py 啟動之後,開啟 postman..
POST: http://localhost:5000/login
Body: raw
{
  "username":"test",
  "password":"test"
}
send. 就可以看到 response Body.. 是一堆 base64 文。

曾經遇到 error : str no decode 之類的。
是用 flask-jwt-extended 用 4.2.3 之後就沒了 (有問題的好像是 3.5.X)

2022/1/18

swagger : 包含 swagger-ui 到restful server 中

上一篇swagger codegen , disable normalize,產生 python code,也可以把 swagger-ui 也內建。
就是在 code-gen 時,加上
swagger_py_codegen --swagger-doc api.json example --ui --spec
這樣就會多產生一個 static folder,裡面就是 swagger-ui 的 server side code。
所以,一樣用 python __init__.py 啟動後,
browser 手動 開啟 http://127.0.0.1:5000/static/swagger-ui/index.html 就會出現 對應的 swagger-ui,
就可以用他來測試 你的 restful server code 了。

如果是要 run swagger-editor locally,可以直接 clone swagger-editor 下來,用 nodejs 的 http-server run 就可以了。

CORS : Cross Origin Resource Sharing. server & browser

這個是為了讓提供服務的 server,限制存取服務來源的協定。
讓提供服務的 server 決定能不能讓別的 site 存取自己的服務。
這格功能要靠自己和 browser 一起完成。

舉例來說,如果你不讓其他 site 存取你的服務,在 response 時,header 就不要加上 "Access-Control-Allow-Origin" 這一條。
其他一樣回覆。

當 browser 看到的時候,就會幫忙檢查提出申請跟回覆得是不是同一個 site,如果不是,就找找服務 site 回覆的內容有沒有 "Access-Control-Allow-Origin",
如果沒有,就在 browser 頁面顯示 error。讓這個請求失敗。

所以,要是 browser 不支援這個功能,那就會把服務器回覆的內容,正確顯示出來,就好像沒那回事一樣。
chome 可以模你這種browser:
google-chrome --disable-web-security --user-data-dir=./aaa
用這幾個 option 啟動,就會忽略這格檢查。

兒服務企要提供服務的話,就要加上 "Access-Control-Allow-Origin=*" 在回覆的 header

以 nodejs 的 http-server 為例,在啟動得時候,加上 --cors 就可以在回覆的時候加上這個。

memo : focal length , FOV and pixel

finding focal length from image size and fov

就是.. 用 pixel 來計算的話, F 的單位也變成 pixel..

2022/1/14

memo : HEIC to jpg

apt install libheic-examples

for file in *.heic; do heif-convert -q 100 $file ${file/%.heic/.jpg}; done
2024 update:

ubuntu 22.04 install libheif-examples

2022/1/12

swagger codegen , disable normalize (and install)

openapi 規定了 restful api 的描述格式 (json,xml,yml)。
然後 swagger 就做了 codegen,依據openapi 的描述文件產生 restful server 的 code。
依照 server site 使用的語言和 package,產生不同的code。

swagger 官方的 codegen 是用 java 寫的,很不方便(實際用起來就是麻煩,要裝舊版 jdk 跟 maven)。
有人用 python 寫了for python 的 codegen,支援一些python web server framework。
這篇就是用 flask 為例子。
用 "-tlp, --templates" 可以指定產出 flask/tornado/falcon 的 code,default 是 flask。

也就說:
  • 依照openapi 寫 api.json/xml/yml
  • 用 swagger_py_codegen 依照剛剛的 api.XXX 產生 flask 的 code
  • 用 python 來 run 剛剛產生的 code
這樣就有提供你寫好的 api.XXX 的 restful server 了。



依照這說明,啟動 flask restful serve
因為要 python3 (python 2.17 pip install 好,run swagger-py-codegen 會出現 No module named abc 的 error),所以用 conda
conda create -n flaskenv python=3.6
conda repo 有 flask,沒有 swagger-py-codegen。
要用 pip install,所以用 conda install pip (因為 python version 和 host 不一樣)。
再用
pip install swagger-py-codegen
然後抄上面 ref的 api.json:
{
    "swagger": "2.0",
    "info": {
        "version": "1.0.0",
        "title": "Simple API",
        "description": "A simple API to learn how to write OpenAPI Specification"
    },
    "schemes": [
        "https"
    ],
    "host": "simple.api",
    "basePath": "/openapi101",
    "paths": {
        "/persons": {
            "get": {
                "summary": "Gets some persons",
                "description": "Returns a list containing all persons.",
                "responses": {
                    "200": {
                        "description": "A list of Person",
                        "schema": {
                            "type": "array",
                            "items": {
                                "properties": {
                                    "firstName": {
                                        "type": "string"
                                    },
                                    "lastName": {
                                        "type": "string"
                                    },
                                    "username": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
就可以generate flask code:
swagger-py-codegen --swagger-doc api.json example-app
接著安裝 requirement.txt 需要的module
然後用 python run __init__.py 就可以用 browser 測試:
http://localhost:5000/openapi101/persons
因為 code gen 的 code 都是空的,所以內容只有 '[]',可以修改 person.py 的 get(self) 的 return 內容,
就會反應在 browser 上。

codegen 有做 return value 的查核,不符合 api.json 的就會被濾掉。
如果不要濾掉,就要 bypass normalize():
diff --git a/example-app/example_app/openapi101/schemas.py b/example-app/example_app/openapi101/schemas.py
index 86571b5..7ca6cbd 100644
--- a/example-app/example_app/openapi101/schemas.py
+++ b/example-app/example_app/openapi101/schemas.py
@@ -226,4 +226,4 @@ def normalize(schema, data, required_defaults=None, resolver=None):
 
         return funcs[type_](schema, data)
 
-    return _normalize(schema, data), errors
+    return data, errors

normailze( ) 會依照 api.json 內容,針對 return 內容 filter,只把符合的項目留下來。


上面的例子,只會產生 rest4 server,不會產生 swagger 的 操作頁面。
要加上--ui 才會。

加上 --spec 的話,會出現 Error:
  File "/usr/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'date' is not JSON serializable
這個要修改swagger_py_codegen 的 code 才行,參考這一篇修改code, build, install。

除此之外,用 python 3.6.9(10,11,13) 都 OK.(update: 20221219 有些3.6都不行了)
使用 3.7.13 也可以。
不過, 在同一個環境 pip install flask 的話, 會出現 Error:
Installing collected packages: zipp, typing-extensions, importlib-metadata, Werkzeug, itsdangerous, click, flask
  Attempting uninstall: click
    Found existing installation: click 6.7
    Uninstalling click-6.7:
      Successfully uninstalled click-6.7
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
swagger-py-codegen 0.4.0 requires click<7, but you have click 8.1.3 which is incompatible.
Successfully installed Werkzeug-2.1.2 click-8.1.3 flask-2.1.2 importlib-metadata-4.11.4 itsdangerous-2.1.2 typing-extensions-4.2.0 zipp-3.8.0
這樣install flask 完,run swagger_py_codegen 會出現 error 在 click
  File "/home/charles-chang/miniconda3/envs/swagger37/lib/python3.7/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
TypeError: generate() got an unexpected keyword argument 'spec'
所以...
swagger-py-codegen 跟 flask 不能同時 install 在同一個 env 中。因為要求的 click 版本不一樣。

如果在 python 2.7 下 run swagger_py_codegen ,會出現 Error:
  File "/home/charles-chang/.local/lib/python2.7/site-packages/dpath/util.py", line 1, in <module>
    from collections.abc import MutableMapping
ImportError: No module named abc

2022/12/19 更新:

這時候,用 conda create new env python=3.6 的話,pip install swagger_py_codegen 後,run codegen 已經會出錯了:
Traceback (most recent call last):
  File "/home/charles-chang/miniconda3/envs/swagger3.6/bin/swagger_py_codegen", line 5, in 
    from swagger_py_codegen import generate
  File "/home/charles-chang/miniconda3/envs/swagger3.6/lib/python3.6/site-packages/swagger_py_codegen/__init__.py", line 4, in 
    from .command import generate  # noqa
  File "/home/charles-chang/miniconda3/envs/swagger3.6/lib/python3.6/site-packages/swagger_py_codegen/command.py", line 18, in 
    from swagger_spec_validator import SwaggerValidationError
  File "/home/charles-chang/miniconda3/envs/swagger3.6/lib/python3.6/site-packages/swagger_spec_validator/__init__.py", line 1, in 
    from swagger_spec_validator.common import SwaggerValidationError
  File "/home/charles-chang/miniconda3/envs/swagger3.6/lib/python3.6/site-packages/swagger_spec_validator/common.py", line 1
    from __future__ import annotations
    ^
SyntaxError: future feature annotations is not defined
用 python 3.7 就沒問題。
另外列出 python3.7 pip install swagger_py_codegen 後的 package version:
# packages in environment at /home/charles-chang/miniconda3/envs/swagger3.7:
#
# Name                    Version                   Build  Channel
_libgcc_mutex             0.1                        main
_openmp_mutex             5.1                       1_gnu
ca-certificates           2022.10.11           h06a4308_0
certifi                   2022.9.24        py37h06a4308_0
click                     6.7                      pypi_0    pypi
dpath                     2.1.3                    pypi_0    pypi
jinja2                    3.1.2                    pypi_0    pypi
json-spec                 0.10.1                   pypi_0    pypi
jsonschema                2.6.0                    pypi_0    pypi
ld_impl_linux-64          2.38                 h1181459_1
libffi                    3.4.2                h6a678d5_6
libgcc-ng                 11.2.0               h1234567_1
libgomp                   11.2.0               h1234567_1
libstdcxx-ng              11.2.0               h1234567_1
markupsafe                2.1.1                    pypi_0    pypi
ncurses                   6.3                  h5eee18b_3
openssl                   1.1.1s               h7f8727e_0
pip                       22.3.1           py37h06a4308_0
python                    3.7.15               h7a1cb2a_1
pyyaml                    4.2b1                    pypi_0    pypi
readline                  8.2                  h5eee18b_0
setuptools                65.5.0           py37h06a4308_0
six                       1.16.0                   pypi_0    pypi
sqlite                    3.40.0               h5082296_0
swagger-py-codegen        0.4.0                    pypi_0    pypi
swagger-spec-validator    3.0.3                    pypi_0    pypi
tk                        8.6.12               h1ccaba5_0
typing-extensions         4.4.0                    pypi_0    pypi
wheel                     0.37.1             pyhd3eb1b0_0
xz                        5.2.8                h5eee18b_0
zlib                      1.2.13               h5eee18b_0

2022/1/11

portainer

ref:Install Portainer with Docker on Linux

mapping host:container 9443 跟 8000 兩個 port。
9443 是 administration port

第一次會要設定 admin password charles123
因為要 8 個字以上,所以+123

其實可以username 可以不用是 admin

2022/1/6

disable console

就是不要 boot console。
ref: 第一個 ref說,在 kernel 5.11 之後,新增了一個 char drivedr : ttyNull。
所以把 kernel 的 ttyNull enable 起來,再用 boot cmdline : console=ttynull 就可以讓系統開機的時候,不show 任何 message。
開完也不會出現 login prompt。

第二個ref,加上 quiet,但是 console= 沒有改 ttyNull 的話,還是會有 boot message,但少非常多,好像只有 Error 才會印出來。
開完一樣會 show login prompt。

2022/1/5

Raspberry PI CM4 --- 1 × PCIe 1-lane Host, Gen 2 ( 5Gbps )

原來 BCM2711 有 pci 界面,但是在 pi4 上是用來接 usb3.0 host controller 了。
在 cm4 (computer module) 就沒有接 usb3.0 ,所以 pci 有 interface。
但是 只支援 32bit 寬度。

所以有一些 pcie-sata expansion board,用來做 nas

2022/1/3

alsa , mixer

紀錄一下,這是 loyal 告訴我的,我以為要 asound.conf 有寫這個 plugin 才有,結果竟然沒寫也有。
aplat -D plug:dmix sample.wav
這樣就可以同時播放,不會有 snd device lock 的問題。