generate-ha-config.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #!/usr/bin/env python3
  2. import argparse
  3. from pathlib import Path
  4. def normalize_product_version(raw: str) -> str:
  5. if raw is None:
  6. return "Edge"
  7. s = str(raw).strip()
  8. if not s:
  9. return "Edge"
  10. sl = s.lower()
  11. if sl == "ai":
  12. return "AI"
  13. if sl == "cmp":
  14. return "CMP"
  15. if sl == "edge":
  16. return "Edge"
  17. if sl == "lightedge":
  18. return "LightEdge"
  19. if sl == "fullstack":
  20. return "FullStack"
  21. # Keep behavior predictable; original sh supported only these values.
  22. return s
  23. def build_yaml(product_version: str, cfg: dict) -> str:
  24. # Keep values consistent with the original generate-ha-config.sh template,
  25. # but all variables must come from CLI args (no hard-coded environment).
  26. DB_IP = cfg["db_ip"]
  27. DB_PORT = cfg["db_port"]
  28. DB_PSWD = cfg["db_password"]
  29. DB_USER = cfg["db_user"]
  30. K8S_VIP = cfg["k8s_vip"]
  31. PRIMARY_INTERFACE = cfg["primary_interface"]
  32. PRIMARY_IP = cfg["primary_ip"]
  33. MASTER_1_INTERFACE = cfg["master_1_interface"]
  34. MASTER_1_IP = cfg["master_1_ip"]
  35. MASTER_2_INTERFACE = cfg["master_2_interface"]
  36. MASTER_2_IP = cfg["master_2_ip"]
  37. REGION = cfg["region"]
  38. ZONE = cfg["zone"]
  39. version = (Path(__file__).resolve().parent / "VERSION").read_text(encoding="utf-8").strip()
  40. ai_extra_primary = ""
  41. ai_extra_master = ""
  42. if product_version == "AI":
  43. ai_extra_primary = " enable_containerd: true"
  44. ai_extra_master = " enable_containerd: true"
  45. # Note: no YAML library is used; we rely on deterministic text templates.
  46. return f"""# primary_master_node 表示运行 k8s 和 Cloudpods 服务的节点
  47. primary_master_node:
  48. # ssh login IP
  49. hostname: {PRIMARY_IP}
  50. # 不使用本地登录方式
  51. use_local: false
  52. # ssh login user
  53. user: root
  54. # cloudpods version
  55. onecloud_version: {version}
  56. # mariadb connection address
  57. db_host: "{DB_IP}"
  58. # mariadb user
  59. db_user: "{DB_USER}"
  60. # mariadb password
  61. db_password: "{DB_PSWD}"
  62. # mariadb port
  63. db_port: "{DB_PORT}"
  64. # 节点服务监听的地址,多网卡时可以指定对应网卡的地址
  65. node_ip: "{PRIMARY_IP}"
  66. # 对应 Kubernetes calico 插件默认网卡选择规则
  67. ip_autodetection_method: "can-reach={PRIMARY_IP}"
  68. # K8s 控制节点的 IP,对应keepalived 监听的 VIP
  69. controlplane_host: {K8S_VIP}
  70. # K8s 控制节点 apiserver 监听的端口
  71. controlplane_port: "6443"
  72. # 该节点作为 Cloudpods 私有云计算节点,如果不想让控制节点作为计算节点,可以设置为 false
  73. as_host: true
  74. # 虚拟机可作为 Cloudpods 内置私有云计算节点(默认为 false)。开启此项时,请确保 as_host: true
  75. as_host_on_vm: true
  76. # 产品版本,从 [Edge, CMP, FullStack] 选择一个,FullStack 会安装融合云,CMP 安装多云管理版本,Edge 安装私有云
  77. product_version: '{product_version}'
  78. {ai_extra_primary}
  79. # 服务对应的镜像仓库,如果待部署的机器不在中国大陆,可以用 dockerhub 的镜像仓库:docker.io/yunion
  80. image_repository: registry.cn-beijing.aliyuncs.com/yunion
  81. # 启用高可用模式
  82. high_availability: true
  83. # 使用 minio 作为后端虚拟机镜像存储
  84. enable_minio: true
  85. ha_using_local_registry: false
  86. # 计算节点默认网桥 br0 对应的网卡
  87. host_networks: "{PRIMARY_INTERFACE}/br0/{PRIMARY_IP}"
  88. # region 设置
  89. region: {REGION}
  90. # zone 设置
  91. zone: {ZONE}
  92. master_nodes:
  93. # 加入控制节点的 k8s vip
  94. controlplane_host: {K8S_VIP}
  95. # 加入控制节点的 K8s apiserver 端口
  96. controlplane_port: "6443"
  97. # 作为 K8s 和 Cloudpods 控制节点
  98. as_controller: true
  99. # 该节点作为 Cloudpods 私有云计算节点,如果不想让控制节点作为计算节点,可以设置为 false
  100. as_host: true
  101. # 虚拟机可作为 Cloudpods 内置私有云计算节点(默认为 false)。开启此项时,请确保 as_host: true
  102. as_host_on_vm: true
  103. {ai_extra_master}
  104. # 从 primary 节点同步 ntp 时间
  105. ntpd_server: "{PRIMARY_IP}"
  106. # 启用高可用模式
  107. high_availability: true
  108. hosts:
  109. - user: root
  110. hostname: "{MASTER_1_IP}"
  111. # 计算节点默认网桥 br0 对应的网卡
  112. host_networks: "{MASTER_1_INTERFACE}/br0/{MASTER_1_IP}"
  113. - user: root
  114. hostname: "{MASTER_2_IP}"
  115. # 计算节点默认网桥 br0 对应的网卡
  116. host_networks: "{MASTER_2_INTERFACE}/br0/{MASTER_2_IP}"
  117. """
  118. def main() -> int:
  119. parser = argparse.ArgumentParser(description="Generate config-ha.yml for HA deployment")
  120. parser.add_argument(
  121. "--product-version",
  122. dest="product_version",
  123. required=True,
  124. help="Product version: Edge/CMP/FullStack/LightEdge/AI",
  125. )
  126. # All deployment parameters are required so users can fully control the generated YAML.
  127. parser.add_argument("--db-ip", dest="db_ip", required=True, help="MARIADB db_host")
  128. parser.add_argument("--db-port", dest="db_port", required=True, type=int, help="MARIADB db_port")
  129. parser.add_argument("--db-user", dest="db_user", required=True, help="MARIADB db_user")
  130. parser.add_argument("--db-password", dest="db_password", required=True, help="MARIADB db_password")
  131. parser.add_argument("--k8s-vip", dest="k8s_vip", required=True, help="keepalived/controlplane VIP")
  132. parser.add_argument("--primary-interface", dest="primary_interface", required=True, help="primary host NIC for br0")
  133. parser.add_argument("--primary-ip", dest="primary_ip", required=True, help="primary host ssh/cluster IP")
  134. parser.add_argument("--master-1-interface", dest="master_1_interface", required=True, help="master-1 NIC for br0")
  135. parser.add_argument("--master-1-ip", dest="master_1_ip", required=True, help="master-1 hostname IP")
  136. parser.add_argument("--master-2-interface", dest="master_2_interface", required=True, help="master-2 NIC for br0")
  137. parser.add_argument("--master-2-ip", dest="master_2_ip", required=True, help="master-2 hostname IP")
  138. parser.add_argument("--region", dest="region", required=True, help="product region")
  139. parser.add_argument("--zone", dest="zone", required=True, help="product zone")
  140. args = parser.parse_args()
  141. product_version = normalize_product_version(args.product_version)
  142. print(f"Normalized PRODUCT_VERSION: {product_version}")
  143. cfg_path = Path(__file__).resolve().parent / "config-ha.yml"
  144. cfg = {
  145. "db_ip": args.db_ip,
  146. "db_port": args.db_port,
  147. "db_password": args.db_password,
  148. "db_user": args.db_user,
  149. "k8s_vip": args.k8s_vip,
  150. "primary_interface": args.primary_interface,
  151. "primary_ip": args.primary_ip,
  152. "master_1_interface": args.master_1_interface,
  153. "master_1_ip": args.master_1_ip,
  154. "master_2_interface": args.master_2_interface,
  155. "master_2_ip": args.master_2_ip,
  156. "region": args.region,
  157. "zone": args.zone,
  158. }
  159. cfg_path.write_text(build_yaml(product_version, cfg), encoding="utf-8")
  160. print(f"Write configuration: {cfg_path.name}")
  161. return 0
  162. if __name__ == "__main__":
  163. raise SystemExit(main())