在上一章中,我們發現無法直接從 odoo CLI 進行資料庫的初始化,但我又不想每次都手動設定。因此,我決定寫一個 Python 腳本,透過發送 POST 請求來取代在網頁上手動設定的步驟。並且這個腳本設計可以在主機上對著容器執行,也可以放在容器內自行執行,方便完成資料庫初始化。
原本預期這個腳本應該很簡單,不知道為什麼寫完後變得這麼長 XD,大概是因為有從檔案中讀取 admin_passwd
之類的功能。
這個腳本的預設值都是從 odoo
容器中直接創建資料庫的值,像是 localhost:8069
,還有從 /etc/odoo/odoo.conf
讀取密碼,但也可以使用參數的方式覆蓋這些連線設定。
import argparse
import requests
import urllib3
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import configparser
import os
import sys
def read_master_password_from_file(conf_file):
"""從 odoo.conf 檔案中讀取 master password。"""
if not os.path.exists(conf_file):
# 檔案不存在,拋出錯誤
raise FileNotFoundError(f"The file {conf_file} does not exist.")
config = configparser.ConfigParser()
with open(conf_file, 'r') as f:
content = f.read()
# 檢查檔案是否包含 section header
if not any(line.strip().startswith('[') and line.strip().endswith(']') for line in content.splitlines()):
# 沒有 section header,加入一個假 section
content = '[dummy_section]\n' + content
config.read_string(content)
section = 'dummy_section'
else:
# 檔案有 section,正常讀取
config.read_string(content)
# 嘗試找到包含 'admin_passwd' 的 section
section = None
for sect in config.sections():
if config.has_option(sect, 'admin_passwd'):
section = sect
break
if not section:
# 找不到 'admin_passwd',拋出錯誤
raise ValueError(f"Master password (admin_passwd) not found in any section of {conf_file}.")
try:
# 從對應 section 中取得 master password
master_pwd = config.get(section, 'admin_passwd')
return master_pwd
except configparser.NoOptionError:
# 無法取得 admin_passwd,拋出錯誤
raise ValueError(f"Master password (admin_passwd) not found in section '{section}' of {conf_file}.")
def create_odoo_db(master_pwd, db_name, login, password, lang, country_code, phone, url, ignore_ssl):
# 如果選擇忽略 SSL 驗證,關閉 SSL 警告
if ignore_ssl:
urllib3.disable_warnings(InsecureRequestWarning)
# 確保 URL 以正確的格式結尾
if not url.startswith('http'):
# 如果未指定協議,預設為 http
url = f'http://{url}'
# 將 URL 末端補上正確的路徑
if not url.endswith('/web/database/create'):
url = f'{url.rstrip("/")}/web/database/create'
# 準備要發送的 POST 請求資料
payload = {
'master_pwd': master_pwd, # 使用者的 master password
'name': db_name, # 要建立的資料庫名稱
'login': login, # 新資料庫的管理員帳號
'password': password, # 管理員的密碼
'lang': lang, # 資料庫的語言
'country_code': country_code, # 資料庫的國家代碼
'phone': phone # 管理員的電話號碼
}
# 透過 POST 發送請求
try:
response = requests.post(url, data=payload, verify=not ignore_ssl)
# 檢查請求是否成功
if response.status_code == 200:
# 如果 response 包含錯誤訊息,判斷是否是因為密碼錯誤
if "Database creation error: Access Denied" in response.text:
print("Database creation failed: Access Denied. The master password is incorrect.")
else:
print("Database created successfully!")
else:
print(f"Failed to create database. Status code: {response.status_code}")
# 顯示伺服器回應的錯誤內容
print(f"Response: {response.text}")
except requests.exceptions.SSLError as ssl_error:
print(f"SSL error occurred: {ssl_error}")
if __name__ == '__main__':
# 設定參數解析器
parser = argparse.ArgumentParser(description="Create an Odoo database using curl.")
# Master password 的設定,可以直接提供或從檔案中讀取
parser.add_argument('-m', '--mpwd', help="Master Password for the Odoo instance. Cannot be used with --mpwd_file.")
parser.add_argument('-f', '--mpwd_file', help="Path to the odoo.conf file containing the Master Password. Cannot be used with --mpwd.")
# 其他參數:資料庫名稱、管理員帳號、管理員密碼、語言、國家代碼、電話
parser.add_argument('--db_name', default='odoo', help="Database name to create (default: 'odoo')")
parser.add_argument('--login', default='admin', help="Admin login for the new database (default: 'admin')")
parser.add_argument('--password', default='admin', help="Admin Password for the new database (default: 'admin')")
parser.add_argument('--lang', default='en_US', help="Language for the new database (default: 'en_US')")
parser.add_argument('--country_code', default='tw', help="Country code for the new database (default: 'tw')")
parser.add_argument('--phone', default='+886987654321', help="Phone number for the admin user (default: '+886987654321')")
# 更新 URL 的說明,允許指定 http 或 https
parser.add_argument('-u', '--url', default='localhost:8069',
help="Base URL for Odoo (default: 'localhost:8069'). You can provide the URL in the format 'localhost:8069' "
"and the script will convert it to 'http://localhost:8069/web/database/create'. If you want to use https, "
"provide the URL like 'https://localhost:443', and it will go to 'https://localhost:443/web/database/create'.")
# 忽略 SSL 驗證錯誤的選項
parser.add_argument('--ignore_ssl', action='store_true',
help="Ignore SSL certificate verification errors. Use this if you encounter certificate errors when using HTTPS.")
args = parser.parse_args()
# 確保不能同時使用 --mpwd 和 --mpwd_file
if args.mpwd and args.mpwd_file:
print("Error: --mpwd and --mpwd_file cannot be used at the same time.")
sys.exit(1)
# 判斷要使用的 master password
if args.mpwd:
master_pwd = args.mpwd
else:
if not args.mpwd_file:
args.mpwd_file = "/etc/odoo/odoo.conf" # 預設的 odoo.conf 路徑
try:
master_pwd = read_master_password_from_file(args.mpwd_file)
except Exception as e:
print(f"Error reading master password from file: {e}")
sys.exit(1)
# 呼叫建立資料庫的函數
create_odoo_db(master_pwd, args.db_name, args.login, args.password, args.lang, args.country_code, args.phone, args.url, args.ignore_ssl)
完成這個腳本後,我們需要將它放入容器中。由於我之前為了實驗編寫了一些其他腳本,於是決定將它們連同 check-db-status.py
一起放入 utils
資料夾,然後一併複製到容器中:
COPY ./utils /usr/local/bin/
別忘了修改權限為 +x
,並在腳本的第一行添加 #! /usr/bin/env python3
。這樣一來,容器啟動後,當等待使用者輸入資料庫資訊時,我們可以使用 docker-compose exec [odoo_container] create-odoo-db.py
來直接完成資料庫的設定。