1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-16 22:50:10 +03:00

F OpenNebula/one#5422: Add sched actions tab (#1379)

This commit is contained in:
Sergio Betanzos 2021-07-26 19:03:12 +02:00 committed by GitHub
parent 54e1c43a1b
commit 494aa50781
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 311 additions and 19 deletions

View File

@ -151,6 +151,7 @@ info-tabs:
enabled: true
actions:
sched_action_create: true
sched_action_update: true
sched_action_delete: true
charter_create: true

View File

@ -151,6 +151,7 @@ info-tabs:
enabled: true
actions:
sched_action_create: true
sched_action_update: true
sched_action_delete: true
charter_create: true

View File

@ -45,7 +45,7 @@ const VmNetworkTab = ({ tabProps = {} }) => {
<>
{actionsAvailable?.includes?.(VM_ACTIONS.ATTACH_NIC) && (
<Button
data-cy='resize'
data-cy='attach-nic'
size='small'
color='secondary'
onClick={show}

View File

@ -0,0 +1,118 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import * as React from 'react'
import PropTypes from 'prop-types'
import { Edit, Trash, WarningTriangleOutline } from 'iconoir-react'
import { useTheme, Typography, Paper } from '@material-ui/core'
// import { useVmApi } from 'client/features/One'
import { Action } from 'client/components/Cards/SelectCard'
import { StatusChip } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import * as VirtualMachine from 'client/models/VirtualMachine'
import * as Helper from 'client/models/Helper'
import { VM_ACTIONS } from 'client/constants'
const SchedulingItem = ({ vmStartTime, schedule, actions = [] }) => {
const classes = rowStyles()
const { palette } = useTheme()
const { ID, ACTION, TIME, MESSAGE, DONE, WARNING } = schedule
const isRelative = String(TIME).includes('+')
const time = Helper.timeFromMilliseconds(
isRelative ? (+vmStartTime + +TIME) : +TIME
)
const doneTime = Helper.timeFromMilliseconds(+DONE)
const now = Math.round(Date.now() / 1000)
const isWarning = WARNING && (now - +vmStartTime) > +WARNING
const labels = [...new Set([
Helper.stringToBoolean(MESSAGE)
])].filter(Boolean)
const { repeat, end } = VirtualMachine.periodicityToString(schedule)
return (
<Paper variant='outlined' className={classes.root}>
<div className={classes.main}>
<div className={classes.title}>
<Typography component='span'>
{`#${ID} ${ACTION}`}
</Typography>
{!!labels.length && (
<span className={classes.labels}>
{labels.map(label => (
<StatusChip key={label} text={label} />
))}
</span>
)}
</div>
<div className={classes.caption}>
{repeat && <span>{repeat}</span>}
{end && <span>{`| ${end}`}</span>}
{DONE && (
<span title={doneTime.toFormat('ff')}>
{`| done ${doneTime.toRelative()}`}
</span>
)}
<span style={{ display: 'flex', gap: '0.5em' }}>
<span title={time.toFormat('ff')}>
{`| ${time.toRelative()}`}
</span>
{isWarning && (
<WarningTriangleOutline size={18} color={palette.warning.main} />
)}
</span>
</div>
</div>
{!!actions.length && (
<div className={classes.actions}>
{actions?.includes?.(VM_ACTIONS.SCHED_ACTION_UPDATE) && (
<Action
cy={`${VM_ACTIONS.SCHED_ACTION_UPDATE}-${ID}`}
icon={<Edit size={18} />}
handleClick={() => undefined}
/>
)}
{actions?.includes?.(VM_ACTIONS.SCHED_ACTION_DELETE) && (
<Action
cy={`${VM_ACTIONS.SCHED_ACTION_DELETE}-${ID}`}
icon={<Trash size={18} />}
handleClick={() => undefined}
/>
)}
</div>
)}
</Paper>
)
}
SchedulingItem.propTypes = {
vmStartTime: PropTypes.string.isRequired,
schedule: PropTypes.object.isRequired,
actions: PropTypes.arrayOf(PropTypes.string)
}
SchedulingItem.displayName = 'SchedulingItem'
export default SchedulingItem

View File

@ -15,15 +15,29 @@
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import * as React from 'react'
import PropTypes from 'prop-types'
const VmSchedulingTab = data => {
return (
<div>
<p>WIP - SCHED ACTIONS</p>
</div>
)
import SchedulingItem from 'client/components/Tabs/Vm/SchedActions/Item'
const SchedulingList = ({ vmStartTime, scheduling, actions }) => (
<div style={{ display: 'grid', gap: '1em', paddingBlock: '0.8em' }}>
{scheduling.map((schedule, idx) => (
<SchedulingItem
key={idx}
vmStartTime={vmStartTime}
schedule={schedule}
actions={actions}
/>
))}
</div>
)
SchedulingList.propTypes = {
vmStartTime: PropTypes.string,
scheduling: PropTypes.array,
actions: PropTypes.arrayOf(PropTypes.string)
}
VmSchedulingTab.displayName = 'VmSchedulingTab'
SchedulingList.displayName = 'SchedulingList'
export default VmSchedulingTab
export default SchedulingList

View File

@ -0,0 +1,92 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import * as React from 'react'
import PropTypes from 'prop-types'
import { ClockOutline } from 'iconoir-react'
import { Button } from '@material-ui/core'
import { useDialog } from 'client/hooks'
import { TabContext } from 'client/components/Tabs/TabProvider'
import { DialogConfirmation } from 'client/components/Dialogs'
import SchedulingList from 'client/components/Tabs/Vm/SchedActions/List'
import { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import * as VirtualMachine from 'client/models/VirtualMachine'
import * as Helper from 'client/models/Helper'
import { T, VM_ACTIONS } from 'client/constants'
const VmSchedulingTab = ({ tabProps = {} }) => {
const { display, show, hide } = useDialog()
const { data: vm } = React.useContext(TabContext)
const { actions = [] } = tabProps
const scheduling = VirtualMachine.getScheduleActions(vm)
const hypervisor = VirtualMachine.getHypervisor(vm)
const actionsAvailable = Helper.getActionsAvailable(actions, hypervisor)
return (
<>
{actionsAvailable?.includes?.(VM_ACTIONS.SCHED_ACTION_CREATE) && (
<Button
data-cy='sched-action-create'
size='small'
color='secondary'
onClick={show}
variant='contained'
>
{Tr(T.AddAction)}
</Button>
)}
{actionsAvailable?.includes?.(VM_ACTIONS.CHARTER_CREATE) && (
<SubmitButton
data-cy='charter-create'
color='secondary'
icon
label={<ClockOutline />}
onClick={show}
/>
)}
<SchedulingList
actions={actionsAvailable}
scheduling={scheduling}
vmStartTime={vm?.STIME}
/>
{display && (
<DialogConfirmation
title={T.AddAction}
handleAccept={hide}
handleCancel={hide}
>
<p>TODO: should define in view yaml ??</p>
</DialogConfirmation>
)}
</>
)
}
VmSchedulingTab.propTypes = {
tabProps: PropTypes.object
}
VmSchedulingTab.displayName = 'VmSchedulingTab'
export default VmSchedulingTab

View File

@ -42,7 +42,7 @@ const VmStorageTab = ({ tabProps = {} }) => {
<>
{actionsAvailable?.includes?.(VM_ACTIONS.ATTACH_DISK) && (
<Button
data-cy='resize'
data-cy='attach-disk'
size='small'
color='secondary'
onClick={show}

View File

@ -21,6 +21,7 @@ module.exports = {
SortBy: 'Sort by',
Filter: 'Filter',
All: 'All',
On: 'On',
/* actions */
Accept: 'Accept',
@ -50,10 +51,32 @@ module.exports = {
AttachNic: 'Attach nic',
Detach: 'Detach',
TakeSnapshot: 'Take snapshot',
AddAction: 'Add action',
/* questions */
DoYouWantProceed: 'Do you want proceed?',
/* Scheduling */
Mon: 'Mon',
Monday: 'Monday',
Tue: 'Tue',
Tuesday: 'Tuesday',
Wed: 'Wed',
Wednesday: 'Wednesday',
Thu: 'Thu',
Thursday: 'Thursday',
Fri: 'Fri',
Friday: 'Friday',
Sat: 'Sat',
Saturday: 'Saturday',
Sun: 'Sun',
Sunday: 'Sunday',
Weekly: 'Weekly',
Monthly: 'Monthly',
Yearly: 'Yearly',
EachHours: 'Each %s hours',
AfterTimes: 'After %s times',
/* dashboard */
InTotal: 'In Total',
Used: 'Used',

View File

@ -494,6 +494,7 @@ export const VM_ACTIONS = {
// SCHEDULING ACTION
SCHED_ACTION_CREATE: 'sched_action_create',
SCHED_ACTION_UPDATE: 'sched_action_update',
SCHED_ACTION_DELETE: 'sched_action_delete',
CHARTER_CREATE: 'charter_create',

View File

@ -46,13 +46,14 @@ export const vmService = ({
* @param {string} data.filter - Filter flag
* @param {number} data.start - Range start ID
* @param {number} data.end - Range end ID
* @param {string|number} data.state - Filter state
* @returns {Array} List of VMs
* @throws Fails when response isn't code 200
*/
getVms: async ({ filter, start, end }) => {
getVms: async ({ filter, start, end, state }) => {
const name = Actions.VM_POOL_INFO
const command = { name, ...Commands[name] }
const config = requestConfig({ filter, start, end }, command)
const config = requestConfig({ filter, start, end, state }, command)
const res = await RestClient.request(config)

View File

@ -14,6 +14,8 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { getSecurityGroupsFromResource, prettySecurityGroup } from 'client/models/SecurityGroup'
import { timeToString } from 'client/models/Helper'
import { Tr } from 'client/components/HOC'
import {
STATES,
@ -21,15 +23,10 @@ import {
VM_LCM_STATES,
NIC_ALIAS_IP_ATTRS,
HISTORY_ACTIONS,
T,
StateInfo
} from 'client/constants'
/**
* @param {string|number} action - Action code
* @returns {HISTORY_ACTIONS} History action name
*/
export const getHistoryAction = action => HISTORY_ACTIONS[+action]
/**
* This function removes, from the given list,
* the Virtual machines in state DONE.
@ -40,6 +37,12 @@ export const getHistoryAction = action => HISTORY_ACTIONS[+action]
export const filterDoneVms = (vms = []) =>
vms.filter(({ STATE }) => VM_STATES[STATE]?.name !== STATES.DONE)
/**
* @param {string|number} action - Action code
* @returns {HISTORY_ACTIONS} History action name
*/
export const getHistoryAction = action => HISTORY_ACTIONS[+action]
/**
* @param {object} vm - Virtual machine
* @returns {object} History records from resource
@ -195,5 +198,43 @@ export const splitNicAlias = vm =>
export const getSnapshotList = vm => {
const { TEMPLATE = {} } = vm ?? {}
return [TEMPLATE.SNAPSHOT].flat()
return [TEMPLATE.SNAPSHOT].filter(Boolean).flat()
}
/**
* @param {object} vm - Virtual machine
* @returns {Array} List of schedule actions from resource
*/
export const getScheduleActions = vm => {
const { TEMPLATE = {} } = vm ?? {}
return [TEMPLATE.SCHED_ACTION].filter(Boolean).flat()
}
/**
* Converts the periodicity of the action to string value.
*
* @param {object} scheduleAction - Schedule action
* @returns {{repeat: string|string[], end: string}} - Periodicity of the action.
*/
export const periodicityToString = scheduleAction => {
const { REPEAT, DAYS = '', END_TYPE, END_VALUE = '' } = scheduleAction ?? {}
const daysOfWeek = [T.Sun, T.Mon, T.Tue, T.Wed, T.Thu, T.Fri, T.Sat]
const days = DAYS?.split(',')?.map(day => Tr(daysOfWeek[day])) ?? []
const repeat = {
0: `${Tr(T.Weekly)} ${days.join(',')}`,
1: `${Tr(T.Monthly)} ${DAYS}`,
2: `${Tr(T.Yearly)} ${DAYS}`,
3: Tr([T.EachHours, DAYS])
}[+REPEAT]
const end = {
0: Tr(T.None),
1: Tr([T.AfterTimes, END_VALUE]),
2: `${Tr(T.On)} ${timeToString(END_VALUE)}`
}[+END_TYPE]
return { repeat, end }
}