colo-provider.sh 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #!/usr/bin/env bash
  2. # |source| this file
  3. #
  4. # Utilities for working with Colo instances
  5. #
  6. # COLO_PARALLELIZE is not ready for use, disable it
  7. declare COLO_PARALLELIZE=false
  8. __cloud_colo_here="$(dirname "${BASH_SOURCE[0]}")"
  9. # shellcheck source=net/scripts/colo-utils.sh
  10. source "${__cloud_colo_here}/colo-utils.sh"
  11. # Default zone
  12. cloud_DefaultZone() {
  13. echo "Denver"
  14. }
  15. cloud_DefaultCustomMemoryGB() {
  16. : # Not implemented
  17. }
  18. cloud_RestartPreemptedInstances() {
  19. : # Not implemented
  20. }
  21. #
  22. # __cloud_FindInstances
  23. #
  24. # Find instances matching the specified pattern.
  25. #
  26. # For each matching instance, an entry in the `instances` array will be added with the
  27. # following information about the instance:
  28. # "name:zone:public IP:private IP"
  29. #
  30. # filter - The instances to filter on
  31. #
  32. # examples:
  33. # $ __cloud_FindInstances "name=exact-machine-name"
  34. # $ __cloud_FindInstances "name~^all-machines-with-a-common-machine-prefix"
  35. #
  36. __cloud_FindInstances() {
  37. declare HOST_NAME IP PRIV_IP STATUS ZONE LOCK_USER INSTNAME INSTANCES_TEXT
  38. declare filter=${1}
  39. declare onlyPreemptible=${2}
  40. instances=()
  41. if ! ${COLO_PARALLELIZE}; then
  42. colo_load_resources
  43. colo_load_availability false
  44. fi
  45. INSTANCES_TEXT="$(
  46. for AVAIL in "${COLO_RES_AVAILABILITY[@]}"; do
  47. IFS=$'\x1f' read -r HOST_NAME IP PRIV_IP STATUS ZONE LOCK_USER INSTNAME PREEMPTIBLE <<<"${AVAIL}"
  48. if [[ ${INSTNAME} =~ ${filter} ]]; then
  49. if [[ -n $onlyPreemptible && $PREEMPTIBLE == "false" ]]; then
  50. continue
  51. else
  52. printf "%-40s | publicIp=%-16s privateIp=%s zone=%s preemptible=%s\n" "${INSTNAME}" "${IP}" "${PRIV_IP}" "${ZONE}" "${PREEMPTIBLE}" 1>&2
  53. echo -e "${INSTNAME}:${IP}:${PRIV_IP}:${ZONE}"
  54. fi
  55. fi
  56. done | sort -t $'\x1f' -k1
  57. )"
  58. if [[ -n "${INSTANCES_TEXT}" ]]; then
  59. while read -r LINE; do
  60. instances+=( "${LINE}" )
  61. done <<<"${INSTANCES_TEXT}"
  62. fi
  63. }
  64. #
  65. # cloud_FindInstances [namePrefix]
  66. #
  67. # Find instances with names matching the specified prefix
  68. #
  69. # For each matching instance, an entry in the `instances` array will be added with the
  70. # following information about the instance:
  71. # "name:public IP:private IP"
  72. #
  73. # namePrefix - The instance name prefix to look for
  74. #
  75. # examples:
  76. # $ cloud_FindInstances all-machines-with-a-common-machine-prefix
  77. #
  78. cloud_FindInstances() {
  79. declare filter="^${1}.*"
  80. declare onlyPreemptible="${2}"
  81. __cloud_FindInstances "${filter}" "${onlyPreemptible}"
  82. }
  83. #
  84. # cloud_FindInstance [name]
  85. #
  86. # Find an instance with a name matching the exact pattern.
  87. #
  88. # For each matching instance, an entry in the `instances` array will be added with the
  89. # following information about the instance:
  90. # "name:public IP:private IP"
  91. #
  92. # name - The instance name to look for
  93. #
  94. # examples:
  95. # $ cloud_FindInstance exact-machine-name
  96. #
  97. cloud_FindInstance() {
  98. declare name="^${1}$"
  99. declare onlyPreemptible="${2}"
  100. __cloud_FindInstances "${name}" "${onlyPreemptible}"
  101. }
  102. #
  103. # cloud_Initialize [networkName]
  104. #
  105. # Perform one-time initialization that may be required for the given testnet.
  106. #
  107. # networkName - unique name of this testnet
  108. #
  109. # This function will be called before |cloud_CreateInstances|
  110. cloud_Initialize() {
  111. # networkName=${1} # unused
  112. # zone=${2} #unused
  113. colo_load_resources
  114. if ${COLO_PARALLELIZE}; then
  115. colo_load_availability
  116. fi
  117. }
  118. #
  119. # cloud_CreateInstances [networkName] [namePrefix] [numNodes]
  120. # [machineType] [zone]
  121. # [bootDiskSize] [startupScript] [address]
  122. # [bootDiskType] [additionalDiskSize] [preemptible]
  123. #
  124. # Creates one more identical instances.
  125. #
  126. # networkName - unique name of this testnet
  127. # namePrefix - unique string to prefix all the instance names with
  128. # numNodes - number of instances to create
  129. # machineType - GCE machine type. Note that this may also include an
  130. # `--accelerator=` or other |gcloud compute instances create|
  131. # options
  132. # zone - cloud zone
  133. # bootDiskSize - Optional size of the boot disk in GB
  134. # startupScript - Optional startup script to execute when the instance boots
  135. # address - Optional name of the GCE static IP address to attach to the
  136. # instance. Requires that |numNodes| = 1 and that addressName
  137. # has been provisioned in the GCE region that is hosting `$zone`
  138. # bootDiskType - Optional specify SSD or HDD boot disk
  139. # additionalDiskSize - Optional specify size of additional storage volume
  140. # preemptible - Optionally request a preemptible instance ("true")
  141. #
  142. # Tip: use cloud_FindInstances to locate the instances once this function
  143. # returns
  144. cloud_CreateInstances() {
  145. #declare networkName="${1}" # unused
  146. declare namePrefix="${2}"
  147. declare numNodes="${3}"
  148. declare machineType="${4}"
  149. # declare zone="${5}" # unused
  150. #declare optionalBootDiskSize="${6}" # unused
  151. #declare optionalStartupScript="${7}" # unused
  152. #declare optionalAddress="${8}" # unused
  153. #declare optionalBootDiskType="${9}" # unused
  154. #declare optionalAdditionalDiskSize="${10}" # unused
  155. declare optionalPreemptible="${11}"
  156. declare sshPrivateKey="${12}"
  157. declare -a nodes
  158. if [[ ${numNodes} = 1 ]]; then
  159. nodes=("${namePrefix}")
  160. else
  161. for node in $(seq -f "${namePrefix}%0${#numNodes}g" 1 "${numNodes}"); do
  162. nodes+=("${node}")
  163. done
  164. fi
  165. if ${COLO_PARALLELIZE}; then
  166. declare HOST_NAME IP PRIV_IP STATUS ZONE LOCK_USER INSTNAME INDEX RES LINE
  167. declare -a AVAILABLE
  168. declare AVAILABLE_TEXT
  169. AVAILABLE_TEXT="$(
  170. for RES in "${COLO_RES_AVAILABILITY[@]}"; do
  171. IFS=$'\x1f' read -r HOST_NAME IP PRIV_IP STATUS ZONE LOCK_USER INSTNAME <<<"${RES}"
  172. if [[ "FREE" = "${STATUS}" ]]; then
  173. INDEX=$(colo_res_index_from_ip "${IP}")
  174. RES_MACH="${COLO_RES_MACHINE[${INDEX}]}"
  175. if colo_machine_types_compatible "${RES_MACH}" "${machineType}"; then
  176. if ! colo_node_is_requisitioned "${INDEX}" "${COLO_RES_REQUISITIONED[*]}"; then
  177. echo -e "${RES_MACH}\x1f${IP}"
  178. fi
  179. fi
  180. fi
  181. done | sort -nt $'\x1f' -k1,1
  182. )"
  183. if [[ -n "${AVAILABLE_TEXT}" ]]; then
  184. while read -r LINE; do
  185. AVAILABLE+=("${LINE}")
  186. done <<<"${AVAILABLE_TEXT}"
  187. fi
  188. if [[ ${#AVAILABLE[@]} -lt ${numNodes} ]]; then
  189. echo "Insufficient resources available to allocate ${numNodes} ${namePrefix}" 1>&2
  190. exit 1
  191. fi
  192. declare node
  193. declare AI=0
  194. for node in "${nodes[@]}"; do
  195. IFS=$'\x1f' read -r _ IP <<<"${AVAILABLE[${AI}]}"
  196. colo_node_requisition "${IP}" "${node}" >/dev/null
  197. AI=$((AI+1))
  198. done
  199. else
  200. declare RES_MACH node
  201. declare RI=0
  202. declare NI=0
  203. while [[ ${NI} -lt ${numNodes} && ${RI} -lt ${COLO_RES_N} ]]; do
  204. node="${nodes[${NI}]}"
  205. RES_MACH="${COLO_RES_MACHINE[${RI}]}"
  206. IP="${COLO_RES_IP[${RI}]}"
  207. if colo_machine_types_compatible "${RES_MACH}" "${machineType}"; then
  208. if colo_node_requisition "${IP}" "${node}" "${sshPrivateKey}" "${optionalPreemptible}" >/dev/null; then
  209. NI=$((NI+1))
  210. fi
  211. fi
  212. RI=$((RI+1))
  213. done
  214. fi
  215. }
  216. #
  217. # cloud_DeleteInstances
  218. #
  219. # Deletes all the instances listed in the `instances` array
  220. #
  221. cloud_DeleteInstances() {
  222. declare forceDelete="${1}"
  223. declare _ IP _ _
  224. for instance in "${instances[@]}"; do
  225. IFS=':' read -r _ IP _ _ <<< "${instance}"
  226. colo_node_free "${IP}" "${forceDelete}" >/dev/null
  227. done
  228. }
  229. #
  230. # cloud_WaitForInstanceReady [instanceName] [instanceIp] [instanceZone] [timeout]
  231. #
  232. # Return once the newly created VM instance is responding. This function is cloud-provider specific.
  233. #
  234. cloud_WaitForInstanceReady() {
  235. #declare instanceName="${1}" # unused
  236. #declare instanceIp="${2}" # unused
  237. #declare timeout="${4}" # unused
  238. true
  239. }
  240. #
  241. # cloud_FetchFile [instanceName] [publicIp] [remoteFile] [localFile]
  242. #
  243. # Fetch a file from the given instance. This function uses a cloud-specific
  244. # mechanism to fetch the file
  245. #
  246. cloud_FetchFile() {
  247. #declare instanceName="${1}" # unused
  248. declare publicIp="${2}"
  249. declare remoteFile="${3}"
  250. declare localFile="${4}"
  251. #declare zone="${5}" # unused
  252. scp \
  253. -o "StrictHostKeyChecking=no" \
  254. -o "UserKnownHostsFile=/dev/null" \
  255. -o "User=solana" \
  256. -o "LogLevel=ERROR" \
  257. -F /dev/null \
  258. "solana@${publicIp}:${remoteFile}" "${localFile}"
  259. }
  260. cloud_StatusAll() {
  261. declare HOST_NAME IP PRIV_IP STATUS ZONE LOCK_USER INSTNAME PREEMPTIBLE
  262. if ! ${COLO_PARALLELIZE}; then
  263. colo_load_resources
  264. colo_load_availability false
  265. fi
  266. for AVAIL in "${COLO_RES_AVAILABILITY[@]}"; do
  267. IFS=$'\x1f' read -r HOST_NAME IP PRIV_IP STATUS ZONE LOCK_USER INSTNAME PREEMPTIBLE <<<"${AVAIL}"
  268. printf "%-30s | publicIp=%-16s privateIp=%s status=%s who=%s zone=%s inst=%s preemptible=%s\n" "${HOST_NAME}" "${IP}" "${PRIV_IP}" "${STATUS}" "${LOCK_USER}" "${ZONE}" "${INSTNAME}" "${PREEMPTIBLE}"
  269. done
  270. }