mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-08 21:17:43 +03:00
parent
feb4a86066
commit
735a8d4feb
@ -24,6 +24,8 @@ resource_name: "VM"
|
||||
# Actions - Which buttons are visible to operate over the resources
|
||||
|
||||
actions:
|
||||
refresh: true
|
||||
create_dialog: true
|
||||
deploy: true
|
||||
migrate: true
|
||||
migrate_live: true
|
||||
@ -46,11 +48,12 @@ actions:
|
||||
resched: true
|
||||
unresched: true
|
||||
save_as_template: true
|
||||
lockU: true
|
||||
chown: true
|
||||
chgrp: true
|
||||
lock: true
|
||||
unlock: true
|
||||
startvnc: true
|
||||
startvmrc: true
|
||||
startspice: true
|
||||
vmrc: true
|
||||
spice: true
|
||||
vnc: true
|
||||
ssh: true
|
||||
rdp: true
|
||||
|
@ -24,6 +24,7 @@ resource_name: "VM"
|
||||
# Actions - Which buttons are visible to operate over the resources
|
||||
|
||||
actions:
|
||||
refresh: true
|
||||
create_dialog: true
|
||||
deploy: true
|
||||
migrate: true
|
||||
@ -46,12 +47,13 @@ actions:
|
||||
terminate_hard: true
|
||||
resched: true
|
||||
unresched: true
|
||||
save_as_template: true
|
||||
lockU: true
|
||||
save_as_template: false
|
||||
chown: false
|
||||
chgrp: false
|
||||
lock: true
|
||||
unlock: true
|
||||
startvnc: true
|
||||
startvmrc: true
|
||||
startspice: true
|
||||
vmrc: true
|
||||
spice: true
|
||||
vnc: true
|
||||
ssh: true
|
||||
rdp: true
|
||||
@ -82,8 +84,8 @@ info-tabs:
|
||||
ownership_panel:
|
||||
enabled: true
|
||||
actions:
|
||||
chown: true
|
||||
chgrp: true
|
||||
chown: false
|
||||
chgrp: false
|
||||
vcenter_panel:
|
||||
enabled: true
|
||||
actions:
|
||||
|
@ -91,35 +91,44 @@ export const PATH = {
|
||||
TEMPLATE: {
|
||||
VMS: {
|
||||
LIST: '/vm-template',
|
||||
DETAIL: '/vm-template/:id',
|
||||
INSTANTIATE: '/vm-template/instantiate'
|
||||
}
|
||||
},
|
||||
STORAGE: {
|
||||
DATASTORES: {
|
||||
LIST: '/datastore'
|
||||
LIST: '/datastore',
|
||||
DETAIL: '/datastore/:id'
|
||||
},
|
||||
IMAGES: {
|
||||
LIST: '/image'
|
||||
LIST: '/image',
|
||||
DETAIL: '/image/:id'
|
||||
},
|
||||
FILES: {
|
||||
LIST: '/file'
|
||||
LIST: '/file',
|
||||
DETAIL: '/file/:id'
|
||||
},
|
||||
MARKETPLACES: {
|
||||
LIST: '/marketplace'
|
||||
LIST: '/marketplace',
|
||||
DETAIL: '/marketplace/:id'
|
||||
},
|
||||
MARKETPLACE_APPS: {
|
||||
LIST: '/marketplaces-app'
|
||||
LIST: '/marketplaces-app',
|
||||
DETAIL: '/marketplaces-app/:id'
|
||||
}
|
||||
},
|
||||
NETWORK: {
|
||||
VNETS: {
|
||||
LIST: '/virtual-network'
|
||||
LIST: '/virtual-network',
|
||||
DETAIL: '/virtual-network/:id'
|
||||
},
|
||||
VN_TEMPLATES: {
|
||||
LIST: '/network-template'
|
||||
LIST: '/network-template',
|
||||
DETAIL: '/network-template/:id'
|
||||
},
|
||||
SEC_GROUPS: {
|
||||
LIST: '/security-group'
|
||||
LIST: '/security-group',
|
||||
DETAIL: '/security-group/:id'
|
||||
}
|
||||
},
|
||||
INFRASTRUCTURE: {
|
||||
@ -132,7 +141,8 @@ export const PATH = {
|
||||
DETAIL: '/host/:id'
|
||||
},
|
||||
ZONES: {
|
||||
LIST: '/zone'
|
||||
LIST: '/zone',
|
||||
DETAIL: '/zone/:id'
|
||||
}
|
||||
},
|
||||
SYSTEM: {
|
||||
|
@ -43,7 +43,7 @@ const useStyles = makeStyles(theme => ({
|
||||
|
||||
const ButtonComponent = forwardRef(
|
||||
({ icon, endicon, children, size = 'small', ...props }, ref) =>
|
||||
icon ? (
|
||||
icon && !endicon ? (
|
||||
<IconButton ref={ref} {...props}>{children}</IconButton>
|
||||
) : (
|
||||
<Button ref={ref}
|
||||
|
@ -37,7 +37,6 @@ import { Translate } from 'client/components/HOC'
|
||||
|
||||
const ButtonToTriggerForm = ({
|
||||
buttonProps = {},
|
||||
dialogProps = {},
|
||||
options = []
|
||||
}) => {
|
||||
const buttonId = buttonProps['data-cy'] ?? 'main-button-form'
|
||||
@ -47,7 +46,7 @@ const ButtonToTriggerForm = ({
|
||||
const open = Boolean(anchorEl)
|
||||
|
||||
const { display, show, hide, values: Form } = useDialog()
|
||||
const { onSubmit: handleSubmit, form, isConfirmDialog = false } = Form ?? {}
|
||||
const { onSubmit: handleSubmit, form, isConfirmDialog = false, dialogProps = {} } = Form ?? {}
|
||||
|
||||
const formConfig = useMemo(() => form?.() ?? {}, [form])
|
||||
const { steps, defaultValues, resolver, fields, transformBeforeSubmit } = formConfig
|
||||
@ -96,9 +95,10 @@ const ButtonToTriggerForm = ({
|
||||
<Paper variant='outlined'>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList variant='menu' disablePadding dense>
|
||||
{options.map(({ cy, icon: Icon, name, ...option }) => (
|
||||
{options.map(({ cy, disabled, icon: Icon, name, ...option }) => (
|
||||
<MenuItem
|
||||
key={name}
|
||||
disabled={disabled}
|
||||
data-cy={cy}
|
||||
onClick={() => openDialogForm(option)}
|
||||
>
|
||||
@ -150,11 +150,11 @@ const ButtonToTriggerForm = ({
|
||||
|
||||
export const ButtonToTriggerFormPropTypes = {
|
||||
buttonProps: PropTypes.shape(SubmitButtonPropTypes),
|
||||
dialogProps: PropTypes.shape(DialogPropTypes),
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
cy: PropTypes.string,
|
||||
isConfirmDialog: PropTypes.bool,
|
||||
dialogProps: PropTypes.shape(DialogPropTypes),
|
||||
name: PropTypes.string,
|
||||
icon: PropTypes.any,
|
||||
form: PropTypes.func,
|
||||
|
@ -67,12 +67,10 @@ const Networking = ({ data, setFormData }) => {
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': 'add-nic',
|
||||
label: 'Add nic'
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `Add new: ${Tr(T.NIC)}`
|
||||
label: Tr(T.AttachNic)
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: { title: T.AttachNic },
|
||||
form: () => AttachNicForm({ nics }),
|
||||
onSubmit: handleSave
|
||||
}]}
|
||||
@ -118,10 +116,10 @@ const Networking = ({ data, setFormData }) => {
|
||||
icon: <Edit size={18} />,
|
||||
tooltip: <Translate word={T.Edit} />
|
||||
}}
|
||||
dialogProps={{
|
||||
title: <><Translate word={T.Edit} />{`: ${NAME} - ${NETWORK}`}</>
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: {
|
||||
title: <Translate word={T.EditSomething} values={[`${NAME} - ${NETWORK}`]} />
|
||||
},
|
||||
form: () => AttachNicForm({ nics }, item),
|
||||
onSubmit: newValues => handleSave(newValues, NAME)
|
||||
}]}
|
||||
|
@ -60,22 +60,19 @@ const ScheduleAction = ({ data, setFormData }) => {
|
||||
'data-cy': 'add-sched-action',
|
||||
label: Tr(T.AddAction)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: Tr(T.ScheduledAction)
|
||||
}}
|
||||
options={[{
|
||||
cy: 'add-sched-action-punctual',
|
||||
name: 'Punctual action',
|
||||
dialogProps: { title: T.ScheduledAction },
|
||||
form: () => PunctualForm(),
|
||||
onSubmit: formData =>
|
||||
handleSave(SCHED_ACTION_SCHEMA.cast(formData))
|
||||
onSubmit: formData => handleSave(SCHED_ACTION_SCHEMA.cast(formData))
|
||||
},
|
||||
{
|
||||
cy: 'add-sched-action-relative',
|
||||
name: 'Relative action',
|
||||
dialogProps: { title: T.ScheduledAction },
|
||||
form: () => RelativeForm(),
|
||||
onSubmit: formData =>
|
||||
handleSave(SCHED_ACTION_SCHEMA.cast(formData))
|
||||
onSubmit: formData => handleSave(SCHED_ACTION_SCHEMA.cast(formData))
|
||||
}]}
|
||||
/>
|
||||
<div className={classes.root}>
|
||||
@ -100,10 +97,10 @@ const ScheduleAction = ({ data, setFormData }) => {
|
||||
icon: <Edit size={18} />,
|
||||
tooltip: <Translate word={T.Edit} />
|
||||
}}
|
||||
dialogProps={{
|
||||
title: <><Translate word={T.Edit} />{`: ${NAME}`}</>
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: {
|
||||
title: <><Translate word={T.Edit} />{`: ${NAME}`}</>
|
||||
},
|
||||
form: () => isRelative
|
||||
? RelativeForm(undefined, item)
|
||||
: PunctualForm(undefined, item),
|
||||
|
@ -75,21 +75,20 @@ const Storage = ({ data, setFormData }) => {
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': 'add-disk',
|
||||
label: 'Add disk'
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `Add new: ${Tr(T.Disk)}`
|
||||
label: Tr(T.AttachDisk)
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
cy: 'attach-image-disk',
|
||||
name: T.Image,
|
||||
dialogProps: { title: T.AttachImage },
|
||||
form: () => ImageSteps({ hypervisor: HYPERVISOR }),
|
||||
onSubmit: handleSave
|
||||
},
|
||||
{
|
||||
cy: 'attach-volatile-disk',
|
||||
name: T.Volatile,
|
||||
dialogProps: { title: T.AttachVolatile },
|
||||
form: () => VolatileSteps({ hypervisor: HYPERVISOR }),
|
||||
onSubmit: handleSave
|
||||
}
|
||||
@ -164,10 +163,10 @@ const Storage = ({ data, setFormData }) => {
|
||||
icon: <Edit size={18} />,
|
||||
tooltip: <Translate word={T.Edit} />
|
||||
}}
|
||||
dialogProps={{
|
||||
title: <><Translate word={T.Edit} />{`: ${NAME}`}</>
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: {
|
||||
title: <Translate word={T.EditSomething} values={[NAME]} />
|
||||
},
|
||||
form: () => isVolatile
|
||||
? VolatileSteps({ hypervisor: HYPERVISOR }, item)
|
||||
: ImageSteps({ hypervisor: HYPERVISOR }, item),
|
||||
|
@ -21,6 +21,7 @@ import { sprintf } from 'sprintf-js'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { DEFAULT_LANGUAGE, LANGUAGES_URL } from 'client/constants'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
|
||||
const TranslateContext = createContext()
|
||||
let languageScript = root.document?.createElement('script')
|
||||
@ -39,7 +40,7 @@ const GenerateScript = (
|
||||
root.document.body.appendChild(script)
|
||||
languageScript = script
|
||||
} catch (error) {
|
||||
console.warn('Error while generating script language')
|
||||
isDevelopment() && console.error('Error while generating script language', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,12 +85,12 @@ const ActionItem = memo(({ item, selectedRows }) => {
|
||||
) : (
|
||||
<ButtonToTriggerForm
|
||||
buttonProps={buttonProps}
|
||||
dialogProps={{
|
||||
...dialogProps,
|
||||
title: typeof title === 'function' ? title(selectedRows) : title,
|
||||
children: typeof children === 'function' ? children(selectedRows) : children
|
||||
}}
|
||||
options={options?.map(({ form, onSubmit, ...option }) => ({
|
||||
dialogProps: {
|
||||
...dialogProps,
|
||||
title: typeof title === 'function' ? title(selectedRows) : title,
|
||||
children: typeof children === 'function' ? children(selectedRows) : children
|
||||
},
|
||||
form: form ? () => form(selectedRows) : undefined,
|
||||
onSubmit: data => onSubmit(data, selectedRows),
|
||||
...option
|
||||
|
@ -59,9 +59,9 @@ const GlobalActions = ({ globalActions, selectedRows }) => {
|
||||
<Action key={item.accessor} item={item} />
|
||||
))}
|
||||
{numberOfRowSelected > 0 && (
|
||||
actionsSelected?.map(item => {
|
||||
actionsSelected?.map((item, idx) => {
|
||||
const { min = 1, max = Number.MAX_SAFE_INTEGER } = item?.selected ?? {}
|
||||
const key = item.accessor ?? item.label
|
||||
const key = item.accessor ?? item.label ?? item.tooltip ?? idx
|
||||
|
||||
if (min < numberOfRowSelected && numberOfRowSelected > max) {
|
||||
return null
|
||||
|
@ -21,12 +21,9 @@ import {
|
||||
AddSquare,
|
||||
Import,
|
||||
Trash,
|
||||
PlayOutline,
|
||||
Lock,
|
||||
NoLock,
|
||||
UserSquareAlt,
|
||||
Group,
|
||||
ShareAndroid,
|
||||
Undo,
|
||||
Cart
|
||||
} from 'iconoir-react'
|
||||
|
||||
@ -84,6 +81,19 @@ const Actions = () => {
|
||||
// TODO: go to IMPORT form
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: VM_TEMPLATE_ACTIONS.INSTANTIATE_DIALOG,
|
||||
label: 'Instantiate',
|
||||
tooltip: 'Instantiate',
|
||||
icon: PlayOutline,
|
||||
selected: { max: 1 },
|
||||
action: rows => {
|
||||
const template = rows?.[0]?.original ?? {}
|
||||
const path = PATH.TEMPLATE.VMS.INSTANTIATE
|
||||
|
||||
history.push(path, template)
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: VM_TEMPLATE_ACTIONS.UPDATE_DIALOG,
|
||||
label: 'Update',
|
||||
@ -97,35 +107,23 @@ const Actions = () => {
|
||||
// history.push(path)
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: VM_TEMPLATE_ACTIONS.INSTANTIATE_DIALOG,
|
||||
label: 'Instantiate',
|
||||
tooltip: 'Instantiate',
|
||||
selected: { max: 1 },
|
||||
action: rows => {
|
||||
const template = rows?.[0]?.original ?? {}
|
||||
const path = PATH.TEMPLATE.VMS.INSTANTIATE
|
||||
|
||||
history.push(path, template)
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: VM_TEMPLATE_ACTIONS.CLONE,
|
||||
label: 'Clone',
|
||||
tooltip: 'Clone',
|
||||
selected: true,
|
||||
dialogProps: {
|
||||
title: rows => {
|
||||
const isMultiple = rows?.length > 1
|
||||
const { ID, NAME } = rows?.[0]?.original
|
||||
|
||||
return [
|
||||
isMultiple ? 'Clone several Templates' : 'Clone Template',
|
||||
!isMultiple && `#${ID} ${NAME}`
|
||||
].filter(Boolean).join(' - ')
|
||||
}
|
||||
},
|
||||
options: [{
|
||||
dialogProps: {
|
||||
title: rows => {
|
||||
const isMultiple = rows?.length > 1
|
||||
const { ID, NAME } = rows?.[0]?.original
|
||||
|
||||
return [
|
||||
isMultiple ? 'Clone several Templates' : 'Clone Template',
|
||||
!isMultiple && `#${ID} ${NAME}`
|
||||
].filter(Boolean).join(' - ')
|
||||
}
|
||||
},
|
||||
form: rows => {
|
||||
const vmTemplates = rows?.map(({ original }) => original)
|
||||
const stepProps = { isMultiple: vmTemplates.length > 1 }
|
||||
@ -153,76 +151,69 @@ const Actions = () => {
|
||||
},
|
||||
{
|
||||
tooltip: 'Change ownership',
|
||||
label: 'Ownership',
|
||||
icon: Group,
|
||||
selected: true,
|
||||
disabled: true,
|
||||
options: [{
|
||||
cy: `action.${VM_TEMPLATE_ACTIONS.CHANGE_OWNER}`,
|
||||
icon: UserSquareAlt,
|
||||
name: 'Change owner',
|
||||
disabled: true,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}, {
|
||||
cy: `action.${VM_TEMPLATE_ACTIONS.CHANGE_GROUP}`,
|
||||
icon: Group,
|
||||
name: 'Change group',
|
||||
disabled: true,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}, {
|
||||
cy: `action.${VM_TEMPLATE_ACTIONS.SHARE}`,
|
||||
icon: ShareAndroid,
|
||||
disabled: true,
|
||||
name: 'Share',
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}, {
|
||||
cy: `action.${VM_TEMPLATE_ACTIONS.UNSHARE}`,
|
||||
icon: Undo,
|
||||
disabled: true,
|
||||
name: 'Unshare',
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}]
|
||||
},
|
||||
{
|
||||
accessor: VM_TEMPLATE_ACTIONS.LOCK,
|
||||
tooltip: 'Lock',
|
||||
label: 'Lock',
|
||||
tooltip: 'Lock/Unlock',
|
||||
icon: Lock,
|
||||
selected: true,
|
||||
dialogProps: {
|
||||
title: 'Lock',
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'Lock: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
options: [{
|
||||
cy: `action.${VM_TEMPLATE_ACTIONS.LOCK}`,
|
||||
name: 'Lock',
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: 'Lock',
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'Templates: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
onSubmit: async (_, rows) => {
|
||||
const templateIds = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(templateIds.map(id => lock(id)))
|
||||
await Promise.all(templateIds.map(id => getVmTemplate(id)))
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => lock(id)))
|
||||
await Promise.all(ids.map(id => getVmTemplate(id)))
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
accessor: VM_TEMPLATE_ACTIONS.UNLOCK,
|
||||
tooltip: 'Unlock',
|
||||
label: 'Unlock',
|
||||
icon: NoLock,
|
||||
selected: true,
|
||||
dialogProps: {
|
||||
title: 'Unlock',
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'Unlock: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
options: [{
|
||||
}, {
|
||||
cy: `action.${VM_TEMPLATE_ACTIONS.UNLOCK}`,
|
||||
name: 'Unlock',
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: 'Unlock',
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'Templates: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
onSubmit: async (_, rows) => {
|
||||
const templateIds = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(templateIds.map(id => unlock(id)))
|
||||
await Promise.all(templateIds.map(id => getVmTemplate(id)))
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => unlock(id)))
|
||||
await Promise.all(ids.map(id => getVmTemplate(id)))
|
||||
}
|
||||
}]
|
||||
},
|
||||
@ -231,19 +222,19 @@ const Actions = () => {
|
||||
tooltip: 'Delete',
|
||||
icon: Trash,
|
||||
selected: true,
|
||||
dialogProps: {
|
||||
title: 'Delete',
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'Delete: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
options: [{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: 'Delete',
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'Templates: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
onSubmit: async (_, rows) => {
|
||||
const templateIds = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(templateIds.map(id => remove(id)))
|
||||
await getVmTemplates()
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => remove(id)))
|
||||
await Promise.all(ids.map(id => getVmTemplate(id)))
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
@ -14,13 +14,353 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as Icons from 'iconoir-react'
|
||||
import { useMemo } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import {
|
||||
RefreshDouble,
|
||||
AddSquare,
|
||||
PlayOutline,
|
||||
SaveFloppyDisk,
|
||||
TransitionRight,
|
||||
SystemShut,
|
||||
Group,
|
||||
Trash,
|
||||
Lock,
|
||||
Cart
|
||||
} from 'iconoir-react'
|
||||
|
||||
export default [
|
||||
{ title: 'Delete', icon: Icons.Trash, handleClick: () => undefined },
|
||||
{ title: 'Resume', icon: Icons.PlayOutline, handleClick: () => undefined },
|
||||
{ title: 'Power Off', icon: Icons.OffRounded, handleClick: () => undefined },
|
||||
{ title: 'Reboot', icon: Icons.Refresh, handleClick: () => undefined },
|
||||
{ title: 'Lock', icon: Icons.Lock, handleClick: () => undefined },
|
||||
{ title: 'Unlock', icon: Icons.NoLock, handleClick: () => undefined }
|
||||
]
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useVmApi } from 'client/features/One'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
// import { } from 'client/components/Forms/Vm'
|
||||
import { createActions } from 'client/components/Tables/Enhanced/Utils'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import { T, VM_ACTIONS, MARKETPLACE_APP_ACTIONS } from 'client/constants'
|
||||
|
||||
const Actions = () => {
|
||||
const history = useHistory()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const {
|
||||
getVm,
|
||||
getVms,
|
||||
terminate,
|
||||
terminateHard,
|
||||
undeploy,
|
||||
undeployHard,
|
||||
poweroff,
|
||||
poweroffHard,
|
||||
reboot,
|
||||
rebootHard,
|
||||
hold,
|
||||
release,
|
||||
stop,
|
||||
suspend,
|
||||
resume,
|
||||
resched,
|
||||
unresched,
|
||||
lock,
|
||||
unlock
|
||||
} = useVmApi()
|
||||
|
||||
const vmActions = useMemo(() => createActions({
|
||||
filters: getResourceView('VM')?.actions,
|
||||
actions: [
|
||||
{
|
||||
accessor: VM_ACTIONS.REFRESH,
|
||||
tooltip: T.Refresh,
|
||||
icon: RefreshDouble,
|
||||
action: async () => {
|
||||
await getVms({ state: -1 })
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: VM_ACTIONS.CREATE_DIALOG,
|
||||
tooltip: T.Create,
|
||||
icon: AddSquare,
|
||||
action: () => {
|
||||
const path = PATH.TEMPLATE.VMS.INSTANTIATE
|
||||
|
||||
history.push(path)
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: VM_ACTIONS.RESUME,
|
||||
tooltip: T.Resume,
|
||||
selected: true,
|
||||
icon: PlayOutline,
|
||||
action: async rows => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => resume(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: VM_ACTIONS.SAVE_AS_TEMPLATE,
|
||||
tooltip: T.SaveAsTemplate,
|
||||
selected: { max: 1 },
|
||||
disabled: true,
|
||||
icon: SaveFloppyDisk,
|
||||
action: () => {}
|
||||
},
|
||||
{
|
||||
tooltip: T.Manage,
|
||||
icon: SystemShut,
|
||||
selected: true,
|
||||
options: [{
|
||||
cy: `action.${VM_ACTIONS.SUSPEND}`,
|
||||
name: T.Suspend,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => suspend(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.STOP}`,
|
||||
name: T.Stop,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => stop(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.POWEROFF}`,
|
||||
name: T.Poweroff,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => poweroff(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.POWEROFF_HARD}`,
|
||||
name: T.PoweroffHard,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => poweroffHard(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.REBOOT}`,
|
||||
name: T.Reboot,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => reboot(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.REBOOT_HARD}`,
|
||||
name: T.RebootHard,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => rebootHard(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.UNDEPLOY}`,
|
||||
name: T.Undeploy,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => undeploy(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.UNDEPLOY_HARD}`,
|
||||
name: T.UndeployHard,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => undeployHard(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
tooltip: 'Hosting',
|
||||
icon: TransitionRight,
|
||||
selected: true,
|
||||
options: [{
|
||||
cy: `action.${VM_ACTIONS.DEPLOY}`,
|
||||
name: T.Deploy,
|
||||
disabled: true,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.MIGRATE}`,
|
||||
name: T.Migrate,
|
||||
disabled: true,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.MIGRATE_LIVE}`,
|
||||
name: T.MigrateLive,
|
||||
disabled: true,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.HOLD}`,
|
||||
name: T.Hold,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => hold(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.RELEASE}`,
|
||||
name: T.Release,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => release(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.RESCHED}`,
|
||||
name: T.Reschedule,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => resched(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.UNRESCHED}`,
|
||||
name: T.UnReschedule,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => unresched(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.RECOVER}`,
|
||||
name: T.Recover,
|
||||
disabled: true,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}]
|
||||
},
|
||||
{
|
||||
tooltip: 'Change ownership',
|
||||
icon: Group,
|
||||
selected: true,
|
||||
options: [{
|
||||
cy: `action.${VM_ACTIONS.CHANGE_OWNER}`,
|
||||
name: T.ChangeOwner,
|
||||
disabled: true,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.CHANGE_GROUP}`,
|
||||
name: T.ChangeGroup,
|
||||
disabled: true,
|
||||
isConfirmDialog: true,
|
||||
onSubmit: () => undefined
|
||||
}]
|
||||
},
|
||||
{
|
||||
tooltip: `${Tr(T.Lock)}/${Tr(T.Unlock)}`,
|
||||
icon: Lock,
|
||||
selected: true,
|
||||
options: [{
|
||||
cy: `action.${VM_ACTIONS.LOCK}`,
|
||||
name: T.Lock,
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Lock,
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'VMs: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => lock(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.UNLOCK}`,
|
||||
name: T.Unlock,
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Unlock,
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'VMs: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => unlock(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
tooltip: T.Terminate,
|
||||
icon: Trash,
|
||||
selected: true,
|
||||
options: [{
|
||||
cy: `action.${VM_ACTIONS.TERMINATE}`,
|
||||
name: T.Terminate,
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Terminate,
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'VMs: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => terminate(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}, {
|
||||
cy: `action.${VM_ACTIONS.TERMINATE_HARD}`,
|
||||
name: T.TerminateHard,
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.TerminateHard,
|
||||
children: rows => {
|
||||
const templates = rows?.map?.(({ original }) => original?.NAME)
|
||||
return 'VMs: ' + templates.join(', ')
|
||||
}
|
||||
},
|
||||
onSubmit: async (_, rows) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map(id => terminateHard(id)))
|
||||
await Promise.all(ids.map(id => getVm(id)))
|
||||
}
|
||||
}]
|
||||
}
|
||||
]
|
||||
}), [view])
|
||||
|
||||
const marketplaceAppActions = useMemo(() => createActions({
|
||||
filters: getResourceView('MARKETPLACE-APP')?.actions,
|
||||
actions: [
|
||||
{
|
||||
accessor: MARKETPLACE_APP_ACTIONS.CREATE_DIALOG,
|
||||
tooltip: 'Create Marketplace App',
|
||||
icon: Cart,
|
||||
selected: { max: 1 },
|
||||
disabled: true,
|
||||
action: rows => {
|
||||
// TODO: go to Marketplace App CREATE form
|
||||
}
|
||||
}
|
||||
]
|
||||
}), [view])
|
||||
|
||||
return [...vmActions, ...marketplaceAppActions]
|
||||
}
|
||||
|
||||
export default Actions
|
||||
|
@ -15,6 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { generatePath } from 'react-router-dom'
|
||||
|
||||
import { useUserApi, useGroupApi, RESOURCES } from 'client/features/One'
|
||||
import { List } from 'client/components/Tabs/Common'
|
||||
@ -56,7 +57,7 @@ const Ownership = memo(({
|
||||
name: T.Owner,
|
||||
value: userName,
|
||||
valueInOptionList: userId,
|
||||
link: PATH.SYSTEM.USERS.DETAIL.replace(':id', userId),
|
||||
link: generatePath(PATH.SYSTEM.USERS.DETAIL, { id: userId }),
|
||||
canEdit: actions?.includes?.(ACTIONS.CHANGE_OWNER),
|
||||
handleGetOptionList: getUserOptions,
|
||||
handleEdit: (_, user) => handleEdit?.({ user })
|
||||
@ -65,7 +66,7 @@ const Ownership = memo(({
|
||||
name: T.Group,
|
||||
value: groupName,
|
||||
valueInOptionList: groupId,
|
||||
link: PATH.SYSTEM.GROUPS.DETAIL.replace(':id', groupId),
|
||||
link: generatePath(PATH.SYSTEM.GROUPS.DETAIL, { id: groupId }),
|
||||
canEdit: actions?.includes?.(ACTIONS.CHANGE_GROUP),
|
||||
handleGetOptionList: getGroupOptions,
|
||||
handleEdit: (_, group) => handleEdit?.({ group })
|
||||
|
@ -15,24 +15,26 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import PropTypes from 'prop-types'
|
||||
import { generatePath } from 'react-router'
|
||||
|
||||
import { StatusChip, LinearProgressWithLabel } from 'client/components/Status'
|
||||
import { List } from 'client/components/Tabs/Common'
|
||||
|
||||
import * as Host from 'client/models/Host'
|
||||
import * as Datastore from 'client/models/Datastore'
|
||||
import { getState, getDatastores, getAllocatedInfo } from 'client/models/Host'
|
||||
import { getCapacityInfo } from 'client/models/Datastore'
|
||||
import { T, VM_ACTIONS } from 'client/constants'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
|
||||
const InformationPanel = ({ host = {}, handleRename, actions }) => {
|
||||
const { ID, NAME, IM_MAD, VM_MAD, CLUSTER_ID, CLUSTER } = host
|
||||
const { name: stateName, color: stateColor } = Host.getState(host)
|
||||
const datastores = Host.getDatastores(host)
|
||||
const { name: stateName, color: stateColor } = getState(host)
|
||||
const datastores = getDatastores(host)
|
||||
const {
|
||||
percentCpuUsed,
|
||||
percentCpuLabel,
|
||||
percentMemUsed,
|
||||
percentMemLabel
|
||||
} = Host.getAllocatedInfo(host)
|
||||
} = getAllocatedInfo(host)
|
||||
|
||||
const info = [
|
||||
{ name: T.ID, value: ID },
|
||||
@ -46,7 +48,12 @@ const InformationPanel = ({ host = {}, handleRename, actions }) => {
|
||||
name: T.State,
|
||||
value: <StatusChip text={stateName} stateColor={stateColor} />
|
||||
},
|
||||
{ name: T.Cluster, value: `#${CLUSTER_ID} ${CLUSTER}` },
|
||||
{
|
||||
name: T.Cluster,
|
||||
value: `#${CLUSTER_ID} ${CLUSTER}`,
|
||||
link: !Number.isNaN(+CLUSTER_ID) &&
|
||||
generatePath(PATH.INFRASTRUCTURE.CLUSTERS.DETAIL, { id: CLUSTER_ID })
|
||||
},
|
||||
{ name: T.IM_MAD, value: IM_MAD },
|
||||
{ name: T.VM_MAD, value: VM_MAD }
|
||||
]
|
||||
@ -60,7 +67,7 @@ const InformationPanel = ({ host = {}, handleRename, actions }) => {
|
||||
}]
|
||||
|
||||
const datastore = datastores.map(ds => {
|
||||
const { percentOfUsed, percentLabel } = Datastore.getCapacityInfo(ds)
|
||||
const { percentOfUsed, percentLabel } = getCapacityInfo(ds)
|
||||
|
||||
return {
|
||||
name: `#${ds?.ID}`, // TODO: add datastore name
|
||||
|
@ -74,10 +74,8 @@ const InformationPanel = ({ actions, vm = {}, handleResizeCapacity }) => {
|
||||
'data-cy': 'resize-capacity',
|
||||
label: T.Resize
|
||||
}}
|
||||
dialogProps={{
|
||||
title: T.ResizeCapacity
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: { title: T.ResizeCapacity },
|
||||
form: () => ResizeCapacityForm(undefined, vm.TEMPLATE),
|
||||
onSubmit: handleResizeCapacity
|
||||
}]}
|
||||
|
@ -14,28 +14,41 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { generatePath } from 'react-router-dom'
|
||||
|
||||
import { useCluster, useClusterApi } from 'client/features/One'
|
||||
import { StatusChip } from 'client/components/Status'
|
||||
import { List } from 'client/components/Tabs/Common'
|
||||
import Multiple from 'client/components/Tables/Vms/multiple'
|
||||
|
||||
import * as VirtualMachine from 'client/models/VirtualMachine'
|
||||
import { getState, getLastHistory, getIps } from 'client/models/VirtualMachine'
|
||||
import * as Helper from 'client/models/Helper'
|
||||
import { T, VM_ACTIONS } from 'client/constants'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
|
||||
const InformationPanel = ({ vm = {}, handleRename, actions }) => {
|
||||
const clusters = useCluster()
|
||||
const { getCluster } = useClusterApi()
|
||||
|
||||
const { ID, NAME, RESCHED, STIME, ETIME, LOCK, DEPLOY_ID } = vm
|
||||
const { name: stateName, color: stateColor } = getState(vm)
|
||||
const { HID: hostId, HOSTNAME: hostname = '--', CID: clusterId } = getLastHistory(vm)
|
||||
const ips = getIps(vm)
|
||||
|
||||
const { name: stateName, color: stateColor } = VirtualMachine.getState(vm)
|
||||
const [clusterName, setClusterName] = useState(
|
||||
() => clusterId === '-1' ? 'default' : clusters.find(c => c.ID === clusterId)?.NAME
|
||||
)
|
||||
|
||||
const { HID: hostId, HOSTNAME: hostname = '--', CID: clusterId } = VirtualMachine.getLastHistory(vm)
|
||||
const clusterName = clusterId === '-1' ? 'default' : '--' // TODO: get from cluster list
|
||||
const pathToHostDetail = PATH.INFRASTRUCTURE.HOSTS.DETAIL.replace(':id', hostId)
|
||||
const pathToClusterDetail = PATH.INFRASTRUCTURE.CLUSTERS.DETAIL.replace(':id', clusterId)
|
||||
useEffect(() => {
|
||||
const loadCluster = async () => {
|
||||
const cluster = await getCluster(clusterId)
|
||||
cluster?.NAME && setClusterName(cluster.NAME)
|
||||
}
|
||||
|
||||
const ips = VirtualMachine.getIps(vm)
|
||||
!clusterName && loadCluster()
|
||||
}, [])
|
||||
|
||||
const info = [
|
||||
{ name: T.ID, value: ID },
|
||||
@ -69,21 +82,23 @@ const InformationPanel = ({ vm = {}, handleRename, actions }) => {
|
||||
name: T.EndTime,
|
||||
value: Helper.timeToString(ETIME)
|
||||
},
|
||||
{
|
||||
hostId && {
|
||||
name: T.Host,
|
||||
value: hostId ? `#${hostId} ${hostname}` : '',
|
||||
link: !Number.isNaN(+hostId) && pathToHostDetail
|
||||
value: `#${hostId} ${hostname}`,
|
||||
link: !Number.isNaN(+hostId) &&
|
||||
generatePath(PATH.INFRASTRUCTURE.HOSTS.DETAIL, { id: hostId })
|
||||
},
|
||||
{
|
||||
clusterId && {
|
||||
name: T.Cluster,
|
||||
value: clusterId ? `#${clusterId} ${clusterName}` : '',
|
||||
link: !Number.isNaN(+clusterId) && pathToClusterDetail
|
||||
value: clusterName ? `#${clusterId} ${clusterName}` : `#${clusterId} --`,
|
||||
link: !Number.isNaN(+clusterId) &&
|
||||
generatePath(PATH.INFRASTRUCTURE.CLUSTERS.DETAIL, { id: clusterId })
|
||||
},
|
||||
{
|
||||
name: T.DeployID,
|
||||
value: DEPLOY_ID
|
||||
}
|
||||
]
|
||||
].filter(Boolean)
|
||||
|
||||
return (
|
||||
<List
|
||||
|
@ -59,12 +59,10 @@ const VmNetworkTab = ({ tabProps: { actions } = {} }) => {
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': 'attach-nic',
|
||||
label: `${Tr(T.Attach)} ${Tr(T.NIC)}`
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Attach)} ${Tr(T.NIC)}`
|
||||
label: Tr(T.AttachNic)
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: { title: T.AttachNic },
|
||||
form: () => AttachNicForm({ nics }),
|
||||
onSubmit: handleAttachNic
|
||||
}]}
|
||||
|
@ -47,18 +47,17 @@ const CreateSchedAction = memo(() => {
|
||||
'data-cy': 'create-sched-action',
|
||||
label: Tr(T.AddAction)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: Tr(T.ScheduledAction)
|
||||
}}
|
||||
options={[{
|
||||
cy: 'create-sched-action-punctual',
|
||||
name: 'Punctual action',
|
||||
dialogProps: { title: T.ScheduledAction },
|
||||
form: () => PunctualForm(vm),
|
||||
onSubmit: handleCreateSchedAction
|
||||
},
|
||||
{
|
||||
cy: 'create-sched-action-relative',
|
||||
name: 'Relative action',
|
||||
dialogProps: { title: T.ScheduledAction },
|
||||
form: () => RelativeForm(vm),
|
||||
onSubmit: handleCreateSchedAction
|
||||
}]}
|
||||
@ -90,10 +89,10 @@ const UpdateSchedAction = memo(({ schedule, name }) => {
|
||||
icon: <Edit size={18} />,
|
||||
tooltip: <Translate word={T.Edit} />
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Update)} ${T.ScheduledAction}: ${name}`
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: {
|
||||
title: <Translate word={T.UpdateScheduledAction} values={[name]} />
|
||||
},
|
||||
form: () => isRelative
|
||||
? RelativeForm(vm, schedule)
|
||||
: PunctualForm(vm, schedule),
|
||||
@ -122,12 +121,12 @@ const DeleteSchedAction = memo(({ schedule, name }) => {
|
||||
icon: <Trash size={18} />,
|
||||
tooltip: <Translate word={T.Delete} />
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Delete)} ${Tr(T.ScheduledAction)}: ${name}`,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
}}
|
||||
options={[{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: <Translate word={T.DeleteScheduledAction} values={[name]} />,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
},
|
||||
onSubmit: handleDelete
|
||||
}]}
|
||||
/>
|
||||
@ -166,37 +165,37 @@ const CharterAction = memo(() => {
|
||||
icon: <ClockOutline />,
|
||||
tooltip: Tr(T.Charter)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: Tr(T.ScheduledAction),
|
||||
children: (
|
||||
<>
|
||||
{leases.map(([action, { time } = {}], idx) => {
|
||||
const allPeriods = {
|
||||
years: time / 365 / 24 / 3600,
|
||||
months: time / 30 / 24 / 3600,
|
||||
weeks: time / 7 / 24 / 3600,
|
||||
days: time / 24 / 3600,
|
||||
hours: time / 3600,
|
||||
minutes: time / 60
|
||||
}
|
||||
|
||||
const [period, parsedTime] = Object
|
||||
.entries(allPeriods)
|
||||
.find(([_, time]) => time >= 1)
|
||||
|
||||
return (
|
||||
<p key={`${action}-${idx}`}>
|
||||
{`${action} - ${parsedTime} ${period}`}
|
||||
</p>
|
||||
)
|
||||
})}
|
||||
<hr />
|
||||
<p>{Tr(T.DoYouWantProceed)}</p>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
options={[{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: Tr(T.ScheduledAction),
|
||||
children: (
|
||||
<>
|
||||
{leases.map(([action, { time } = {}], idx) => {
|
||||
const allPeriods = {
|
||||
years: time / 365 / 24 / 3600,
|
||||
months: time / 30 / 24 / 3600,
|
||||
weeks: time / 7 / 24 / 3600,
|
||||
days: time / 24 / 3600,
|
||||
hours: time / 3600,
|
||||
minutes: time / 60
|
||||
}
|
||||
|
||||
const [period, parsedTime] = Object
|
||||
.entries(allPeriods)
|
||||
.find(([_, time]) => time >= 1)
|
||||
|
||||
return (
|
||||
<p key={`${action}-${idx}`}>
|
||||
{`${action} - ${parsedTime} ${period}`}
|
||||
</p>
|
||||
)
|
||||
})}
|
||||
<hr />
|
||||
<p>{Tr(T.DoYouWantProceed)}</p>
|
||||
</>
|
||||
)
|
||||
},
|
||||
onSubmit: handleCreateCharter
|
||||
}]}
|
||||
/>
|
||||
|
@ -23,7 +23,7 @@ import { useVmApi } from 'client/features/One'
|
||||
import { TabContext } from 'client/components/Tabs/TabProvider'
|
||||
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { T, VM_ACTIONS } from 'client/constants'
|
||||
|
||||
const RevertAction = memo(({ snapshot }) => {
|
||||
@ -39,12 +39,12 @@ const RevertAction = memo(({ snapshot }) => {
|
||||
'data-cy': `${VM_ACTIONS.SNAPSHOT_REVERT}-${SNAPSHOT_ID}`,
|
||||
icon: <UndoAction size={18} />
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Revert)}: #${SNAPSHOT_ID} - ${NAME}`,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
}}
|
||||
options={[{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: <Translate word={T.RevertSomething} values={`#${SNAPSHOT_ID} - ${NAME}`} />,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
},
|
||||
onSubmit: handleRevert
|
||||
}]}
|
||||
/>
|
||||
@ -64,12 +64,12 @@ const DeleteAction = memo(({ snapshot }) => {
|
||||
'data-cy': `${VM_ACTIONS.SNAPSHOT_DELETE}-${SNAPSHOT_ID}`,
|
||||
icon: <Trash size={18} />
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Delete)}: #${SNAPSHOT_ID} - ${NAME}`,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
}}
|
||||
options={[{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: <Translate word={T.DeleteSomething} values={`#${SNAPSHOT_ID} - ${NAME}`} />,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
},
|
||||
onSubmit: handleDelete
|
||||
}]}
|
||||
/>
|
||||
|
@ -52,10 +52,8 @@ const VmSnapshotTab = ({ tabProps: { actions } = {} }) => {
|
||||
'data-cy': 'snapshot-create',
|
||||
label: Tr(T.TakeSnapshot)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: Tr(T.TakeSnapshot)
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: { title: T.TakeSnapshot },
|
||||
form: () => CreateSnapshotForm(),
|
||||
onSubmit: handleSnapshotCreate
|
||||
}]}
|
||||
|
@ -24,7 +24,7 @@ import { TabContext } from 'client/components/Tabs/TabProvider'
|
||||
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
import { SaveAsDiskForm, CreateDiskSnapshotForm, ResizeDiskForm } from 'client/components/Forms/Vm'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { T, VM_ACTIONS } from 'client/constants'
|
||||
|
||||
const DetachAction = memo(({ disk, name: imageName }) => {
|
||||
@ -41,12 +41,12 @@ const DetachAction = memo(({ disk, name: imageName }) => {
|
||||
icon: <Trash size={18} />,
|
||||
tooltip: Tr(T.Detach)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Detach)}: #${DISK_ID} - ${imageName}`,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
}}
|
||||
options={[{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: <Translate word={T.DetachSomething} values={`#${DISK_ID} - ${imageName}`} />,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
},
|
||||
onSubmit: handleDetach
|
||||
}]}
|
||||
/>
|
||||
@ -74,12 +74,13 @@ const SaveAsAction = memo(({ disk, snapshot, name: imageName }) => {
|
||||
icon: <SaveActionFloppy size={18} />,
|
||||
tooltip: Tr(T.SaveAs)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: snapshot
|
||||
? `${Tr(T.SaveAs)} ${Tr(T.Image)}: #${snapshotId} - ${snapshotName}`
|
||||
: `${Tr(T.SaveAs)} ${Tr(T.Image)}: #${diskId} - ${imageName}`
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: {
|
||||
title: snapshot
|
||||
? <Translate word={T.SaveAsImage} values={`#${snapshotId} - ${snapshotName}`} />
|
||||
: <Translate word={T.SaveAsImage} values={`#${diskId} - ${imageName}`} />,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
},
|
||||
form: () => SaveAsDiskForm(),
|
||||
onSubmit: handleSaveAs
|
||||
}]}
|
||||
@ -104,10 +105,15 @@ const ResizeAction = memo(({ disk, name: imageName }) => {
|
||||
icon: <Expand size={18} />,
|
||||
tooltip: Tr(T.Resize)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Resize)}: #${DISK_ID} - ${imageName}`
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: {
|
||||
title: (
|
||||
<Translate
|
||||
word={T.ResizeSomething}
|
||||
values={`#${DISK_ID} - ${imageName}`}
|
||||
/>
|
||||
)
|
||||
},
|
||||
form: () => ResizeDiskForm(undefined, disk),
|
||||
onSubmit: handleResize
|
||||
}]}
|
||||
@ -132,10 +138,15 @@ const SnapshotCreateAction = memo(({ disk, name: imageName }) => {
|
||||
icon: <Camera size={18} />,
|
||||
tooltip: Tr(T.TakeSnapshot)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.TakeSnapshot)}: #${DISK_ID} - ${imageName}`
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: {
|
||||
title: (
|
||||
<Translate
|
||||
word={T.TakeSnapshotSomething}
|
||||
values={`#${DISK_ID} - ${imageName}`}
|
||||
/>
|
||||
)
|
||||
},
|
||||
form: () => CreateDiskSnapshotForm(),
|
||||
onSubmit: handleSnapshotCreate
|
||||
}]}
|
||||
@ -163,10 +174,11 @@ const SnapshotRenameAction = memo(({ disk, snapshot }) => {
|
||||
icon: <Edit size={18} />,
|
||||
tooltip: Tr(T.Edit)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Rename)}: #${ID} - ${NAME}`
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: {
|
||||
title: <Translate word={T.RenameSomething} values={`#${ID} - ${NAME}`} />,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
},
|
||||
form: () => CreateDiskSnapshotForm(undefined, snapshot),
|
||||
onSubmit: handleRename
|
||||
}]}
|
||||
@ -192,12 +204,12 @@ const SnapshotRevertAction = memo(({ disk, snapshot }) => {
|
||||
icon: <UndoAction size={18} />,
|
||||
tooltip: Tr(T.Revert)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Revert)}: #${ID} - ${NAME}`,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
}}
|
||||
options={[{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: <Translate word={T.RevertSomething} values={`#${ID} - ${NAME}`} />,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
},
|
||||
onSubmit: handleRevert
|
||||
}]}
|
||||
/>
|
||||
@ -222,12 +234,12 @@ const SnapshotDeleteAction = memo(({ disk, snapshot }) => {
|
||||
icon: <Trash size={18} />,
|
||||
tooltip: Tr(T.Delete)
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Delete)}: #${ID} - ${NAME}`,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
}}
|
||||
options={[{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: <Translate word={T.DeleteSomething} values={`#${ID} - ${NAME}`} />,
|
||||
children: <p>{Tr(T.DoYouWantProceed)}</p>
|
||||
},
|
||||
onSubmit: handleDelete
|
||||
}]}
|
||||
/>
|
||||
|
@ -51,21 +51,20 @@ const VmStorageTab = ({ tabProps: { actions } = {} }) => {
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': 'attach-disk',
|
||||
label: `${Tr(T.Attach)} ${Tr(T.Disk)}`
|
||||
}}
|
||||
dialogProps={{
|
||||
title: `${Tr(T.Attach)} ${Tr(T.Disk)}`
|
||||
label: Tr(T.AttachDisk)
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
cy: 'attach-image-disk',
|
||||
name: T.Image,
|
||||
dialogProps: { title: T.AttachImage },
|
||||
form: () => ImageSteps({ hypervisor }),
|
||||
onSubmit: handleAttachDisk
|
||||
},
|
||||
{
|
||||
cy: 'attach-volatile-disk',
|
||||
name: T.Volatile,
|
||||
dialogProps: { title: T.AttachVolatile },
|
||||
form: () => VolatileSteps({ hypervisor }),
|
||||
onSubmit: handleAttachDisk
|
||||
}
|
||||
|
@ -29,41 +29,79 @@ module.exports = {
|
||||
Active: 'Active',
|
||||
AddAction: 'Add action',
|
||||
Attach: 'Attach',
|
||||
AttachDisk: 'Attach disk',
|
||||
AttachImage: 'Attach image disk',
|
||||
AttachVolatile: 'Attach volatile disk',
|
||||
AttachNic: 'Attach NIC',
|
||||
BackToList: 'Back to %s list',
|
||||
Cancel: 'Cancel',
|
||||
Change: 'Change',
|
||||
ChangeGroup: 'Change group',
|
||||
ChangeOwner: 'Change owner',
|
||||
Clone: 'Clone',
|
||||
Configuration: 'Configuration',
|
||||
Create: 'Create',
|
||||
Delete: 'Delete',
|
||||
DeleteSomething: 'Delete %s',
|
||||
DeleteScheduledAction: 'Delete scheduled action: %s',
|
||||
DeleteSomething: 'Delete: %s',
|
||||
Deploy: 'Deploy',
|
||||
Detach: 'Detach',
|
||||
DetachSomething: 'Detach %s',
|
||||
DetachSomething: 'Detach: %s',
|
||||
Done: 'Done',
|
||||
Edit: 'Edit',
|
||||
EditSomething: 'Edit: %s',
|
||||
Finish: 'Finish',
|
||||
Hold: 'Hold',
|
||||
Info: 'Info',
|
||||
Instantiate: 'Instantiate',
|
||||
Lock: 'Lock',
|
||||
Migrate: 'Migrate',
|
||||
MigrateLive: 'Migrate live',
|
||||
Poweroff: 'Poweroff',
|
||||
PoweroffHard: 'Poweroff hard',
|
||||
Reboot: 'Reboot',
|
||||
RebootHard: 'Reboot hard',
|
||||
Recover: 'Recover',
|
||||
Refresh: 'Refresh',
|
||||
Release: 'Release',
|
||||
Remove: 'Remove',
|
||||
Rename: 'Rename',
|
||||
RenameSomething: 'Rename: %s',
|
||||
Reschedule: 'Reschedule',
|
||||
Resize: 'Resize',
|
||||
ResizeSomething: 'Resize: %s',
|
||||
ResizeCapacity: 'Resize capacity',
|
||||
Resume: 'Resume',
|
||||
Revert: 'Revert',
|
||||
RevertSomething: 'Revert: %s',
|
||||
Terminate: 'Terminate',
|
||||
TerminateHard: 'Terminate hard',
|
||||
Save: 'Save',
|
||||
SaveAs: 'Save as',
|
||||
SaveAsImage: 'Save as Image',
|
||||
SaveAsTemplate: 'Save as Template',
|
||||
Search: 'Search',
|
||||
Select: 'Select',
|
||||
SelectVmTemplate: 'Select a VM Template',
|
||||
SelectGroup: 'Select a group',
|
||||
SelectRequest: 'Select request',
|
||||
SelectVmTemplate: 'Select a VM Template',
|
||||
Show: 'Show',
|
||||
ShowAll: 'Show all',
|
||||
SignIn: 'Sign In',
|
||||
SignOut: 'Sign Out',
|
||||
Stop: 'Stop',
|
||||
Submit: 'Submit',
|
||||
Suspend: 'Suspend',
|
||||
Take: 'Take',
|
||||
TakeSnapshot: 'Take snapshot',
|
||||
TakeSnapshotOf: 'Take snapshot of %s',
|
||||
TakeSnapshotSomething: 'Take snapshot: %s',
|
||||
TakeSnapshotOf: 'Take snapshot: %s',
|
||||
Undeploy: 'Undeploy',
|
||||
UndeployHard: 'Undeploy hard',
|
||||
Unlock: 'Unlock',
|
||||
UnReschedule: 'Un-Reschedule',
|
||||
Update: 'Update',
|
||||
UpdateScheduledAction: 'Update scheduled action: %s',
|
||||
|
||||
/* questions */
|
||||
Yes: 'Yes',
|
||||
@ -241,7 +279,6 @@ module.exports = {
|
||||
|
||||
/* instances schema */
|
||||
IP: 'IP',
|
||||
Reschedule: 'Reschedule',
|
||||
DeployID: 'Deploy ID',
|
||||
Deployment: 'Deployment',
|
||||
Monitoring: 'Monitoring',
|
||||
|
@ -432,39 +432,41 @@ export const VM_LCM_STATES = [
|
||||
|
||||
/** @enum {string} Virtual machine actions */
|
||||
export const VM_ACTIONS = {
|
||||
REFRESH: 'refresh',
|
||||
CREATE_DIALOG: 'create_dialog',
|
||||
DEPLOY: 'deploy',
|
||||
MIGRATE: 'migrate',
|
||||
MIGRATE_LIVE: 'migrate_live',
|
||||
MIGRATE_POFF: 'migrate_poff',
|
||||
MIGRATE_POFF_HARD: 'migrate_poff_hard',
|
||||
HOLD: 'hold',
|
||||
RELEASE: 'release',
|
||||
SUSPEND: 'suspend',
|
||||
RESUME: 'resume',
|
||||
STOP: 'stop',
|
||||
RECOVER: 'recover',
|
||||
REBOOT: 'reboot',
|
||||
REBOOT_HARD: 'reboot_hard',
|
||||
POWEROFF: 'poweroff',
|
||||
LOCK: 'lock',
|
||||
MIGRATE_LIVE: 'migrate_live',
|
||||
MIGRATE_POFF_HARD: 'migrate_poff_hard',
|
||||
MIGRATE_POFF: 'migrate_poff',
|
||||
MIGRATE: 'migrate',
|
||||
POWEROFF_HARD: 'poweroff_hard',
|
||||
UNDEPLOY: 'undeploy',
|
||||
UNDEPLOY_HARD: 'undeploy_hard',
|
||||
TERMINATE: 'terminate',
|
||||
TERMINATE_HARD: 'terminate_hard',
|
||||
POWEROFF: 'poweroff',
|
||||
REBOOT_HARD: 'reboot_hard',
|
||||
REBOOT: 'reboot',
|
||||
RECOVER: 'recover',
|
||||
RELEASE: 'release',
|
||||
RESCHED: 'resched',
|
||||
UNRESCHED: 'unresched',
|
||||
RESUME: 'resume',
|
||||
SAVE_AS_TEMPLATE: 'save_as_template',
|
||||
LOCK: 'lockU',
|
||||
STOP: 'stop',
|
||||
SUSPEND: 'suspend',
|
||||
TERMINATE_HARD: 'terminate_hard',
|
||||
TERMINATE: 'terminate',
|
||||
UNDEPLOY_HARD: 'undeploy_hard',
|
||||
UNDEPLOY: 'undeploy',
|
||||
UNLOCK: 'unlock',
|
||||
STAR_TVNC: 'startvnc',
|
||||
STAR_TVMRC: 'startvmrc',
|
||||
STAR_TSPICE: 'startspice',
|
||||
UNRESCHED: 'unresched',
|
||||
|
||||
// REMOTE
|
||||
VMRC: 'vmrc',
|
||||
SPICE: 'spice',
|
||||
VNC: 'vnc',
|
||||
SSH: 'ssh',
|
||||
RDP: 'rdp',
|
||||
SAVE_RDP: 'save_rdp',
|
||||
SAVE_VIRT_VIEWER: 'save_virt_viewer',
|
||||
FILE_RDP: 'file_rdp',
|
||||
FILE_VIRT_VIEWER: 'file_virt_viewer',
|
||||
|
||||
RENAME: ACTIONS.RENAME,
|
||||
CHANGE_MODE: ACTIONS.CHANGE_MODE,
|
||||
|
@ -16,7 +16,7 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useHistory, generatePath } from 'react-router-dom'
|
||||
import { Container, Box } from '@material-ui/core'
|
||||
|
||||
import { PATH } from 'client/apps/sunstone/routesFlow'
|
||||
@ -65,9 +65,8 @@ function ApplicationsTemplates () {
|
||||
gridProps={{ 'data-cy': 'applications-templates' }}
|
||||
CardComponent={ApplicationTemplateCard}
|
||||
cardsProps={({ value }) => ({
|
||||
handleEdit: () => history.push(
|
||||
PATH.APPLICATIONS_TEMPLATES.EDIT.replace(':id', value?.ID)
|
||||
),
|
||||
handleEdit: () =>
|
||||
history.push(generatePath(PATH.APPLICATIONS_TEMPLATES.EDIT, { id: value?.ID })),
|
||||
handleDeploy: () => setShowDialog(value),
|
||||
handleRemove: undefined // TODO
|
||||
})}
|
||||
|
@ -16,7 +16,7 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useHistory, generatePath } from 'react-router-dom'
|
||||
import { useTheme, Container, Box } from '@material-ui/core'
|
||||
import { Trash as DeleteIcon, Settings as EditIcon } from 'iconoir-react'
|
||||
|
||||
@ -88,7 +88,7 @@ function Providers () {
|
||||
actions: [
|
||||
{
|
||||
handleClick: () =>
|
||||
history.push(PATH.PROVIDERS.EDIT.replace(':id', ID)),
|
||||
history.push(generatePath(PATH.PROVIDERS.EDIT, { id: ID })),
|
||||
icon: <EditIcon />,
|
||||
cy: 'provider-edit'
|
||||
},
|
||||
|
@ -38,12 +38,14 @@ const ResponseForm = ({
|
||||
const { control, handleSubmit, errors, formState } = useForm()
|
||||
|
||||
const onSubmit = async dataForm => {
|
||||
const config = requestConfig(dataForm, { name, httpMethod, params })
|
||||
try {
|
||||
const config = requestConfig(dataForm, { name, httpMethod, params })
|
||||
|
||||
const { id, ...res } = await RestClient.request(config) ?? {}
|
||||
|
||||
id === 401 && console.log('ERROR')
|
||||
id === 200 && handleChangeResponse(JSON.stringify(res, null, '\t'))
|
||||
const { id, ...res } = await RestClient.request(config) ?? {}
|
||||
handleChangeResponse(JSON.stringify(res, null, '\t'))
|
||||
} catch (err) {
|
||||
console.log('ERROR', err)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -19,11 +19,13 @@ import { useState } from 'react'
|
||||
import { Container, Box } from '@material-ui/core'
|
||||
|
||||
import { VmsTable } from 'client/components/Tables'
|
||||
import VmActions from 'client/components/Tables/Vms/actions'
|
||||
import VmTabs from 'client/components/Tabs/Vm'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
|
||||
function VirtualMachines () {
|
||||
const [selectedRows, onSelectedRowsChange] = useState([])
|
||||
const actions = VmActions()
|
||||
|
||||
const getRowIds = () =>
|
||||
JSON.stringify(selectedRows?.map(row => row.id).join(', '), null, 2)
|
||||
@ -38,7 +40,10 @@ function VirtualMachines () {
|
||||
component={Container}
|
||||
>
|
||||
<SplitPane>
|
||||
<VmsTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
<VmsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', overflow: 'auto' }}>
|
||||
|
@ -23,7 +23,7 @@ import { httpCodes } from 'server/utils/constants'
|
||||
/**
|
||||
* @param {string} type - Name of redux action
|
||||
* @param {Promise} service - Request from service
|
||||
* @param {Function} [wrapResult] - Function to wrapping the response
|
||||
* @param {function(object, object)} [wrapResult] - Function to wrapping the response
|
||||
* @returns {AsyncThunkAction} Asynchronous redux action
|
||||
*/
|
||||
export const createAction = (type, service, wrapResult) =>
|
||||
|
@ -26,23 +26,43 @@ export const getVm = createAction(`${VM}/detail`, vmService.getVm)
|
||||
export const getVms = createAction(
|
||||
`${VM}/pool`,
|
||||
vmService.getVms,
|
||||
(response, { vms: currentVms }) => {
|
||||
(response, { [RESOURCES.vm]: currentVms }) => {
|
||||
const vms = filterBy([...currentVms, ...response], 'ID')
|
||||
|
||||
return { [RESOURCES.vm]: vms }
|
||||
}
|
||||
)
|
||||
|
||||
export const terminateVm = createAction(
|
||||
`${VM}/delete`,
|
||||
payload => vmService.actionVm({
|
||||
...payload,
|
||||
action: {
|
||||
params: { hard: false },
|
||||
perform: 'terminate'
|
||||
}
|
||||
})
|
||||
)
|
||||
export const terminate = createAction(`${VM}/terminate`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'terminate' }))
|
||||
export const terminateHard = createAction(`${VM}/terminate-hard`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'terminate-hard' }))
|
||||
export const undeploy = createAction(`${VM}/undeploy`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'undeploy' }))
|
||||
export const undeployHard = createAction(`${VM}/undeploy-hard`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'undeploy-hard' }))
|
||||
export const poweroff = createAction(`${VM}/poweroff`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'poweroff' }))
|
||||
export const poweroffHard = createAction(`${VM}/poweroff-hard`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'poweroff-hard' }))
|
||||
export const reboot = createAction(`${VM}/reboot`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'reboot' }))
|
||||
export const rebootHard = createAction(`${VM}/reboot-hard`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'reboot-hard' }))
|
||||
export const hold = createAction(`${VM}/hold`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'hold' }))
|
||||
export const release = createAction(`${VM}/release`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'release' }))
|
||||
export const stop = createAction(`${VM}/stop`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'stop' }))
|
||||
export const suspend = createAction(`${VM}/suspend`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'suspend' }))
|
||||
export const resume = createAction(`${VM}/resume`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'resume' }))
|
||||
export const resched = createAction(`${VM}/resched`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'resched' }))
|
||||
export const unresched = createAction(`${VM}/unresched`,
|
||||
({ id }) => vmService.actionVm({ id, action: 'unresched' }))
|
||||
|
||||
export const updateUserTemplate = createAction(`${VM}/update`, vmService.updateUserTemplate)
|
||||
export const rename = createAction(`${VM}/rename`, vmService.rename)
|
||||
|
@ -36,7 +36,21 @@ export const useVmApi = () => {
|
||||
return {
|
||||
getVm: id => unwrapDispatch(actions.getVm({ id })),
|
||||
getVms: options => unwrapDispatch(actions.getVms(options)),
|
||||
terminateVm: id => unwrapDispatch(actions.terminateVm({ id })),
|
||||
terminate: id => unwrapDispatch(actions.terminate({ id })),
|
||||
terminateHard: id => unwrapDispatch(actions.terminateHard({ id })),
|
||||
undeploy: id => unwrapDispatch(actions.undeploy({ id })),
|
||||
undeployHard: id => unwrapDispatch(actions.undeployHard({ id })),
|
||||
poweroff: id => unwrapDispatch(actions.poweroff({ id })),
|
||||
poweroffHard: id => unwrapDispatch(actions.poweroffHard({ id })),
|
||||
reboot: id => unwrapDispatch(actions.reboot({ id })),
|
||||
rebootHard: id => unwrapDispatch(actions.rebootHard({ id })),
|
||||
hold: id => unwrapDispatch(actions.hold({ id })),
|
||||
release: id => unwrapDispatch(actions.release({ id })),
|
||||
stop: id => unwrapDispatch(actions.stop({ id })),
|
||||
suspend: id => unwrapDispatch(actions.suspend({ id })),
|
||||
resume: id => unwrapDispatch(actions.resume({ id })),
|
||||
resched: id => unwrapDispatch(actions.resched({ id })),
|
||||
unresched: id => unwrapDispatch(actions.unresched({ id })),
|
||||
updateUserTemplate: (id, template, replace) =>
|
||||
unwrapDispatch(actions.updateUserTemplate({ id, template, replace })),
|
||||
rename: (id, name) => unwrapDispatch(actions.rename({ id, name })),
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useReducer, useCallback, useEffect, useRef, ReducerState, ReducerAction } from 'react'
|
||||
import { fakeDelay } from 'client/utils'
|
||||
import { fakeDelay, isDevelopment } from 'client/utils'
|
||||
|
||||
const STATUS = {
|
||||
INIT: 'INIT',
|
||||
@ -144,7 +144,7 @@ const useFetch = (request, socket) => {
|
||||
const { reload = false, delay = 0 } = options
|
||||
|
||||
if (!(Number.isInteger(delay) && delay >= 0)) {
|
||||
console.error(`
|
||||
isDevelopment() && console.error(`
|
||||
Delay must be a number >= 0!
|
||||
If you're using it as a function, it must also return a number >= 0.`)
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useReducer, useCallback, useEffect, useRef, ReducerState, ReducerAction } from 'react'
|
||||
import { fakeDelay } from 'client/utils'
|
||||
import { fakeDelay, isDevelopment } from 'client/utils'
|
||||
|
||||
const STATUS = {
|
||||
INIT: 'INIT',
|
||||
@ -126,7 +126,7 @@ const useFetchAll = () => {
|
||||
const { reload = false, delay = 0 } = options
|
||||
|
||||
if (!(Number.isInteger(delay) && delay >= 0)) {
|
||||
console.error(`
|
||||
isDevelopment() && console.error(`
|
||||
Delay must be a number >= 0!
|
||||
If you're using it as a function, it must also return a number >= 0.`)
|
||||
}
|
||||
|
@ -238,11 +238,13 @@ const commandXMLRPC = (resource = '', method = '', defaultMethod = '') => {
|
||||
const commandWithDefault = defaultMethod
|
||||
? `${command}.${defaultMethod}`
|
||||
: command
|
||||
if (typeof method === 'string' && method !== 'action') {
|
||||
|
||||
if (method) {
|
||||
command = allowedActions.includes(method)
|
||||
? `${command}.${method}`
|
||||
: commandWithDefault
|
||||
}
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user