From d5b2e8a2864a95d31b5bcda9b73b81baaf5f04a2 Mon Sep 17 00:00:00 2001 From: Sergio Betanzos Date: Mon, 5 Jul 2021 14:56:06 +0200 Subject: [PATCH] F OpenNebula/one#5422: Add view condition to vm detail --- .../components/Tables/Enhanced/Utils/utils.js | 6 +- .../client/components/Tables/Vms/detail.js | 193 ++---------------- .../src/client/components/Tables/Vms/index.js | 13 +- .../src/client/components/Tabs/Vm/capacity.js | 30 +++ .../components/Tabs/Vm/configuration.js | 41 ++++ .../src/client/components/Tabs/Vm/index.js | 52 +++++ .../src/client/components/Tabs/Vm/info.js | 47 +++++ .../src/client/components/Tabs/Vm/log.js | 13 ++ .../src/client/components/Tabs/Vm/network.js | 33 +++ .../client/components/Tabs/Vm/placement.js | 13 ++ .../client/components/Tabs/Vm/schedActions.js | 13 ++ .../src/client/components/Tabs/Vm/snapshot.js | 13 ++ .../src/client/components/Tabs/Vm/storage.js | 55 +++++ .../src/client/components/Tabs/index.js | 2 +- .../containers/Dashboard/Sunstone/index.js | 4 +- 15 files changed, 342 insertions(+), 186 deletions(-) create mode 100644 src/fireedge/src/client/components/Tabs/Vm/capacity.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/configuration.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/index.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/info.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/log.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/network.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/placement.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/schedActions.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/snapshot.js create mode 100644 src/fireedge/src/client/components/Tabs/Vm/storage.js diff --git a/src/fireedge/src/client/components/Tables/Enhanced/Utils/utils.js b/src/fireedge/src/client/components/Tables/Enhanced/Utils/utils.js index 8f31815d0c..c1644ac579 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/Utils/utils.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/Utils/utils.js @@ -1,10 +1,6 @@ import { CategoryFilter } from 'client/components/Tables/Enhanced/Utils' -export const createColumns = ({ view, resource, columns }) => { - const filters = view - ?.find(({ resource_name: name }) => name === resource) - ?.filters ?? {} - +export const createColumns = ({ filters = {}, columns = [] }) => { if (Object.keys(filters).length === 0) return columns return columns.map(column => { diff --git a/src/fireedge/src/client/components/Tables/Vms/detail.js b/src/fireedge/src/client/components/Tables/Vms/detail.js index 0d28dfcaa7..1eb88e940a 100644 --- a/src/fireedge/src/client/components/Tables/Vms/detail.js +++ b/src/fireedge/src/client/components/Tables/Vms/detail.js @@ -1,27 +1,24 @@ -import React, { useEffect } from 'react' +import * as React from 'react' import PropTypes from 'prop-types' -import { LinearProgress, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core' - -import Tabs from 'client/components/Tabs' -import { StatusBadge } from 'client/components/Status' +import { LinearProgress } from '@material-ui/core' import { useFetch, useSocket } from 'client/hooks' import { useVmApi } from 'client/features/One' -import * as VirtualMachine from 'client/models/VirtualMachine' -import * as Helper from 'client/models/Helper' -import { prettyBytes } from 'client/utils' +import VmTabs from 'client/components/Tabs/Vm' -const NavArrowDown = {'>'} - -const VmDetail = ({ id }) => { +const VmDetail = React.memo(({ id, view = {} }) => { const { getHooksSocket } = useSocket() - const socket = getHooksSocket({ resource: 'vm', id }) - const { getVm } = useVmApi() - const { data, fetchRequest, loading, error } = useFetch(getVm, socket) - useEffect(() => { + const { + data, + fetchRequest, + loading, + error + } = useFetch(getVm, getHooksSocket({ resource: 'vm', id })) + + React.useEffect(() => { fetchRequest(id) }, [id]) @@ -33,168 +30,20 @@ const VmDetail = ({ id }) => { return
{error}
} - const { ID, NAME, UNAME, GNAME, RESCHED, STIME, ETIME, LOCK, DEPLOY_ID, TEMPLATE, USER_TEMPLATE } = data - - const isVCenter = VirtualMachine.isVCenter(data) - const { name: stateName, color: stateColor } = VirtualMachine.getState(data) - - const { HID: hostId, HOSTNAME: hostname = '--', CID: clusterId } = VirtualMachine.getLastHistory(data) - const clusterName = clusterId === '-1' ? 'default' : '--' // TODO: get from cluster list - - const ips = VirtualMachine.getIps(data) - const { nics, alias } = VirtualMachine.splitNicAlias(data) - - const disks = VirtualMachine.getDisks(data) - - const tabs = [ - { - name: 'info', - renderContent: ( -
-
- - - {`#${ID} - ${NAME}`} - -
-
-

Owner: {UNAME}

-

Group: {GNAME}

-

Reschedule: {Helper.booleanToString(+RESCHED)}

-

Locked: {Helper.levelLockToString(LOCK?.LOCKED)}

-

IP: {ips.join(', ') || '--'}

-

Start time: {Helper.timeToString(STIME)}

-

End time: {Helper.timeToString(ETIME)}

-

Host: {hostId ? `#${hostId} ${hostname}` : ''}

-

Cluster: {clusterId ? `#${clusterId} ${clusterName}` : ''}

-

Deploy ID: {DEPLOY_ID}

-
-
- ) - }, - { - name: 'capacity', - renderContent: ( -
-

Physical CPU: {TEMPLATE?.CPU}

-

Virtual CPU: {TEMPLATE?.VCPU ?? '-'}

- {isVCenter && ( -

Virtual Cores: {` - Cores x ${TEMPLATE?.TOPOLOGY?.CORES || '-'} | - Sockets ${TEMPLATE?.TOPOLOGY?.SOCKETS || '-'} - `}

- )} -

Memory: {prettyBytes(+TEMPLATE?.MEMORY, 'MB')}

-

Cost / CPU: {TEMPLATE?.CPU_COST}

-

Cost / MByte: {TEMPLATE?.MEMORY_COST}

-
- ) - }, - { - name: 'storage', - renderContent: ( -
-

VM DISKS

- {disks.map(({ - DISK_ID, - DATASTORE = '-', - TARGET = '-', - IMAGE, - TYPE, - FORMAT, - SIZE, - MONITOR_SIZE, - READONLY, - SAVE = 'No', - CLONE - }) => { - const size = +SIZE ? prettyBytes(+SIZE, 'MB') : '-' - const monitorSize = +MONITOR_SIZE ? prettyBytes(+MONITOR_SIZE, 'MB') : '-' - - const type = String(TYPE).toLowerCase() - - const image = IMAGE ?? ({ - fs: `${FORMAT} - ${size}`, - swap: size - }[type]) - - return ( -

- {`${DISK_ID} | ${DATASTORE} | ${TARGET} | ${image} | ${monitorSize}/${size} | ${type} | ${READONLY} | ${SAVE} | ${CLONE}`} -

- ) - })} -
- ) - }, - { - name: 'network', - renderContent: ( -
-
-

VM NICS

- {nics.map(({ NIC_ID, NETWORK = '-', BRIDGE = '-', IP = '-', MAC = '-', PCI_ID = '' }) => ( -

- {`${NIC_ID} | ${NETWORK} | ${BRIDGE} | ${IP} | ${MAC} | ${PCI_ID}`} -

- ))} -
-
-
-

VM ALIAS

- {alias.map(({ NIC_ID, NETWORK = '-', BRIDGE = '-', IP = '-', MAC = '-' }) => ( -

- {`${NIC_ID} | ${NETWORK} | ${BRIDGE} | ${IP} | ${MAC}`} -

- ))} -
-
- ) - }, - { - name: 'template', - renderContent: ( -
- - - User Template - - -
-                
-                  {JSON.stringify(USER_TEMPLATE, null, 2)}
-                
-              
-
-
- - - Template - - -
-                
-                  {JSON.stringify(TEMPLATE, null, 2)}
-                
-              
-
-
-
- ) - } - ] + const tabs = Object.entries(view['info-tabs'] ?? {}) + ?.map(([tabName, { enabled } = {}]) => !!enabled && tabName) + ?.filter(Boolean) return ( - + ) -} +}) VmDetail.propTypes = { - id: PropTypes.string.isRequired + id: PropTypes.string.isRequired, + view: PropTypes.object.isRequired } +VmDetail.displayName = 'VmDetail' + export default VmDetail diff --git a/src/fireedge/src/client/components/Tables/Vms/index.js b/src/fireedge/src/client/components/Tables/Vms/index.js index 38425446f7..1516072f27 100644 --- a/src/fireedge/src/client/components/Tables/Vms/index.js +++ b/src/fireedge/src/client/components/Tables/Vms/index.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react' +import * as React from 'react' import { useAuth } from 'client/features/Auth' import { useFetch } from 'client/hooks' @@ -18,16 +18,19 @@ const VmsTable = () => { const { getVms } = useVmApi() const { view, views, filterPool } = useAuth() + const viewSelected = React.useMemo(() => views[view], [view]) + + const resourceView = viewSelected?.find(({ resource_name: name }) => name === 'VM') + const columns = React.useMemo(() => createColumns({ - view: views[view], - resource: 'VM', + filters: resourceView?.filters, columns: VmColumns }), [view]) const { status, data, fetchRequest, loading, reloading, error, STATUS } = useFetch(getVms) const { INIT, PENDING } = STATUS - useEffect(() => { + React.useEffect(() => { const requests = { INIT: () => fetchRequest({ start: INITIAL_ELEMENT, @@ -60,7 +63,7 @@ const VmsTable = () => { isLoading={loading || reloading} getRowId={row => String(row.ID)} RowComponent={VmRow} - renderDetail={row => } + renderDetail={row => } /> ) } diff --git a/src/fireedge/src/client/components/Tabs/Vm/capacity.js b/src/fireedge/src/client/components/Tabs/Vm/capacity.js new file mode 100644 index 0000000000..218d31c0a7 --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/capacity.js @@ -0,0 +1,30 @@ +import * as React from 'react' + +import * as VirtualMachine from 'client/models/VirtualMachine' +import { prettyBytes } from 'client/utils' + +const VmCapacityTab = data => { + const { TEMPLATE } = data + + const isVCenter = VirtualMachine.isVCenter(data) + + return ( +
+

Physical CPU: {TEMPLATE?.CPU}

+

Virtual CPU: {TEMPLATE?.VCPU ?? '-'}

+ {isVCenter && ( +

Virtual Cores: {` + Cores x ${TEMPLATE?.TOPOLOGY?.CORES || '-'} | + Sockets ${TEMPLATE?.TOPOLOGY?.SOCKETS || '-'} + `}

+ )} +

Memory: {prettyBytes(+TEMPLATE?.MEMORY, 'MB')}

+

Cost / CPU: {TEMPLATE?.CPU_COST}

+

Cost / MByte: {TEMPLATE?.MEMORY_COST}

+
+ ) +} + +VmCapacityTab.displayName = 'VmCapacityTab' + +export default VmCapacityTab diff --git a/src/fireedge/src/client/components/Tabs/Vm/configuration.js b/src/fireedge/src/client/components/Tabs/Vm/configuration.js new file mode 100644 index 0000000000..412a6eb350 --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/configuration.js @@ -0,0 +1,41 @@ +import * as React from 'react' +import { Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core' + +const NavArrowDown = {'>'} + +const VmConfigurationTab = data => { + const { TEMPLATE, USER_TEMPLATE } = data + + return ( +
+ + + {'User Template'} + + +
+            
+              {JSON.stringify(USER_TEMPLATE, null, 2)}
+            
+          
+
+
+ + + {'Template'} + + +
+            
+              {JSON.stringify(TEMPLATE, null, 2)}
+            
+          
+
+
+
+ ) +} + +VmConfigurationTab.displayName = 'VmConfigurationTab' + +export default VmConfigurationTab diff --git a/src/fireedge/src/client/components/Tabs/Vm/index.js b/src/fireedge/src/client/components/Tabs/Vm/index.js new file mode 100644 index 0000000000..c1e3f3c06a --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/index.js @@ -0,0 +1,52 @@ +import * as React from 'react' +import PropTypes from 'prop-types' + +import Tabs from 'client/components/Tabs' + +const stringToCamelCase = s => + s.replace( + /([-_][a-z])/ig, + $1 => $1.toUpperCase() + .replace('-', '') + .replace('_', '') + ) + +const stringToCamelSpace = s => s.replace(/([a-z])([A-Z])/g, '$1 $2') + +const VmTabs = ({ data, tabs }) => { + const [renderTabs, setTabs] = React.useState(() => []) + + React.useEffect(() => { + const loadTab = async tabKey => { + try { + const camelCaseKey = stringToCamelCase(tabKey) + + // dynamic import => client/components/Tabs/Vm + const tabComponent = await import(`./${camelCaseKey}`) + + setTabs(prev => prev.concat([{ + name: stringToCamelSpace(camelCaseKey), + renderContent: tabComponent.default(data) + }])) + } catch (error) {} + } + + // reset + setTabs([]) + + tabs?.forEach(loadTab) + }, [tabs?.length]) + + return +} + +VmTabs.propTypes = { + data: PropTypes.object.isRequired, + tabs: PropTypes.arrayOf( + PropTypes.string + ).isRequired +} + +VmTabs.displayName = 'VmTabs' + +export default VmTabs diff --git a/src/fireedge/src/client/components/Tabs/Vm/info.js b/src/fireedge/src/client/components/Tabs/Vm/info.js new file mode 100644 index 0000000000..e2459eb002 --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/info.js @@ -0,0 +1,47 @@ +import * as React from 'react' +import { StatusBadge } from 'client/components/Status' + +import * as VirtualMachine from 'client/models/VirtualMachine' +import * as Helper from 'client/models/Helper' + +const VmInfoTab = data => { + const { ID, NAME, UNAME, GNAME, RESCHED, STIME, ETIME, LOCK, DEPLOY_ID } = data + + const { name: stateName, color: stateColor } = VirtualMachine.getState(data) + + const { HID: hostId, HOSTNAME: hostname = '--', CID: clusterId } = VirtualMachine.getLastHistory(data) + const clusterName = clusterId === '-1' ? 'default' : '--' // TODO: get from cluster list + + const ips = VirtualMachine.getIps(data) + + return ( +
+

+ + + {`#${ID} - ${NAME}`} + +

+
+

Owner: {UNAME}

+

Group: {GNAME}

+

Reschedule: {Helper.booleanToString(+RESCHED)}

+

Locked: {Helper.levelLockToString(LOCK?.LOCKED)}

+

IP: {ips.join(', ') || '--'}

+

Start time: {Helper.timeToString(STIME)}

+

End time: {Helper.timeToString(ETIME)}

+

Host: {hostId ? `#${hostId} ${hostname}` : ''}

+

Cluster: {clusterId ? `#${clusterId} ${clusterName}` : ''}

+

Deploy ID: {DEPLOY_ID}

+
+
+ ) +} + +VmInfoTab.displayName = 'VmInfoTab' + +export default VmInfoTab diff --git a/src/fireedge/src/client/components/Tabs/Vm/log.js b/src/fireedge/src/client/components/Tabs/Vm/log.js new file mode 100644 index 0000000000..9f3c523ede --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/log.js @@ -0,0 +1,13 @@ +import * as React from 'react' + +const VmLogTab = data => { + return ( +
+

WIP - LOG

+
+ ) +} + +VmLogTab.displayName = 'VmLogTab' + +export default VmLogTab diff --git a/src/fireedge/src/client/components/Tabs/Vm/network.js b/src/fireedge/src/client/components/Tabs/Vm/network.js new file mode 100644 index 0000000000..1b77cb1fa5 --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/network.js @@ -0,0 +1,33 @@ +import * as React from 'react' + +import * as VirtualMachine from 'client/models/VirtualMachine' + +const VmNetworkTab = data => { + const { nics, alias } = VirtualMachine.splitNicAlias(data) + + return ( +
+
+

VM NICS

+ {nics.map(({ NIC_ID, NETWORK = '-', BRIDGE = '-', IP = '-', MAC = '-', PCI_ID = '' }) => ( +

+ {`${NIC_ID} | ${NETWORK} | ${BRIDGE} | ${IP} | ${MAC} | ${PCI_ID}`} +

+ ))} +
+
+
+

VM ALIAS

+ {alias.map(({ NIC_ID, NETWORK = '-', BRIDGE = '-', IP = '-', MAC = '-' }) => ( +

+ {`${NIC_ID} | ${NETWORK} | ${BRIDGE} | ${IP} | ${MAC}`} +

+ ))} +
+
+ ) +} + +VmNetworkTab.displayName = 'VmNetworkTab' + +export default VmNetworkTab diff --git a/src/fireedge/src/client/components/Tabs/Vm/placement.js b/src/fireedge/src/client/components/Tabs/Vm/placement.js new file mode 100644 index 0000000000..205a7f9b6e --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/placement.js @@ -0,0 +1,13 @@ +import * as React from 'react' + +const VmPlacementTab = data => { + return ( +
+

WIP - PLACEMENT

+
+ ) +} + +VmPlacementTab.displayName = 'VmPlacementTab' + +export default VmPlacementTab diff --git a/src/fireedge/src/client/components/Tabs/Vm/schedActions.js b/src/fireedge/src/client/components/Tabs/Vm/schedActions.js new file mode 100644 index 0000000000..04ac3ea0fe --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/schedActions.js @@ -0,0 +1,13 @@ +import * as React from 'react' + +const VmSchedulingTab = data => { + return ( +
+

WIP - SCHED ACTIONS

+
+ ) +} + +VmSchedulingTab.displayName = 'VmSchedulingTab' + +export default VmSchedulingTab diff --git a/src/fireedge/src/client/components/Tabs/Vm/snapshot.js b/src/fireedge/src/client/components/Tabs/Vm/snapshot.js new file mode 100644 index 0000000000..6c7913ae8f --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/snapshot.js @@ -0,0 +1,13 @@ +import * as React from 'react' + +const VmSnapshotTab = data => { + return ( +
+

WIP - SNAPSHOT

+
+ ) +} + +VmSnapshotTab.displayName = 'VmSnapshotTab' + +export default VmSnapshotTab diff --git a/src/fireedge/src/client/components/Tabs/Vm/storage.js b/src/fireedge/src/client/components/Tabs/Vm/storage.js new file mode 100644 index 0000000000..61eb84a91a --- /dev/null +++ b/src/fireedge/src/client/components/Tabs/Vm/storage.js @@ -0,0 +1,55 @@ +import * as React from 'react' + +import * as VirtualMachine from 'client/models/VirtualMachine' +import { prettyBytes } from 'client/utils' + +const VmStorageTab = data => { + const disks = VirtualMachine.getDisks(data) + + return ( +
+

VM DISKS

+ {disks.map(({ + DISK_ID, + DATASTORE = '-', + TARGET = '-', + IMAGE, + TYPE, + FORMAT, + SIZE, + MONITOR_SIZE, + READONLY, + SAVE = 'No', + CLONE + }) => { + const size = +SIZE ? prettyBytes(+SIZE, 'MB') : '-' + const monitorSize = +MONITOR_SIZE ? prettyBytes(+MONITOR_SIZE, 'MB') : '-' + + const type = String(TYPE).toLowerCase() + + const image = IMAGE ?? ({ + fs: `${FORMAT} - ${size}`, + swap: size + }[type]) + + return ( +

+ {`${DISK_ID} | + ${DATASTORE} | + ${TARGET} | + ${image} | + ${monitorSize}/${size} | + ${type} | + ${READONLY} | + ${SAVE} | + ${CLONE}`} +

+ ) + })} +
+ ) +} + +VmStorageTab.displayName = 'VmStorageTab' + +export default VmStorageTab diff --git a/src/fireedge/src/client/components/Tabs/index.js b/src/fireedge/src/client/components/Tabs/index.js index 5d6f3d01b7..43987152ce 100644 --- a/src/fireedge/src/client/components/Tabs/index.js +++ b/src/fireedge/src/client/components/Tabs/index.js @@ -36,7 +36,7 @@ const Tabs = ({ tabs = [], renderHiddenTabs = false }) => { /> )} - ), [tabSelected]) + ), [tabs.length, tabSelected]) const renderAllHiddenTabContents = useMemo(() => tabs.map((tabProps, idx) => { diff --git a/src/fireedge/src/client/containers/Dashboard/Sunstone/index.js b/src/fireedge/src/client/containers/Dashboard/Sunstone/index.js index 4000f4bb30..d2dbdd880f 100644 --- a/src/fireedge/src/client/containers/Dashboard/Sunstone/index.js +++ b/src/fireedge/src/client/containers/Dashboard/Sunstone/index.js @@ -5,7 +5,7 @@ import { Container, Box, Grid } from '@material-ui/core' import { useAuth } from 'client/features/Auth' import { useFetchAll } from 'client/hooks' -import { useVmApi, useUserApi, useImageApi, useVNetworkApi } from 'client/features/One' +import { useUserApi, useImageApi, useVNetworkApi } from 'client/features/One' import * as Widgets from 'client/components/Widgets' import dashboardStyles from 'client/containers/Dashboard/Provision/styles' @@ -14,7 +14,6 @@ function Dashboard () { const { status, fetchRequestAll, STATUS } = useFetchAll() const { INIT, PENDING } = STATUS - const { getVms } = useVmApi() const { getUsers } = useUserApi() const { getImages } = useImageApi() const { getVNetworks } = useVNetworkApi() @@ -26,7 +25,6 @@ function Dashboard () { React.useEffect(() => { fetchRequestAll([ - getVms(), getUsers(), getImages(), getVNetworks()