diff --git a/frontend/packages/kubevirt-plugin/src/components/vms/types.ts b/frontend/packages/kubevirt-plugin/src/components/vms/types.ts new file mode 100644 index 00000000000..49e6cba4201 --- /dev/null +++ b/frontend/packages/kubevirt-plugin/src/components/vms/types.ts @@ -0,0 +1,60 @@ +import { K8sResourceKind, ObjectMetadata } from '@console/internal/module/k8s'; + +// https://kubevirt.io/api-reference/master/definitions.html#_v1_virtualmachineinstancespec +export type VMISpec = { + affinity: any; + dnsConfig: any; + dnsPolicy: string; + domain?: any; + evictionStrategy: any; + hostname: string; + livenessProbe: any; + networks?: any[]; + nodeSelector: any; + readinessProbe: any; + subdomain: string; + terminationGracePeriodSeconds: number; + tolerations: any[]; + volumes?: any[]; +}; + +// https://kubevirt.io/api-reference/master/definitions.html#_v1_virtualmachineinstancestatus +export type VMIStatus = { + conditions: any[]; + interfaces: any[]; + migrationMethod: string; + migrationState: any; + nodeName: string; + phase: string; + reason: string; +}; + +export type VMIKind = { + spec: VMISpec; + status: VMIStatus; +} & K8sResourceKind; + +export type VMITemplate = { + metadata?: ObjectMetadata; + spec?: VMISpec; +}; + +export type VMSpec = { + template: VMITemplate; + running?: boolean; + runStrategy?: any; + dataVolumeTemplates?: any[]; +}; + +export type VMStatus = { + conditions?: any[]; + created?: boolean; + ready?: boolean; + stateChangeRequests?: any[]; +}; + +// https://kubevirt.io/api-reference/master/definitions.html#_v1_virtualmachine +export type VMKind = { + spec: VMSpec; + status: VMStatus; +} & K8sResourceKind; diff --git a/frontend/packages/kubevirt-plugin/src/components/vms/vm-details-page.tsx b/frontend/packages/kubevirt-plugin/src/components/vms/vm-details-page.tsx new file mode 100644 index 00000000000..be61269f584 --- /dev/null +++ b/frontend/packages/kubevirt-plugin/src/components/vms/vm-details-page.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; + +import { navFactory } from '@console/internal/components/utils'; + +import { DetailsPage } from '@console/internal/components/factory'; +import { K8sResourceKindReference } from '@console/internal/module/k8s'; + +import { VMDetailsFirehose } from './vm-details'; + +// import { VmEvents } from './vm-events'; +// import VmConsolesConnected from '../vmconsoles'; +// import { Nic } from '../nic'; +// import { Disk } from '../disk'; +// import { menuActions } from './menu-actions'; + +export const VirtualMachinesDetailsPage = (props: VirtualMachinesDetailsPageProps) => { + /* TODO(mlibra): pages will be transferred one by one in follow-ups + const consolePage = { + href: 'consoles', + name: 'Consoles', + component: VmConsolesConnected, + }; + + const nicsPage = { + href: 'nics', + name: 'Network Interfaces', + component: VmNic, + }; + + const disksPage = { + href: 'disks', + name: 'Disks', + component: VmDisk, + }; + */ + + const pages = [ + navFactory.details(VMDetailsFirehose), + navFactory.editYaml(), + // consolePage, + // navFactory.events(VmEvents), + // nicsPage, + // disksPage, + ]; + + const menuActions = undefined; // TODO(mlibra): menuActions + + return ; +}; + +type VirtualMachinesDetailsPageProps = { + name: string; + namespace: string; + kind: K8sResourceKindReference; + match: any; +}; diff --git a/frontend/packages/kubevirt-plugin/src/components/vms/vm-details.tsx b/frontend/packages/kubevirt-plugin/src/components/vms/vm-details.tsx new file mode 100644 index 00000000000..2e8c87ee079 --- /dev/null +++ b/frontend/packages/kubevirt-plugin/src/components/vms/vm-details.tsx @@ -0,0 +1,86 @@ +import * as React from 'react'; +import * as _ from 'lodash'; + +import { getResource, getServicesForVm } from 'kubevirt-web-ui-components'; + +import { + Firehose, + StatusBox, + ScrollToTopOnMount, + SectionHeading, +} from '@console/internal/components/utils'; + +import { PodKind } from '@console/internal/module/k8s'; +import { PodModel, ServiceModel } from '@console/internal/models'; + +import { ServicesList } from '@console/internal/components/service'; +import { VMKind, VMIKind } from './types'; +import { VirtualMachineInstanceModel, VirtualMachineInstanceMigrationModel } from '../../models'; +import { VMResourceSummary, VMDetailsList } from './vm-resource'; + +export const VMDetailsFirehose = ({ obj: vm }: { obj: VMKind }) => { + const { name, namespace } = vm.metadata; + + const vmiRes = getResource(VirtualMachineInstanceModel, { + name, + namespace, + isList: false, + prop: 'vmi', + optional: true, + }); + + const resources = [ + vmiRes, + getResource(PodModel, { namespace, prop: 'pods' }), + getResource(VirtualMachineInstanceMigrationModel, { namespace, prop: 'migrations' }), + getResource(ServiceModel, { namespace, prop: 'services' }), + ]; + + return ( +
+ + + +
+ ); +}; + +const VMDetails = (props: VMDetailsProps) => { + const { vm, ...restProps } = props; + const flatResources = { + vm, + vmi: _.get(props, 'vmi.data'), + pods: _.get(props, 'pods.data'), + migrations: _.get(props, 'migrations.data'), + }; + + const vmServicesData = getServicesForVm(_.get(props, 'services', {}).data, vm); + + return ( + + +
+ +
+
+ +
+
+ +
+
+
+
+ + +
+
+ ); +}; + +type VMDetailsProps = { + vm: VMKind; + pods?: PodKind[]; + migrations?: any[]; + vmi?: VMIKind; +}; diff --git a/frontend/packages/kubevirt-plugin/src/components/vms/vm-resource.tsx b/frontend/packages/kubevirt-plugin/src/components/vms/vm-resource.tsx new file mode 100644 index 00000000000..02660e0d614 --- /dev/null +++ b/frontend/packages/kubevirt-plugin/src/components/vms/vm-resource.tsx @@ -0,0 +1,98 @@ +import * as React from 'react'; + +import { + getDescription, + getOperatingSystemName, + getOperatingSystem, + getVmStatus, + getVmiIpAddresses, + getWorkloadProfile, + getVmTemplate, + getTemplateDisplayName, + getNodeName, + getFlavor, + VmStatuses, + BootOrder, + isVmOff, + getBootableDevicesInOrder, +} from 'kubevirt-web-ui-components'; + +import { ResourceSummary, NodeLink, ResourceLink } from '@console/internal/components/utils'; +import { PodKind } from '@console/internal/module/k8s'; +import { getName, getNamespace, DASH } from '@console/shared'; +import { PodModel } from '@console/internal/models'; + +import { VMKind, VMIKind } from './types'; + +export const VMResourceSummary = ({ vm }: VMResourceSummaryProps) => { + const template = getVmTemplate(vm); + const templateLink = template && getTemplateDisplayName(template); // TODO(mlibra): link to a template detail, once implemented + + return ( + +
Description
+
{getDescription(vm)}
+
Operating System
+
{getOperatingSystemName(vm) || getOperatingSystem(vm) || DASH}
+
Template
+
{templateLink || DASH}
+
+ ); +}; + +export const VMDetailsList = ({ vm, vmi, pods, migrations }: VMResourceListProps) => { + const vmStatus = getVmStatus(vm, pods, migrations); + const { launcherPod } = vmStatus; + const sortedBootableDevices = getBootableDevicesInOrder(vm); + const nodeName = getNodeName(launcherPod); + const vmIsOff = isVmOff(vmStatus); + const ipAddresses = vmIsOff ? [] : getVmiIpAddresses(vmi); + + return ( +
+
Status
+
+ +
+
Pod
+
+ {launcherPod ? ( + + ) : ( + DASH + )} +
+
Boot Order
+
+ {sortedBootableDevices.length > 0 ? ( + + ) : ( + DASH + )} +
+
IP Address
+
{ipAddresses.length > 0 ? ipAddresses.join(', ') : DASH}
+
Node
+
{}
+
Flavour
+
{getFlavor(vm) || DASH}
+
Workload Profile
+
{getWorkloadProfile(vm) || DASH}
+
+ ); +}; + +type VMResourceSummaryProps = { + vm: VMKind; +}; + +type VMResourceListProps = { + vm: VMKind; + pods?: PodKind[]; + migrations?: any[]; + vmi?: VMIKind; +}; diff --git a/frontend/packages/kubevirt-plugin/src/components/vm.tsx b/frontend/packages/kubevirt-plugin/src/components/vms/vm.tsx similarity index 99% rename from frontend/packages/kubevirt-plugin/src/components/vm.tsx rename to frontend/packages/kubevirt-plugin/src/components/vms/vm.tsx index 1aeb6d84990..7c227e7968a 100644 --- a/frontend/packages/kubevirt-plugin/src/components/vm.tsx +++ b/frontend/packages/kubevirt-plugin/src/components/vms/vm.tsx @@ -26,7 +26,7 @@ import { VirtualMachineModel, // VirtualMachineInstanceModel, // VirtualMachineInstanceMigrationModel, -} from '../models'; +} from '../../models'; // import { openCreateVmWizard } from '../modals/create-vm-modal'; // import { menuActions } from './menu-actions'; diff --git a/frontend/packages/kubevirt-plugin/src/plugin.ts b/frontend/packages/kubevirt-plugin/src/plugin.ts index 2d47bea2663..baefbe7795d 100644 --- a/frontend/packages/kubevirt-plugin/src/plugin.ts +++ b/frontend/packages/kubevirt-plugin/src/plugin.ts @@ -12,6 +12,8 @@ import { import * as models from './models'; import { yamlTemplates } from './yaml-templates'; +import './style.scss'; + type ConsumedExtensions = | ResourceNSNavItem | ResourceListPage @@ -53,7 +55,7 @@ const plugin: Plugin = [ properties: { model: models.VirtualMachineModel, loader: () => - import('./components/vm' /* webpackChunkName: "kubevirt-virtual-machines" */).then( + import('./components/vms/vm' /* webpackChunkName: "kubevirt-virtual-machines" */).then( (m) => m.VirtualMachinesPage, ), }, @@ -65,13 +67,16 @@ const plugin: Plugin = [ template: yamlTemplates.getIn([models.VirtualMachineModel, 'default']), }, }, - // { - // type: 'Page/Resource/Details', - // properties: { - // model: VirtualMachineModel, - // loader: () => import('./components/vm-detail' /* webpackChunkName: "kubevirt-virtual-machines" */).then(m => m.VirtualMachinesDetailsPage), - // }, - // }, + { + type: 'Page/Resource/Details', + properties: { + model: models.VirtualMachineModel, + loader: () => + import( + './components/vms/vm-details-page' /* webpackChunkName: "kubevirt-virtual-machine-details" */ + ).then((m) => m.VirtualMachinesDetailsPage), + }, + }, ]; export default plugin; diff --git a/frontend/packages/kubevirt-plugin/src/style.scss b/frontend/packages/kubevirt-plugin/src/style.scss new file mode 100644 index 00000000000..559dc1db2f1 --- /dev/null +++ b/frontend/packages/kubevirt-plugin/src/style.scss @@ -0,0 +1 @@ +@import '~kubevirt-web-ui-components/dist/sass/components';