oci-help.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. #!/usr/bin/env sh
  2. trap 'onCtrlC' INT
  3. ############################################################
  4. #
  5. # 甲骨文ARM实例自动新建/升级脚本
  6. #
  7. ############################################################
  8. #====== 新建实例配置相关 ======#
  9. # 区域ID [availability_domain]
  10. Available_Domain='xxxx:AP-xxxxx-1-AD-1'
  11. # 镜像 [source_id]
  12. Image_ID='ocid1.image.oc1.ap-xxxxx-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
  13. # 子网ID [subnet_id]
  14. Subnet_ID='ocid1.subnet.oc1.ap-xxxxx-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
  15. # 公钥 [ssh_authorized_keys]
  16. SSH_Key_PUB="ssh-rsa xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ssh-key-xxxx-xx-xx"
  17. # 租户ID [compartment_id]
  18. Compartment_ID='ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
  19. # 配置 [shape]
  20. Shape='VM.Standard.A1.Flex'
  21. # CPU数目
  22. CPU=1
  23. # 内存大小(GB)
  24. RAM=6
  25. # 引导卷大小(GB)
  26. HD=50
  27. # 实例名称
  28. Instance_Name="instance-xxxx-xxxx"
  29. #====== 升级实例配置相关 ======#
  30. # 升级的实例OCID [实例详细信息页面的OCID]
  31. _Instance_ID="ocid1.instance.oc1.ap-xxxxx-1.xxxxxxxxxxxxxx"
  32. # 升级到CPU个数
  33. _CPU=4
  34. # 升级到内存大小(GB)
  35. _RAM=24
  36. #====== 新建/升级实例时间间隔 ======#
  37. # 指定一个时间范围,随机生成时间间隔。
  38. min_Time=5
  39. max_Time=30
  40. #====== OCI个人资料名称 ======#
  41. # 执行 oci setup config 配置oci时,「Enter the name of the profile you would like to create:」输入的名称,不输入直接回车名称默认为 DEFAULT。
  42. profile="DEFAULT"
  43. #====== Telegram bot 消息提醒配置相关 ======#
  44. # 发送消息提醒。0: 不发送;1: 发送
  45. SEND_MSG=1
  46. # Telegram bot token, 通过 BotFather(https://t.me/BotFather) 创建一个 Bot 获取 token。
  47. TOKEN=xxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  48. # 接收消息的Telegram ID, 通过 IDBot(https://t.me/myidbot) 获取个人 Telegram ID。
  49. CHAT_ID=xxxxxxxxx
  50. # 使用代理访问Telegram bot发送消息的API。0: 不使用;1: 使用。
  51. PROXY=0
  52. # Socks 代理
  53. PROXY_URL=socks5://127.0.0.1:1080
  54. # Http 代理
  55. #PROXY_URL=http://127.0.0.1:1087
  56. ###============================== 以下区域无需修改 ==============================###
  57. PROJECT="甲骨文 OCI 新建/升级实例"
  58. VER=1.0.0
  59. PROJECT_ENTRY="$0"
  60. LOG_DIR=./log
  61. LOG_FILE=$LOG_DIR/OCI.log
  62. NO_TIMESTAMP=0
  63. # 保存日志到文件。0:不保存;1:保存
  64. SAVE_LOG=1
  65. # Telegram bot 发送消息 API
  66. URL="https://api.telegram.org/bot${TOKEN}/sendMessage"
  67. #################################################################################
  68. # 新建实例
  69. oci_launch_instance() {
  70. oci compute instance launch --profile $profile \
  71. --availability-domain $Available_Domain \
  72. --image-id $Image_ID \
  73. --subnet-id $Subnet_ID \
  74. --shape $Shape \
  75. --assign-public-ip true \
  76. --metadata '{"ssh_authorized_keys": "'"${SSH_Key_PUB}"'"}' \
  77. --compartment-id $Compartment_ID \
  78. --shape-config '{"ocpus":'$CPU',"memory_in_gbs":'$RAM'}' \
  79. --boot-volume-size-in-gbs $HD \
  80. --display-name $Instance_Name
  81. }
  82. launch_instance() {
  83. msg_text="开始新建实例「${Instance_Name}: ${CPU}C${RAM}G」"
  84. info "$msg_text"
  85. sendMessage "$msg_text"
  86. while [ true ]; do
  87. _warn "正在尝试新建实例..."
  88. ret=$(oci_launch_instance 2>&1)
  89. #ret=${ret#*:}
  90. ret=${ret#*ServiceError:}
  91. status=$(echo "${ret}" | jq '.status' 2> /dev/null)
  92. message=$(echo "${ret}" | jq '.message' 2> /dev/null)
  93. #oci_launch_instance > ${LOG_DIR}/result.json 2>&1
  94. #sed -i '' '1d' ${LOG_DIR}/result.json
  95. #status="$(cat ${LOG_DIR}/result.json | jq '.status')"
  96. #message="$(cat ${LOG_DIR}/result.json | jq '.message')"
  97. #_info "$status, $message"
  98. msg_text="Message: ${message}, Status: ${status}"
  99. case "${status}" in
  100. 500)
  101. debug "$msg_text"
  102. ;;
  103. 429)
  104. debug "$msg_text"
  105. ;;
  106. 502)
  107. error "$msg_text"
  108. sendMessage "脚本已停止, ${msg_text}"
  109. break
  110. ;;
  111. 503)
  112. error "$msg_text"
  113. sendMessage "脚本已停止, ${msg_text}"
  114. break
  115. ;;
  116. 400)
  117. error "$msg_text"
  118. sendMessage "脚本已停止, ${msg_text}"
  119. break
  120. ;;
  121. 401)
  122. error "$msg_text"
  123. sendMessage "脚本已停止, ${msg_text}"
  124. break
  125. ;;
  126. 404)
  127. error "$msg_text"
  128. sendMessage "脚本已停止, ${msg_text}"
  129. break
  130. ;;
  131. 409)
  132. error "$msg_text"
  133. sendMessage "脚本已停止, ${msg_text}"
  134. break
  135. ;;
  136. *)
  137. if [ -n "$(echo "$ret" | grep -i "data")" ]; then
  138. # 实例新建成功
  139. text_success="实例「${Instance_Name}: ${CPU}C${RAM}G」新建成功, 实例详细信息请查看[success.json]."
  140. info "${text_success}"
  141. sendMessage "${text_success}"
  142. echo "$ret" > ./success.json 2>&1
  143. sleep 3s
  144. break
  145. exit 0
  146. else
  147. local text_error="脚本已停止, $ret"
  148. error "$text_error"
  149. sendMessage "$text_error"
  150. break
  151. exit
  152. fi
  153. ;;
  154. esac
  155. local interval=$(random_range $min_Time $max_Time)
  156. sleep $interval
  157. done
  158. }
  159. # 升级实例
  160. oci_update_instance() {
  161. oci compute instance update --profile ${profile} \
  162. --instance-id ${_Instance_ID} \
  163. --shape-config '{"ocpus":'${_CPU}',"memory_in_gbs":'${_RAM}'}' \
  164. --force
  165. }
  166. update_instance() {
  167. msg_text="开始升级实例到「${_CPU} Core CPU, ${_RAM} GB RAM」"
  168. info "$msg_text"
  169. sendMessage "$msg_text"
  170. while [ true ]; do
  171. _warn "正在尝试升级实例..."
  172. ret=$(oci_update_instance 2>&1)
  173. ret=${ret#*ServiceError:}
  174. status=$(echo "${ret}" | jq '.status' 2> /dev/null)
  175. message=$(echo "${ret}" | jq '.message' 2> /dev/null)
  176. msg_text="Message: ${message}, Status: ${status}"
  177. case "${status}" in
  178. 500)
  179. debug "$msg_text"
  180. ;;
  181. 429)
  182. debug "$msg_text"
  183. ;;
  184. 502)
  185. error "$msg_text"
  186. sendMessage "脚本已停止, ${msg_text}"
  187. break
  188. ;;
  189. 503)
  190. error "$msg_text"
  191. sendMessage "脚本已停止, ${msg_text}"
  192. break
  193. ;;
  194. 400)
  195. error "$msg_text"
  196. sendMessage "脚本已停止, ${msg_text}"
  197. break
  198. ;;
  199. 401)
  200. error "$msg_text"
  201. sendMessage "脚本已停止, ${msg_text}"
  202. break
  203. ;;
  204. 404)
  205. error "$msg_text"
  206. sendMessage "脚本已停止, ${msg_text}"
  207. break
  208. ;;
  209. 409)
  210. error "$msg_text"
  211. sendMessage "脚本已停止, ${msg_text}"
  212. break
  213. ;;
  214. *)
  215. if [ -n "$(echo "$ret" | grep -i "data")" ]; then
  216. text_success="实例已成功升级到「${_CPU} Core CPU, ${_RAM} GB RAM」, 实例详细信息请查看[success.json]."
  217. info "${text_success}"
  218. sendMessage "${text_success}"
  219. echo "$ret" > ./success.json 2>&1
  220. sleep 3s
  221. break
  222. exit 0
  223. else
  224. local text_error="脚本已停止, $ret"
  225. error "$text_error"
  226. sendMessage "$text_error"
  227. break
  228. exit
  229. fi
  230. ;;
  231. esac
  232. local interval=$(random_range $min_Time $max_Time)
  233. sleep $interval
  234. done
  235. }
  236. # 生成指定范围随机数
  237. random_range() {
  238. local min=$1
  239. local max=$2
  240. echo $((RANDOM % ($max - $min) + $min))
  241. }
  242. sendMessage() {
  243. if [ 1 -eq $SEND_MSG ]; then
  244. if [ 1 -eq $PROXY ]; then
  245. result=$(curl --connect-timeout 10 --max-time 10 -s -S -x $PROXY_URL -X POST $URL -d parse_mode=Markdown -d chat_id=${CHAT_ID} -d text="*甲骨文信息*%0A${1}" 2>&1)
  246. if [ 0 -eq $? ]; then
  247. info "Telegram 消息提醒发送成功"
  248. else
  249. error "Telegram 消息提醒发送失败, $result"
  250. fi
  251. else
  252. result=$(curl --connect-timeout 10 --max-time 10 -s -S -X POST $URL -d parse_mode=Markdown -d chat_id=${CHAT_ID} -d text="*甲骨文信息*%0A${1}" 2>&1)
  253. if [ 0 -eq $? ]; then
  254. info "Telegram 消息提醒发送成功"
  255. else
  256. error "Telegram 消息提醒发送失败, $result"
  257. fi
  258. fi
  259. fi
  260. }
  261. onCtrlC() {
  262. error "检测到「Ctrl + C」,正在终止脚本..."
  263. sendMessage "脚本已停止运行。"
  264. exit 0
  265. }
  266. version() {
  267. echo "$PROJECT"
  268. echo "v$VER"
  269. }
  270. showhelp() {
  271. version
  272. echo "Usage: $PROJECT_ENTRY <command> ... [parameters ...]
  273. Commands:
  274. -h, --help Show this help message.
  275. -v, --version Show version info.
  276. --launch Create instance.
  277. --update Update instance.
  278. Parameters:
  279. --available-domain 区域ID
  280. --image-id 系统镜像ID
  281. --subnet-id 子网ID
  282. --shape 配置类型
  283. --shape-config 配置参数:CPU个数、内存大小(GB)
  284. --boot-volume-size 引导卷大小(GB)
  285. --ssh-key-pub SSH公钥
  286. --compartment-id 租户ID
  287. --instance-name 实例名称
  288. --instance-id 实例OCID,升级实例需要。
  289. --profile 配置oci时指定的别名,默认为DEFAULT。
  290. 当一台机器上面为多个甲骨文账号配置oci时,
  291. 需要指定不同的别名区分。
  292. "
  293. }
  294. _printf_black() {
  295. printf '\33[1;30m%b\33[0m' "$1"
  296. }
  297. _printf_red() {
  298. printf '\33[1;31m%b\33[0m' "$1"
  299. }
  300. _printf_green() {
  301. printf '\33[1;32m%b\33[0m' "$1"
  302. }
  303. _printf_yellow() {
  304. printf '\33[1;33m%b\33[0m' "$1"
  305. }
  306. _printf_blue() {
  307. printf '\33[1;34m%b\33[0m' "$1"
  308. }
  309. _printf_purple() {
  310. printf '\33[1;35m%b\33[0m' "$1"
  311. }
  312. _printf_skyBlue() {
  313. printf '\33[1;36m%b\33[0m' "$1"
  314. }
  315. _printf_white() {
  316. printf '\33[1;37m%b\33[0m' "$1"
  317. }
  318. _printf_normal() {
  319. printf -- "%b" "$1"
  320. }
  321. _error() {
  322. if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
  323. printf -- "%s" "[$(date '+%Y-%m-%d %H:%M:%S')] " >&2
  324. fi
  325. if [ -z "$2" ]; then
  326. _printf_red "$1" >&2
  327. else
  328. _printf_red "$1='$2'" >&2
  329. fi
  330. printf "\n" >&2
  331. return 1
  332. }
  333. _warn() {
  334. _exitstatus="$?"
  335. if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
  336. printf -- "%s" "[$(date '+%Y-%m-%d %H:%M:%S')] " >&2
  337. fi
  338. if [ -z "$2" ]; then
  339. _printf_yellow "$1" >&2
  340. else
  341. _printf_yellow "$1='$2'" >&2
  342. fi
  343. printf "\n" >&2
  344. # return the saved exit status
  345. return "$_exitstatus"
  346. }
  347. _info() {
  348. _exitstatus="$?"
  349. if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
  350. printf -- "%s" "[$(date '+%Y-%m-%d %H:%M:%S')] "
  351. fi
  352. if [ -z "$2" ]; then
  353. _printf_green "$1"
  354. else
  355. _printf_green "$1='$2'"
  356. fi
  357. printf "\n"
  358. return "$_exitstatus"
  359. }
  360. _debug() {
  361. _exitstatus="$?"
  362. if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
  363. printf -- "%s" "[$(date '+%Y-%m-%d %H:%M:%S')] "
  364. fi
  365. if [ -z "$2" ]; then
  366. _printf_normal "$1"
  367. else
  368. _printf_normal "$1='$2'"
  369. fi
  370. printf "\n"
  371. return "$_exitstatus"
  372. }
  373. error() {
  374. if [ $SAVE_LOG -eq 1 ]; then
  375. _error "$1" 2>&1 | tee -a $LOG_FILE
  376. return
  377. fi
  378. _error "$1"
  379. }
  380. warn() {
  381. if [ $SAVE_LOG -eq 1 ]; then
  382. _warn "$1" 2>&1 | tee -a $LOG_FILE
  383. return
  384. fi
  385. _warn "$1"
  386. }
  387. info() {
  388. if [ $SAVE_LOG -eq 1 ]; then
  389. _info "$1" 2>&1 | tee -a $LOG_FILE
  390. return
  391. fi
  392. _info "$1"
  393. }
  394. debug() {
  395. if [ $SAVE_LOG -eq 1 ]; then
  396. _debug "$1" 2>&1 | tee -a $LOG_FILE
  397. return
  398. fi
  399. _debug "$1"
  400. }
  401. install_JQ() {
  402. _warn "正在安装JQ..."
  403. if [ `uname` = 'Darwin' ]; then
  404. if [ "$(command -v brew)" ]; then
  405. # 使用brew安装jq
  406. brew install jq
  407. else
  408. # brew未安装
  409. _error "请手动安装Homebrew"
  410. exit
  411. fi
  412. elif [ $(uname) = 'Linux' ]; then
  413. source /etc/os-release
  414. case $ID in
  415. debian | ubuntu)
  416. sudo apt-get update -y
  417. sudo apt-get install jq -y
  418. ;;
  419. centos)
  420. sudo yum install epel-release -y
  421. sudo yum install jq -y
  422. ;;
  423. *)
  424. _error "请手动安装jq"
  425. exit
  426. ;;
  427. esac
  428. else
  429. _error "请手动安装jq"
  430. exit
  431. fi
  432. }
  433. _init() {
  434. _info "${PROJECT} 脚本正在启动..."
  435. if ! [ -d ./log/ ]; then
  436. _info "创建日志目录"
  437. mkdir ${LOG_DIR}
  438. fi
  439. # 检查oci命令行工具是否安装
  440. if [ -z "$(command -v oci)" ]; then
  441. _error "oci命令行工具未安装, 请手动安装"
  442. exit
  443. fi
  444. # 检查jq是否安装
  445. if [ -z "$(command -v jq)" ]; then
  446. install_JQ
  447. fi
  448. }
  449. _process() {
  450. _CMD=""
  451. while [ ${#} -gt 0 ]; do
  452. case "${1}" in
  453. --help | -h)
  454. showhelp
  455. return
  456. ;;
  457. --version | -v)
  458. version
  459. return
  460. ;;
  461. --launch)
  462. _CMD="launch"
  463. ;;
  464. --update)
  465. _CMD="update"
  466. ;;
  467. --available-domain)
  468. Available_Domain=$2
  469. shift
  470. ;;
  471. --image-id)
  472. Image_ID=$2
  473. shift
  474. ;;
  475. --subnet-id)
  476. Subnet_ID=$2
  477. shift
  478. ;;
  479. --shape)
  480. Shape=$2
  481. shift
  482. ;;
  483. --ssh-key-pub)
  484. SSH_Key_PUB=$2
  485. shift
  486. ;;
  487. --compartment-id)
  488. Compartment_ID=$2
  489. shift
  490. ;;
  491. --shape-config)
  492. CPU=$2
  493. RAM=$3
  494. _CPU=$2
  495. _RAM=$3
  496. shift 2
  497. ;;
  498. --boot-volume-size)
  499. HD=$2
  500. shift
  501. ;;
  502. --instance-name)
  503. Instance_Name=$2
  504. shift
  505. ;;
  506. --profile)
  507. profile=$2
  508. shift
  509. ;;
  510. --instance-id)
  511. _Instance_ID=$2
  512. shift
  513. ;;
  514. *)
  515. _error "Unknown parameter : $1"
  516. return 1
  517. ;;
  518. esac
  519. shift 1
  520. done
  521. _init
  522. case "${_CMD}" in
  523. launch) launch_instance ;;
  524. update) update_instance ;;
  525. *)
  526. if [ "$_CMD" ]; then
  527. _error "Invalid command: $_CMD"
  528. fi
  529. showhelp
  530. return 1
  531. ;;
  532. esac
  533. }
  534. _startswith() {
  535. _str="$1"
  536. _sub="$2"
  537. echo "$_str" | grep "^$_sub" >/dev/null 2>&1
  538. }
  539. main() {
  540. [ -z "$1" ] && showhelp && return
  541. if _startswith "$1" '-'; then _process "$@"; else "$@"; fi
  542. }
  543. main "$@"