mirror of
https://github.com/OpenNebula/one.git
synced 2024-12-22 13:33:52 +03:00
parent
35232a764d
commit
940b38a903
@ -1,6 +1,6 @@
|
||||
name: 'aws-frankfurt'
|
||||
|
||||
description: 'Elastic cluster on AWS in Frakfurt'
|
||||
description: 'Elastic cluster on AWS in Frankfurt'
|
||||
provider: 'aws'
|
||||
|
||||
plain:
|
||||
|
@ -18,7 +18,7 @@
|
||||
inputs:
|
||||
- name: 'number_hosts'
|
||||
type: text
|
||||
description: 'Number of AWS instances to crearte'
|
||||
description: 'Number of AWS instances to create'
|
||||
default: '1'
|
||||
|
||||
- name: 'aws_ami_image'
|
||||
|
@ -20,9 +20,12 @@ export default {
|
||||
},
|
||||
error: {
|
||||
light: '#e57373',
|
||||
main: '#f44336',
|
||||
main: '#e04d40',
|
||||
dark: '#d32f2f',
|
||||
contrastText: '#ffffff'
|
||||
},
|
||||
debug: {
|
||||
main: '#7b7c7e'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,7 @@ const useStyles = makeStyles(theme => ({
|
||||
position: 'sticky',
|
||||
textAlign: 'center'
|
||||
},
|
||||
button: { padding: theme.spacing(0, 2) },
|
||||
svg: {
|
||||
color: '#fff !important',
|
||||
backgroundColor: 'transparent !important'
|
||||
}
|
||||
button: { padding: theme.spacing(0, 2) }
|
||||
}))
|
||||
|
||||
const baseClass = 'react-auto-scroll'
|
||||
@ -94,9 +90,8 @@ const AutoScrollBox = memo(({
|
||||
<Slide in={!autoScroll} direction="down" mountOnEnter unmountOnExit>
|
||||
<div className={classes.wrapperButton}>
|
||||
<Chip
|
||||
avatar={<ArrowBottomIcon />}
|
||||
color="primary"
|
||||
classes={{ avatarColorPrimary: classes.svg }}
|
||||
avatar={<ArrowBottomIcon style={{ backgroundColor: 'transparent' }} />}
|
||||
color='secondary'
|
||||
className={classes.button}
|
||||
label={autoButtonText}
|
||||
onClick={() => setAutoScroll(true)}
|
||||
|
172
src/fireedge/src/client/components/DebugLog/ansiHtml.js
Normal file
172
src/fireedge/src/client/components/DebugLog/ansiHtml.js
Normal file
@ -0,0 +1,172 @@
|
||||
// Reference to https://github.com/sindresorhus/ansi-regex
|
||||
var _regANSI = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/
|
||||
|
||||
var _defColors = {
|
||||
reset: ['fff', '000'], // [FOREGROUD_COLOR, BACKGROUND_COLOR]
|
||||
black: '000',
|
||||
red: 'ff0000',
|
||||
green: '209805',
|
||||
yellow: 'e8bf03',
|
||||
blue: '0000ff',
|
||||
magenta: 'ff00ff',
|
||||
cyan: '00ffee',
|
||||
lightgrey: 'f0f0f0',
|
||||
darkgrey: '888'
|
||||
}
|
||||
var _styles = {
|
||||
30: 'black',
|
||||
31: 'red',
|
||||
32: 'green',
|
||||
33: 'yellow',
|
||||
34: 'blue',
|
||||
35: 'magenta',
|
||||
36: 'cyan',
|
||||
37: 'lightgrey'
|
||||
}
|
||||
var _openTags = {
|
||||
1: 'font-weight:bold', // bold
|
||||
2: 'opacity:0.5', // dim
|
||||
3: '<i>', // italic
|
||||
4: '<u>', // underscore
|
||||
8: 'display:none', // hidden
|
||||
9: '<del>' // delete
|
||||
}
|
||||
var _closeTags = {
|
||||
23: '</i>', // reset italic
|
||||
24: '</u>', // reset underscore
|
||||
29: '</del>' // reset delete
|
||||
}
|
||||
|
||||
;[0, 21, 22, 27, 28, 39, 49].forEach(function (n) {
|
||||
_closeTags[n] = '</span>'
|
||||
})
|
||||
|
||||
/**
|
||||
* Converts text with ANSI color codes to HTML markup.
|
||||
* @param {String} text
|
||||
* @returns {*}
|
||||
*/
|
||||
export default function ansiHTML (text) {
|
||||
// Returns the text if the string has no ANSI escape code.
|
||||
if (!_regANSI.test(text)) {
|
||||
return text
|
||||
}
|
||||
|
||||
// Cache opened sequence.
|
||||
var ansiCodes = []
|
||||
// Replace with markup.
|
||||
var ret = text.replace(/\033\[(\d+)*m/g, function (match, seq) {
|
||||
var ot = _openTags[seq]
|
||||
if (ot) {
|
||||
// If current sequence has been opened, close it.
|
||||
if (!!~ansiCodes.indexOf(seq)) { // eslint-disable-line no-extra-boolean-cast
|
||||
ansiCodes.pop()
|
||||
return '</span>'
|
||||
}
|
||||
// Open tag.
|
||||
ansiCodes.push(seq)
|
||||
return ot[0] === '<' ? ot : '<span style="' + ot + ';">'
|
||||
}
|
||||
|
||||
var ct = _closeTags[seq]
|
||||
if (ct) {
|
||||
// Pop sequence
|
||||
ansiCodes.pop()
|
||||
return ct
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
// Make sure tags are closed.
|
||||
var l = ansiCodes.length
|
||||
;(l > 0) && (ret += Array(l + 1).join('</span>'))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize colors.
|
||||
* @param {Object} colors reference to _defColors
|
||||
*/
|
||||
ansiHTML.setColors = function (colors) {
|
||||
if (typeof colors !== 'object') {
|
||||
throw new Error('`colors` parameter must be an Object.')
|
||||
}
|
||||
|
||||
var _finalColors = {}
|
||||
for (var key in _defColors) {
|
||||
var hex = colors.hasOwnProperty(key) ? colors[key] : null
|
||||
if (!hex) {
|
||||
_finalColors[key] = _defColors[key]
|
||||
continue
|
||||
}
|
||||
if (key === 'reset') {
|
||||
if (typeof hex === 'string') {
|
||||
hex = [hex]
|
||||
}
|
||||
if (!Array.isArray(hex) || hex.length === 0 || hex.some(function (h) {
|
||||
return typeof h !== 'string'
|
||||
})) {
|
||||
throw new Error('The value of `' + key + '` property must be an Array and each item could only be a hex string, e.g.: FF0000')
|
||||
}
|
||||
var defHexColor = _defColors[key]
|
||||
if (!hex[0]) {
|
||||
hex[0] = defHexColor[0]
|
||||
}
|
||||
if (hex.length === 1 || !hex[1]) {
|
||||
hex = [hex[0]]
|
||||
hex.push(defHexColor[1])
|
||||
}
|
||||
|
||||
hex = hex.slice(0, 2)
|
||||
} else if (typeof hex !== 'string') {
|
||||
throw new Error('The value of `' + key + '` property must be a hex string, e.g.: FF0000')
|
||||
}
|
||||
_finalColors[key] = hex
|
||||
}
|
||||
_setTags(_finalColors)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset colors.
|
||||
*/
|
||||
ansiHTML.reset = function () {
|
||||
_setTags(_defColors)
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose tags, including open and close.
|
||||
* @type {Object}
|
||||
*/
|
||||
ansiHTML.tags = {}
|
||||
|
||||
if (Object.defineProperty) {
|
||||
Object.defineProperty(ansiHTML.tags, 'open', {
|
||||
get: function () { return _openTags }
|
||||
})
|
||||
Object.defineProperty(ansiHTML.tags, 'close', {
|
||||
get: function () { return _closeTags }
|
||||
})
|
||||
} else {
|
||||
ansiHTML.tags.open = _openTags
|
||||
ansiHTML.tags.close = _closeTags
|
||||
}
|
||||
|
||||
function _setTags (colors) {
|
||||
// reset all
|
||||
_openTags['0'] = 'font-weight:normal;opacity:1;color:#' + colors.reset[0] + ';background:#' + colors.reset[1]
|
||||
// inverse
|
||||
_openTags['7'] = 'color:#' + colors.reset[1] + ';background:#' + colors.reset[0]
|
||||
// dark grey
|
||||
_openTags['90'] = 'color:#' + colors.darkgrey
|
||||
|
||||
for (var code in _styles) {
|
||||
var color = _styles[code]
|
||||
var oriColor = colors[color] || '000'
|
||||
_openTags[code] = 'color:#' + oriColor
|
||||
code = parseInt(code)
|
||||
_openTags[(code + 10).toString()] = 'background:#' + oriColor
|
||||
}
|
||||
}
|
||||
|
||||
ansiHTML.reset()
|
@ -1,46 +1,56 @@
|
||||
import React, { useEffect, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { makeStyles, lighten, Box } from '@material-ui/core'
|
||||
import { makeStyles, Box } from '@material-ui/core'
|
||||
import AutoScrollBox from 'client/components/AutoScrollBox'
|
||||
import { DEBUG_LEVEL } from 'client/constants'
|
||||
|
||||
import AnsiHtml from 'client/components/DebugLog/ansiHtml'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
message: ({ color = '#fafafa' }) => ({
|
||||
color,
|
||||
root: {
|
||||
display: 'flex',
|
||||
marginBottom: '0.3em',
|
||||
padding: '0.5em 0',
|
||||
cursor: 'default',
|
||||
fontFamily: 'monospace',
|
||||
padding: theme.spacing(0.5, 1),
|
||||
'&:hover': { backgroundColor: lighten('#1d1f21', 0.02) }
|
||||
})
|
||||
'&:hover': {
|
||||
background: '#333537'
|
||||
}
|
||||
},
|
||||
time: {
|
||||
paddingLeft: '0.5em',
|
||||
minWidth: '220px'
|
||||
},
|
||||
message: {
|
||||
color: '#fafafa'
|
||||
},
|
||||
[DEBUG_LEVEL.ERROR]: { borderLeft: `0.3em solid ${theme.palette.error.light}` },
|
||||
[DEBUG_LEVEL.WARN]: { borderLeft: `0.3em solid ${theme.palette.warning.main}` },
|
||||
[DEBUG_LEVEL.INFO]: { borderLeft: `0.3em solid ${theme.palette.info.main}` },
|
||||
[DEBUG_LEVEL.DEBUG]: { borderLeft: `0.3em solid ${theme.palette.debug.main}` }
|
||||
}))
|
||||
|
||||
// --------------------------------------------
|
||||
// MESSAGE COMPONENT
|
||||
// --------------------------------------------
|
||||
|
||||
const Message = memo(({ log, message, index }) => {
|
||||
const color = () => {
|
||||
const lastMessage = log[index - 1]
|
||||
|
||||
if (message.includes('WARNING')) return 'yellow'
|
||||
else if (message.includes('WARNING')) return 'yellow'
|
||||
else if (
|
||||
message.includes('ERROR') || lastMessage?.includes('ERROR')
|
||||
) return 'red'
|
||||
else return '#fafafa'
|
||||
}
|
||||
|
||||
const classes = useStyles({ color: color() })
|
||||
const Message = memo(({ timestamp = '', severity = DEBUG_LEVEL.DEBUG, message }) => {
|
||||
const classes = useStyles()
|
||||
const sanitize = AnsiHtml(message)
|
||||
|
||||
return (
|
||||
<div key={index} className={classes.message}>
|
||||
{message}
|
||||
<div className={clsx([classes.root, classes[severity]])}>
|
||||
<div className={classes.time}>{timestamp}</div>
|
||||
<div className={classes.message}>{sanitize}</div>
|
||||
</div>
|
||||
)
|
||||
}, (prev, next) => prev.index === next.index)
|
||||
})
|
||||
|
||||
Message.propTypes = {
|
||||
log: PropTypes.array,
|
||||
timestamp: PropTypes.string,
|
||||
severity: PropTypes.string,
|
||||
message: PropTypes.string,
|
||||
index: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
@ -59,23 +69,55 @@ const DebugLog = memo(({ uuid, socket, logDefault }) => {
|
||||
const [log, setLog] = useState(logDefault)
|
||||
|
||||
useEffect(() => {
|
||||
uuid && socket.on(socketData =>
|
||||
socketData?.id === uuid && setLog(prev => [...prev, socketData?.data])
|
||||
)
|
||||
uuid && socket.on((socketData = {}) => {
|
||||
const { id, data, command, commandId } = socketData
|
||||
|
||||
id === uuid && setLog(prev => ({
|
||||
...prev,
|
||||
[command]: {
|
||||
[commandId]: [...(prev?.[command]?.[commandId] ?? []), data]
|
||||
}
|
||||
}))
|
||||
})
|
||||
return () => uuid && socket.off()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box borderRadius={5} bgcolor={'#1d1f21'} width={1} height={1}>
|
||||
<Box borderRadius={5} bgcolor={'#1d1f21'} width={1} height={1} style={{ fontSize: '1.1em', wordBreak: 'break-word' }}>
|
||||
<AutoScrollBox scrollBehavior="auto">
|
||||
{log?.map((message, index) => {
|
||||
const isString = typeof message === 'string'
|
||||
const stringMessage = isString ? message : JSON.stringify(message)
|
||||
{Object.entries(log)?.map(([command, entries]) =>
|
||||
Object.entries(entries)?.map(([commandId, messages]) =>
|
||||
messages?.map((data, index) => {
|
||||
const key = `${index}-${command}-${commandId}`
|
||||
|
||||
return (
|
||||
<Message key={index} log={log} message={stringMessage} index={index} />
|
||||
try {
|
||||
const { timestamp, severity, message } = JSON.parse(data)
|
||||
const decryptMessage = atob(message)
|
||||
|
||||
return (
|
||||
<Message
|
||||
key={key}
|
||||
timestamp={timestamp}
|
||||
severity={severity}
|
||||
message={decryptMessage}
|
||||
/>
|
||||
)
|
||||
} catch {
|
||||
const severity = data.includes(DEBUG_LEVEL.ERROR)
|
||||
? DEBUG_LEVEL.ERROR
|
||||
: data.includes(DEBUG_LEVEL.INFO)
|
||||
? DEBUG_LEVEL.INFO
|
||||
: data.includes(DEBUG_LEVEL.WARN)
|
||||
? DEBUG_LEVEL.WARN
|
||||
: DEBUG_LEVEL.DEBUG
|
||||
|
||||
return (
|
||||
<Message key={key} severity={severity} message={data} />
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
})}
|
||||
)}
|
||||
</AutoScrollBox>
|
||||
</Box>
|
||||
)
|
||||
@ -87,7 +129,7 @@ DebugLog.propTypes = {
|
||||
on: PropTypes.func.isRequired,
|
||||
off: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
logDefault: PropTypes.array
|
||||
logDefault: PropTypes.object
|
||||
}
|
||||
|
||||
DebugLog.defaultProps = {
|
||||
@ -96,7 +138,7 @@ DebugLog.defaultProps = {
|
||||
on: () => undefined,
|
||||
off: () => undefined
|
||||
},
|
||||
logDefault: []
|
||||
logDefault: {}
|
||||
}
|
||||
|
||||
DebugLog.displayName = 'DebugLog'
|
||||
|
@ -60,6 +60,13 @@ export const INPUT_TYPES = {
|
||||
AUTOCOMPLETE: 'autocomplete'
|
||||
}
|
||||
|
||||
export const DEBUG_LEVEL = {
|
||||
ERROR: 'ERROR',
|
||||
WARN: 'WARN',
|
||||
INFO: 'INFO',
|
||||
DEBUG: 'DEBUG'
|
||||
}
|
||||
|
||||
export * as T from 'client/constants/translates'
|
||||
export * from 'client/constants/flow'
|
||||
export * from 'client/constants/states'
|
||||
|
@ -9,23 +9,37 @@ import DebugLog from 'client/components/DebugLog'
|
||||
const Log = React.memo(({ hidden, data: { ID } }) => {
|
||||
const { getProvision } = useSocket()
|
||||
const { getProvisionLog } = useProvision()
|
||||
const { data: provision, fetchRequest, loading } = useFetch(getProvisionLog)
|
||||
const { data: provisionLog, fetchRequest, loading } = useFetch(getProvisionLog)
|
||||
|
||||
React.useEffect(() => {
|
||||
!hidden && fetchRequest({ id: ID })
|
||||
}, [ID])
|
||||
|
||||
React.useEffect(() => {
|
||||
(!provision && !hidden) && fetchRequest({ id: ID })
|
||||
(!provisionLog && !hidden) && fetchRequest({ id: ID })
|
||||
}, [hidden])
|
||||
|
||||
const log = provisionLog?.log?.reduce((res, dataLog) => {
|
||||
try {
|
||||
const json = JSON.parse(dataLog)
|
||||
const { data, command, commandId } = json
|
||||
|
||||
return {
|
||||
...res,
|
||||
[command]: {
|
||||
[commandId]: [...(res?.[command]?.[commandId] ?? []), data]
|
||||
}
|
||||
}
|
||||
} catch { return res }
|
||||
}, {})
|
||||
|
||||
return loading ? (
|
||||
<LinearProgress color='secondary' style={{ width: '100%' }} />
|
||||
) : (
|
||||
<DebugLog
|
||||
uuid={provision?.uuid ?? ID}
|
||||
uuid={provisionLog?.uuid ?? ID}
|
||||
socket={getProvision}
|
||||
logDefault={provision?.log}
|
||||
logDefault={log}
|
||||
/>
|
||||
)
|
||||
}, (prev, next) =>
|
||||
|
@ -240,14 +240,7 @@ const addPrependCommand = (command = '', resource = '') => {
|
||||
}
|
||||
}
|
||||
|
||||
const addOptionalCreateCommand = () =>{
|
||||
let rtn = []
|
||||
if(optionalCreateCommand){
|
||||
rtn.push(optionalCreateCommand)
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
|
||||
const addOptionalCreateCommand = () => [optionalCreateCommand].filter(Boolean)
|
||||
|
||||
const executeCommandAsync = (
|
||||
command = '',
|
||||
|
Loading…
Reference in New Issue
Block a user