PageRenderTime 25ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/src/fireedge/src/server/routes/api/oneprovision/provision/helpers.js

https://github.com/OpenNebula/one
JavaScript | 295 lines | 208 code | 21 blank | 66 comment | 26 complexity | 66a2483a4c1d742491847c377e49508d MD5 | raw file
  1. /* ------------------------------------------------------------------------- *
  2. * Copyright 2002-2022, OpenNebula Project, OpenNebula Systems *
  3. * *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may *
  5. * not use this file except in compliance with the License. You may obtain *
  6. * a copy of the License at *
  7. * *
  8. * http://www.apache.org/licenses/LICENSE-2.0 *
  9. * *
  10. * Unless required by applicable law or agreed to in writing, software *
  11. * distributed under the License is distributed on an "AS IS" BASIS, *
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
  13. * See the License for the specific language governing permissions and *
  14. * limitations under the License. *
  15. * ------------------------------------------------------------------------- */
  16. const btoa = require('btoa')
  17. const { parse } = require('yaml')
  18. const { v4 } = require('uuid')
  19. const { DateTime } = require('luxon')
  20. const { publish } = require('server/utils/server')
  21. const {
  22. httpResponse,
  23. existsFile,
  24. rotateBySize,
  25. executeCommand,
  26. executeCommandAsync,
  27. } = require('server/utils/server')
  28. const { defaults, httpCodes } = require('server/utils/constants')
  29. const {
  30. findRecursiveFolder,
  31. getSpecificConfig,
  32. } = require('server/routes/api/oneprovision/utils')
  33. const {
  34. defaultCommandProvision,
  35. defaultEmptyFunction,
  36. defaultRegexpStartJSON,
  37. defaultRegexpEndJSON,
  38. defaultRegexpSplitLine,
  39. defaultSizeRotate,
  40. } = defaults
  41. const { ok, internalServerError } = httpCodes
  42. const relName = 'provision-mapping'
  43. const ext = 'yml'
  44. const logFile = {
  45. name: 'stdouterr',
  46. ext: 'log',
  47. }
  48. const appendError = '.ERROR'
  49. /**
  50. * Execute command Async and emit in WS.
  51. *
  52. * @param {string} command - command to execute
  53. * @param {object} actions - external functions when command emit in stderr, stdout and finalize
  54. * @param {Function} actions.err - emit when have stderr
  55. * @param {Function} actions.out - emit when have stdout
  56. * @param {Function} actions.close - emit when finalize
  57. * @param {object} dataForLog - data
  58. * @param {number} dataForLog.id - data id
  59. * @param {string} dataForLog.command - data command
  60. * @returns {boolean} check if emmit data
  61. */
  62. const executeWithEmit = (command = [], actions = {}, dataForLog = {}) => {
  63. if (
  64. !(
  65. command &&
  66. Array.isArray(command) &&
  67. command.length > 0 &&
  68. actions &&
  69. dataForLog
  70. )
  71. ) {
  72. return
  73. }
  74. const { err: externalErr, out: externalOut, close: externalClose } = actions
  75. const err =
  76. externalErr && typeof externalErr === 'function'
  77. ? externalErr
  78. : defaultEmptyFunction
  79. const out =
  80. externalOut && typeof externalOut === 'function'
  81. ? externalOut
  82. : defaultEmptyFunction
  83. const close =
  84. externalClose && typeof externalClose === 'function'
  85. ? actions.close
  86. : defaultEmptyFunction
  87. // data for log
  88. const id = (dataForLog && dataForLog.id) || ''
  89. const commandName = (dataForLog && dataForLog.command) || ''
  90. let lastLine = ''
  91. const uuid = v4()
  92. let pendingMessages = ''
  93. /**
  94. * Emit data of command.
  95. *
  96. * @param {string} message - line of command CLI
  97. * @param {Function} callback - function when recieve a information
  98. */
  99. const emit = (message, callback = defaultEmptyFunction) => {
  100. /**
  101. * Publisher data to WS.
  102. *
  103. * @param {string} line - command CLI line
  104. */
  105. const publisher = (line = '') => {
  106. const resposeData = callback(line, uuid) || {
  107. id,
  108. data: line,
  109. command: commandName,
  110. commandId: uuid,
  111. }
  112. publish(defaultCommandProvision, resposeData)
  113. }
  114. message
  115. .toString()
  116. .split(defaultRegexpSplitLine)
  117. .forEach((line) => {
  118. if (line) {
  119. if (
  120. (defaultRegexpStartJSON.test(line) &&
  121. defaultRegexpEndJSON.test(line)) ||
  122. (!defaultRegexpStartJSON.test(line) &&
  123. !defaultRegexpEndJSON.test(line) &&
  124. pendingMessages.length === 0)
  125. ) {
  126. lastLine = line
  127. publisher(lastLine)
  128. } else if (
  129. (defaultRegexpStartJSON.test(line) &&
  130. !defaultRegexpEndJSON.test(line)) ||
  131. (!defaultRegexpStartJSON.test(line) &&
  132. !defaultRegexpEndJSON.test(line) &&
  133. pendingMessages.length > 0)
  134. ) {
  135. pendingMessages += line
  136. } else {
  137. lastLine = pendingMessages + line
  138. publisher(lastLine)
  139. pendingMessages = ''
  140. }
  141. }
  142. })
  143. }
  144. executeCommandAsync(
  145. defaultCommandProvision,
  146. command,
  147. getSpecificConfig('oneprovision_prepend_command'),
  148. {
  149. err: (message) => {
  150. emit(message, err)
  151. },
  152. out: (message) => {
  153. emit(message, out)
  154. },
  155. close: (success) => {
  156. close(success, lastLine)
  157. },
  158. }
  159. )
  160. return true
  161. }
  162. /**
  163. * Find log data.
  164. *
  165. * @param {string} id - id of provision
  166. * @param {boolean} fullPath - if need return the path of log
  167. * @returns {object} data of log
  168. */
  169. const logData = (id, fullPath = false) => {
  170. let rtn = false
  171. if (!Number.isInteger(parseInt(id, 10))) {
  172. return rtn
  173. }
  174. const basePath = `${global.paths.CPI}/provision`
  175. const relFile = `${basePath}/${relName}`
  176. const relFileYML = `${relFile}.${ext}`
  177. const find = findRecursiveFolder(basePath, id)
  178. /**
  179. * Found log.
  180. *
  181. * @param {string} path - path of log
  182. * @param {string} uuid - uuid of log
  183. */
  184. const rtnFound = (path = '', uuid) => {
  185. if (!path) {
  186. return
  187. }
  188. const stringPath = `${path}/${logFile.name}.${logFile.ext}`
  189. existsFile(
  190. stringPath,
  191. (filedata) => {
  192. rotateBySize(stringPath, defaultSizeRotate)
  193. rtn = { uuid, log: filedata.split(defaultRegexpSplitLine) }
  194. if (fullPath) {
  195. rtn.fullPath = stringPath
  196. }
  197. },
  198. defaultEmptyFunction
  199. )
  200. }
  201. if (find) {
  202. rtnFound(find)
  203. } else {
  204. // Temporal provision
  205. existsFile(
  206. relFileYML,
  207. (filedata) => {
  208. const fileData = parse(filedata) || {}
  209. if (!fileData[id]) {
  210. return
  211. }
  212. const findPending = findRecursiveFolder(basePath, fileData[id])
  213. if (findPending) {
  214. rtnFound(findPending, fileData[id])
  215. } else {
  216. const findError = findRecursiveFolder(
  217. basePath,
  218. fileData[id] + appendError
  219. )
  220. findError && rtnFound(findError, fileData[id])
  221. }
  222. },
  223. defaultEmptyFunction
  224. )
  225. }
  226. return rtn
  227. }
  228. /**
  229. * Execute Command sync and return http response.
  230. *
  231. * @param {any[]} params - params for command.
  232. * @returns {object} httpResponse
  233. */
  234. const addResourceSync = (params) => {
  235. if (!(params && Array.isArray(params))) {
  236. return
  237. }
  238. const executedCommand = executeCommand(
  239. defaultCommandProvision,
  240. params,
  241. getSpecificConfig('oneprovision_prepend_command')
  242. )
  243. try {
  244. const response = executedCommand.success ? ok : internalServerError
  245. return httpResponse(
  246. response,
  247. executedCommand.data ? JSON.parse(executedCommand.data) : params.id
  248. )
  249. } catch (error) {
  250. return httpResponse(internalServerError, '', executedCommand.data)
  251. }
  252. }
  253. /**
  254. * Executing line for provision logs.
  255. *
  256. * @param {string} message - message to log
  257. * @returns {string} line message stringify
  258. */
  259. const executingMessage = (message = '') =>
  260. JSON.stringify({
  261. timestamp: DateTime.now().toFormat('yyyy-MM-dd HH:mm:ss ZZZ'),
  262. severity: 'DEBUG',
  263. message: btoa(message),
  264. })
  265. module.exports = {
  266. executeWithEmit,
  267. logData,
  268. addResourceSync,
  269. executingMessage,
  270. relName,
  271. ext,
  272. logFile,
  273. appendError,
  274. }