Set up GIS software on multiple computers (Windows & Linux) simultaneosly using SaltStack
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

356 lines
10 KiB

6 years ago
6 years ago
6 years ago
  1. #!/bin/bash
  2. # Salt module: GIS workstation environment for multiple computers
  3. # Copyright (C) 2018 Pekka Helenius
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. ###########################################################
  18. # Salt master requirement: Ubuntu 18.04 LTS or variants
  19. # Salt minion requirement: Microsoft Windows and/or Ubuntu 18.04 LTS or variants
  20. ###########################################################
  21. # Test connection to minion computers and ask for confirmation to continue.
  22. # May be useful in some situations but consider disabling the check
  23. # in large automated cluster environments.
  24. #
  25. # If accepted minion computer can't be reached and the value is true,
  26. # user is asked for input
  27. #
  28. # Default value: true
  29. #
  30. TEST_MINION_PING="true"
  31. # Automatically accept all minion computers?
  32. # If not true, user is asked for input for each unaccepted minion ID
  33. #
  34. # Default value: true
  35. #
  36. AUTOACCEPT_MINIONS="true"
  37. # Use more verbose debug output for Salt master?
  38. #
  39. # Default value: true
  40. #
  41. SALT_DEBUG="true"
  42. # Run salt only locally (I.E. test localhost minion)?
  43. #
  44. # Another implementation would be using 'salt-call' command
  45. #
  46. # Default value: true
  47. #
  48. SALT_LOCALONLY="false"
  49. ###########################################################
  50. # Check root
  51. function checkRoot() {
  52. if [ $(id -u) -ne 0 ]; then
  53. echo "Run the script with root permissions (sudo or root)."
  54. exit 1
  55. fi
  56. }
  57. checkRoot
  58. ###########################################################
  59. # Colors
  60. bash_red='\e[91m'
  61. bash_color_default='\e[0m'
  62. ###########################################################
  63. # Check for command dependencies
  64. function checkCommands() {
  65. if [[ $(which --help 2>/dev/null) ]] && [[ $(echo --help 2>/dev/null) ]]; then
  66. COMMANDS=(grep mkdir systemctl id wget cat tee awk sed tr chmod cp rm mv touch dpkg apt-cache apt-get)
  67. a=0
  68. for command in ${COMMANDS[@]}; do
  69. if [[ ! $(which $command 2>/dev/null) ]]; then
  70. COMMANDS_NOTFOUND[$a]=$command
  71. let a++
  72. fi
  73. done
  74. if [[ -v COMMANDS_NOTFOUND ]]; then
  75. echo -e "\n${bash_red}Error:${bash_color_default} The following commands could not be found: ${COMMANDS_NOTFOUND[*]}\nAborting\n"
  76. exit 1
  77. fi
  78. else
  79. exit 1
  80. fi
  81. }
  82. checkCommands
  83. ###########################################################
  84. # Check Salt master Linux distribution
  85. if [[ -f /etc/os-release ]]; then
  86. MASTER_DISTRO=$(grep ^VERSION_ID /etc/os-release | grep -oP '(?<=")[^"]*')
  87. if [[ $MASTER_DISTRO != "18.04" ]]; then
  88. echo -e "This script is supported only on Ubuntu 18.04 LTS. Aborting.\n"
  89. exit 1
  90. fi
  91. else
  92. echo -e "Can't recognize your Linux distribution. Aborting.\n"
  93. exit 1
  94. fi
  95. ###########################################################
  96. # Welcome message
  97. function welcomeMessage() {
  98. echo -e "This script will install GIS workstation environment for multiple Ubuntu 18.04 LTS & MS Windows computers.\n\nSoftware:\n\n -QGIS\n -CloudCompare\n -LASTools\n -QuickRoute\n -Merkaartor\n -etc.\n"
  99. read -r -p "Continue? [y/N] " answer
  100. if [[ ! $(echo $answer | sed 's/ //g') =~ ^([yY][eE][sS]|[yY])$ ]]; then
  101. echo -e "Aborting.\n"
  102. exit 1
  103. fi
  104. unset answer
  105. }
  106. welcomeMessage
  107. ###########################################################
  108. # Check network connection
  109. function checkInternet() {
  110. if [[ $(echo $(wget --delete-after -q -T 5 github.com -o -)$?) -ne 0 ]]; then
  111. echo -e "\nInternet connection test failed (GitHub). Please check your connection and try again.\n"
  112. exit 1
  113. fi
  114. }
  115. ###########################################################
  116. if [[ ! -d /srv/{salt,pillar} ]]; then
  117. mkdir -p /srv/{pillar,salt/win/repo-ng/installers}
  118. fi
  119. cp -R srv_salt/* /srv/salt/
  120. cp -R srv_salt_winrepo/* /srv/salt/win/repo-ng/
  121. cp -R srv_pillar/* /srv/pillar
  122. ###########################################################
  123. if [[ ! -d compiled ]]; then
  124. mkdir compiled
  125. fi
  126. ###########################################################
  127. # Run scripts
  128. checkInternet
  129. if [[ $(systemctl is-active salt-master) != "active" ]]; then
  130. bash ./saltscripts/1-setup-salt-env.sh
  131. fi
  132. function minionAcceptPolicy() {
  133. echo -e "\nApplying minion ID key policy\n"
  134. if [[ $AUTOACCEPT_MINIONS == "true" ]]; then
  135. salt-key -y -A | sed '/^The key glob/d'
  136. else
  137. for accept_minion in $(salt-key | sed '/Unaccepted Keys/,/Rejected Keys/!d;//d'); do
  138. salt-key -a $accept_minion
  139. done
  140. fi
  141. echo -e "Current policy:\n$(salt-key)"
  142. }
  143. if [[ $SALT_LOCALONLY != "true" ]]; then
  144. minionAcceptPolicy
  145. else
  146. salt-key -y -D &> /dev/null
  147. systemctl restart salt-minion.service
  148. if [[ $? -eq 0 ]]; then
  149. sleep 5
  150. salt-key -y -a defaultMinion
  151. if [[ $(salt-key | sed '/Accepted Keys/,/Unaccepted Keys/!d;//d' | grep defaultMinion | wc -w) -eq 0 ]]; then
  152. exit 1
  153. fi
  154. fi
  155. fi
  156. function checkWinMinions() {
  157. local IFS=$'\n'
  158. WINMINIONS=""
  159. if [[ $SALT_LOCALONLY != "true" ]]; then
  160. echo -e "Checking for accepted Windows minions. Please wait.\n"
  161. for win_minion in $(salt-key | sed '/Accepted Keys/,/Unaccepted Keys/!d;//d'); do
  162. if [[ $(salt $win_minion grains.item os 2> /dev/null | sed '$!d; s/^\s*//; /^No minions/d') == "Windows" ]]; then
  163. WINMINIONS="true"
  164. fi
  165. done
  166. fi
  167. }
  168. echo -e "\nSalt master - version: $(salt-call grains.item saltversion | sed -n '$p' | sed -e 's/\s//g')"
  169. echo -e "Salt master - reported IP addresses: $(salt-call network.ip_addrs | sed '1d; s/^.*\-\s//' | sed ':a;N;$!ba;s/\n/, /g')\n"
  170. checkWinMinions
  171. bash ./saltscripts/2-get-programs-on-master.sh ${SALT_LOCALONLY} ${WINMINIONS}
  172. ###########################################################
  173. # Fix permissions
  174. for dir in /srv/salt /srv/pillar; do
  175. find $dir -type d -exec chmod u=rwx,go=rx {} +
  176. find $dir -type f -exec chmod u=rw,go=r {} +
  177. done
  178. ###########################################################
  179. # Test minion connectivity
  180. function testMinions() {
  181. local IFS=$'\n'
  182. MASTER_VERSION=$(dpkg -s salt-master | grep '^Version:' | awk '{print $2}')
  183. ACCEPTED_MINIONS=$(salt-key | sed '/Accepted Keys/,/Denied Keys/!d;//d')
  184. i=0
  185. m=1
  186. for check_minion in $ACCEPTED_MINIONS; do
  187. echo -e "Testing minion '$check_minion' ($m / $(echo $ACCEPTED_MINIONS | wc -w))"
  188. if [[ $(echo $(salt $check_minion test.ping) | awk '{print $NF}') != "True" ]]; then
  189. echo -e "Can't connect to Salt minion '\e[91m$check_minion\e[0m'. Make sure the minion computer is connected and\nSalt minion program version matches with the Salt master version ($MASTER_VERSION)\nIf they do match, delete old master public key from the minion computer, restart Salt minion service and try again."
  190. echo -e "\nThe master key is stored at the following locations:\n\n -Windows: C:\salt\\\\conf\pki\minion\minion_master.pub\n -Linux: /etc/salt/minion/minion_master.pub\n\nSystem administration rights are required to access it.\n"
  191. read -r -p "Continue? [y/N] " answer
  192. if [[ $(echo $answer | sed 's/ //g') =~ ^([nN][oO]|[nN])$ ]]; then
  193. echo -e "Aborting.\n"
  194. exit 1
  195. fi
  196. else
  197. echo -e "\tSalt minion '$check_minion' - version: $(salt $check_minion grains.item saltversion | sed -n '$p' | sed -e 's/\s//g')"
  198. OK_MINIONS[$i]=$check_minion
  199. let i++
  200. fi
  201. let m++
  202. done
  203. }
  204. if [[ $TEST_MINION_PING == "true" ]]; then
  205. echo -e "\nTesting connections to the accepted minion computers. This takes a while.\n"
  206. testMinions
  207. fi
  208. ###########################################################
  209. # Install & set up software
  210. TIMEOUT="600" # 10 minutes
  211. # Reason for increasing the timeout from 5 to 300:
  212. # Installing CloudCompare & QGIS programs take very long time
  213. # During the installation, Windows minions do not return anything
  214. # to the master computer, thus the master computer
  215. # thinks that the minions have timed out, although, in reality,
  216. # they are likely installing the applications
  217. #
  218. # Extra long timeout especially required by Windows QGIS installation!!
  219. #
  220. # Proper fix for this would be needed
  221. # but this behavior may be linked with options and output
  222. # by Windows NSIS (Nullsoft) installers
  223. # NOTE: Windows minions return "Failed" (retcode 2) status
  224. # for installed programs, although they return
  225. # alphabetical "install status: success" as well.
  226. # This is likely a retcode bug in Saltstack.
  227. # Salt is known to have these issues in the past.
  228. # Failed minions (failed)
  229. f=0
  230. # Succeeded minions (succeeded)
  231. s=0
  232. # All minions (total)
  233. t=0
  234. for minion in ${OK_MINIONS[*]}; do
  235. minion_ips=$(salt $minion network.ip_addrs | sed '1d; s/^.*\-\s//' | sed ':a;N;$!ba;s/\n/, /g')
  236. echo -e "\n(Minion $(( $t + 1 )) / ${#OK_MINIONS[@]}) Refreshing Salt grains & pillars for minion computer '\e[95m$minion\e[0m' (IPs: $minion_ips).\n"
  237. salt $minion saltutil.refresh_grains
  238. salt $minion saltutil.refresh_pillar
  239. # Update Salt package databases, especially for Windows minions
  240. echo -e "\n(Minion $(( $t + 1 )) / ${#OK_MINIONS[@]}) Updating Salt minion package databases for minion computer '\e[95m$minion\e[0m' (IPs: $minion_ips).\n"
  241. salt $minion pkg.refresh_db
  242. if [[ $SALT_DEBUG == "true" ]]; then
  243. saltdbg='-l debug'
  244. else
  245. saltdbg=''
  246. fi
  247. echo -e "\n(Minion $(( $t + 1 )) / ${#OK_MINIONS[@]}) Applying Salt state updates to the minion computer '\e[95m$minion\e[0m' (IPs: $minion_ips).\n"
  248. salt $saltdbg -t $TIMEOUT $minion state.highstate --state-output terse
  249. if [[ $? -ne 0 ]]; then
  250. let f++
  251. FAILED_MINIONS[$f]=$(echo "$minion (IPs: $minion_ips)")
  252. else
  253. let s++
  254. fi
  255. let t++
  256. done
  257. echo -e "\nSucceeded minions: $s of $t\nFailed minions: $f of $t\n"
  258. if [[ $f -ne 0 ]]; then
  259. echo -e "The following minions returned failed status:\n\n \e[91m$(echo ${FAILED_MINIONS[@]})\e[0m\n"
  260. fi
  261. unset OK_MINIONS