diff --git a/packages/eslint-plugin/cspell.wordlist.txt b/packages/eslint-plugin/cspell.wordlist.txt index 202a10c58..7b60f32fc 100644 --- a/packages/eslint-plugin/cspell.wordlist.txt +++ b/packages/eslint-plugin/cspell.wordlist.txt @@ -64,3 +64,5 @@ autoattach virt multiqueue filesystems +bootloader +typeahead diff --git a/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json b/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json index fa14e2cb5..a66320a29 100644 --- a/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json +++ b/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json @@ -69,6 +69,7 @@ "Data centers": "Data centers", "Data stores": "Data stores", "Dates are compared in UTC. End of the interval is included.": "Dates are compared in UTC. End of the interval is included.", + "Dedicated CPU": "Dedicated CPU", "Default": "Default", "Default Transfer Network": "Default Transfer Network", "Defines the CPU limits allocated to the main container in the controller pod. The default value is 500 milliCPU.": "Defines the CPU limits allocated to the main container in the controller pod. The default value is 500 milliCPU.", @@ -122,8 +123,10 @@ "Error: VDDK init image must be valid.": "Error: VDDK init image must be valid.", "Failed": "Failed", "False": "False", + "Features": "Features", "Filter by cluster": "Filter by cluster", "Filter by endpoint": "Filter by endpoint", + "Filter by features": "Filter by features", "Filter by flavor": "Filter by flavor", "Filter by host": "Filter by host", "Filter by image": "Filter by image", @@ -137,6 +140,7 @@ "Filter by tenant": "Filter by tenant", "Flavor": "Flavor", "From": "From", + "GPUs/Host Devices": "GPUs/Host Devices", "Hide values": "Hide values", "Hooks for virtualization": "Hooks for virtualization", "Host": "Host", @@ -225,6 +229,7 @@ "No StorageMaps found in namespace <1>{namespace}.": "No StorageMaps found in namespace <1>{namespace}.", "No StorageMaps found.": "No StorageMaps found.", "Not Ready": "Not Ready", + "NUMA": "NUMA", "Number of cluster in provider": "Number of cluster in provider", "Number of data centers in provider": "Number of data centers in provider", "Number of data stores in provider": "Number of data stores in provider", @@ -264,6 +269,7 @@ "Owner": "Owner", "Password": "Password", "Path": "Path", + "Persistent TPM/EFI": "Persistent TPM/EFI", "Plans": "Plans", "Plans for virtualization": "Plans for virtualization", "Please choose a NetworkAttachmentDefinition for default data transfer.": "Please choose a NetworkAttachmentDefinition for default data transfer.", diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/OpenShiftVirtualMachinesList.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/OpenShiftVirtualMachinesList.tsx index a3fb05daa..ccacf9a0b 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/OpenShiftVirtualMachinesList.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/OpenShiftVirtualMachinesList.tsx @@ -2,10 +2,11 @@ import React from 'react'; import { EnumToTuple, ResourceFieldFactory } from '@kubev2v/common'; +import { toVmFeatureEnum } from './utils/helpers/toVmFeatureEnum'; import { ProviderVirtualMachinesList, VmData } from './components'; import { OpenShiftVirtualMachinesRow } from './OpenShiftVirtualMachinesRow'; import { ProviderVirtualMachinesProps } from './ProviderVirtualMachines'; -import { getVmPowerState } from './utils'; +import { getOpenShiftFeatureMap, getVmPowerState } from './utils'; const openShiftVmFieldsMetadataFactory: ResourceFieldFactory = (t) => [ { @@ -33,6 +34,19 @@ const openShiftVmFieldsMetadataFactory: ResourceFieldFactory = (t) => [ }, sortable: true, }, + { + resourceFieldId: 'features', + jsonPath: (data: VmData) => getOpenShiftFeatureMap(data?.vm), + label: t('Features'), + isVisible: true, + isIdentity: false, + filter: { + type: 'features', + placeholderLabel: t('Filter by features'), + values: EnumToTuple(toVmFeatureEnum(t)), + }, + sortable: true, + }, { resourceFieldId: 'template', jsonPath: "$.vm.object.metadata.labels['vm.kubevirt.io/template']", diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/OpenShiftVirtualMachinesRow.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/OpenShiftVirtualMachinesRow.tsx index 68583bbd9..1c0583392 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/OpenShiftVirtualMachinesRow.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/OpenShiftVirtualMachinesRow.tsx @@ -6,13 +6,14 @@ import { Td, Tr } from '@patternfly/react-table'; import { PowerStateCellRenderer } from './components/PowerStateCellRenderer'; import { VmResourceLinkRenderer } from './components/VmResourceLinkRenderer'; -import { VMCellProps, VmData } from './components'; +import { VMCellProps, VmData, VmFeaturesCell } from './components'; import { getVmTemplate } from './utils'; const cellRenderers: Record> = { name: VmResourceLinkRenderer, status: PowerStateCellRenderer, template: ({ data }) => {getVmTemplate(data?.vm)}, + features: VmFeaturesCell, }; const renderTd = ({ resourceData, resourceFieldId, resourceFields }: RenderTdProps) => { diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/ProviderVirtualMachinesList.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/ProviderVirtualMachinesList.tsx index 463859e10..44954c681 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/ProviderVirtualMachinesList.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/ProviderVirtualMachinesList.tsx @@ -5,6 +5,7 @@ import { ProviderData } from 'src/modules/Providers/utils'; import { useForkliftTranslation } from 'src/utils/i18n'; import { + EnumFilter, loadUserSettings, ResourceFieldFactory, RowProps, @@ -53,8 +54,9 @@ export const ProviderVirtualMachinesList: React.FC ); }; @@ -65,3 +67,8 @@ const concernsMatcher: ValueMatcher = { Array.isArray(concerns) && concerns.some(({ category, label }) => category === filter || label === filter), }; + +const featuresMatcher: ValueMatcher = { + filterType: 'features', + matchValue: (features: { [key: string]: boolean }) => (filter: string) => !!features?.[filter], +}; diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/VmFeaturesCell.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/VmFeaturesCell.tsx new file mode 100644 index 000000000..5d917af2b --- /dev/null +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/VmFeaturesCell.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { TableCell } from 'src/modules/Providers/utils'; +import { useForkliftTranslation } from 'src/utils/i18n'; + +import { Label } from '@patternfly/react-core'; + +import { getOpenShiftFeatureMap } from '../utils'; +import { toVmFeatureEnum } from '../utils/helpers/toVmFeatureEnum'; + +import { VMCellProps } from './VMCellProps'; + +export const VmFeaturesCell: React.FC = ({ data }) => { + const { t } = useForkliftTranslation(); + const featureToLabel = toVmFeatureEnum(t); + return ( + + {Object.entries(getOpenShiftFeatureMap(data?.vm)) + .filter(([, value]) => value) + .map(([key]) => ( + + ))} + + ); +}; diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/index.ts b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/index.ts index 7ff5972ec..5e23e1172 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/index.ts +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/index.ts @@ -3,5 +3,6 @@ export * from './PowerStateCellRenderer'; export * from './ProviderVirtualMachinesList'; export * from './VMCellProps'; export * from './VMConcernsCellRenderer'; +export * from './VmFeaturesCell'; export * from './VMNameCellRenderer'; // @endindex diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/getOpenShiftFeatureMap.ts b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/getOpenShiftFeatureMap.ts new file mode 100644 index 000000000..4f8afa7c2 --- /dev/null +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/getOpenShiftFeatureMap.ts @@ -0,0 +1,21 @@ +import { VmFeatures } from 'src/utils/types'; + +import { ProviderVirtualMachine, V1DomainSpec } from '@kubev2v/types'; + +export const getOpenShiftFeatureMap = (vm: ProviderVirtualMachine): VmFeatures => { + if (vm.providerType !== 'openshift') { + return {}; + } + const domain: V1DomainSpec = vm.object?.spec?.template?.spec?.domain; + if (!domain) { + return {}; + } + + return { + numa: !!domain.cpu?.numa, + gpusHostDevices: !!domain.devices?.gpus?.length || !!domain?.devices?.hostDevices?.length, + persistentTpmEfi: + !!domain?.devices?.tpm?.persistent || domain?.firmware?.bootloader?.efi?.persistent, + dedicatedCpu: !!domain?.cpu?.dedicatedCpuPlacement, + }; +}; diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/index.ts b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/index.ts index aa2d06682..c97ba37f1 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/index.ts +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/index.ts @@ -1,4 +1,5 @@ // @index(['./*', /style/g], f => `export * from '${f.path}';`) +export * from './getOpenShiftFeatureMap'; export * from './getVmPowerState'; export * from './vmProps'; // @endindex diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/toVmFeatureEnum.ts b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/toVmFeatureEnum.ts new file mode 100644 index 000000000..ad15382cc --- /dev/null +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/utils/helpers/toVmFeatureEnum.ts @@ -0,0 +1,8 @@ +import { VmFeatures } from 'src/utils/types'; + +export const toVmFeatureEnum = (t: (string) => string): { [k in keyof VmFeatures]: string } => ({ + numa: t('NUMA'), + gpusHostDevices: t('GPUs/Host Devices'), + persistentTpmEfi: t('Persistent TPM/EFI'), + dedicatedCpu: t('Dedicated CPU'), +}); diff --git a/packages/forklift-console-plugin/src/utils/types.ts b/packages/forklift-console-plugin/src/utils/types.ts index 66250c60f..8ab406adc 100644 --- a/packages/forklift-console-plugin/src/utils/types.ts +++ b/packages/forklift-console-plugin/src/utils/types.ts @@ -48,3 +48,10 @@ export type ProviderStatus = (typeof ProviderStatusValues)[number]; export const MappingStatusValues = ['Ready', 'NotReady'] as const; export type MappingStatus = (typeof MappingStatusValues)[number]; + +export interface VmFeatures { + numa?: boolean; + gpusHostDevices?: boolean; + persistentTpmEfi?: boolean; + dedicatedCpu?: boolean; +} diff --git a/packages/mocks/src/definitions/basic/vms.mock.ts b/packages/mocks/src/definitions/basic/vms.mock.ts index b2b70acd1..b64402f1a 100644 --- a/packages/mocks/src/definitions/basic/vms.mock.ts +++ b/packages/mocks/src/definitions/basic/vms.mock.ts @@ -380,7 +380,7 @@ export const MOCK_OPENSHIFT_VMS: { [uid in OpenshiftProviderIDs]: OpenshiftVM[] name: 'rheltinyvm', namespace: NAMESPACE_FORKLIFT, selfLink: `providers/openshift/${OPENSHIFT_HOST_UID}/vms/3dcaf3ec-6b51-4ca0-8345-6d61841731d7`, - uid: '', + uid: '3dcaf3ec-6b51-4ca0-8345-6d61841731d7', version: '', object: { kind: 'VirtualMachine', @@ -407,6 +407,7 @@ export const MOCK_OPENSHIFT_VMS: { [uid in OpenshiftProviderIDs]: OpenshiftVM[] }, name: 'rheltinyvm', namespace: NAMESPACE_FORKLIFT, + uid: '3dcaf3ec-6b51-4ca0-8345-6d61841731d7', }, spec: { dataVolumeTemplates: [ @@ -495,5 +496,196 @@ export const MOCK_OPENSHIFT_VMS: { [uid in OpenshiftProviderIDs]: OpenshiftVM[] }, providerType: 'openshift', }, + { + name: 'fedora-rival-catshark', + namespace: NAMESPACE_FORKLIFT, + selfLink: `providers/openshift/${OPENSHIFT_HOST_UID}/vms/029b9890-259e-4ade-b22f-3991c3359062`, + uid: '029b9890-259e-4ade-b22f-3991c3359062', + version: '', + providerType: 'openshift', + object: { + apiVersion: 'kubevirt.io/v1', + kind: 'VirtualMachine', + metadata: { + annotations: { + 'kubemacpool.io/transaction-timestamp': '2023-10-01T11:11:12.736465708Z', + 'kubevirt.io/latest-observed-api-version': 'v1', + 'kubevirt.io/storage-observed-api-version': 'v1alpha3', + 'vm.kubevirt.io/validations': + '[\n {\n "name": "minimal-required-memory",\n "path": "jsonpath::.spec.domain.resources.requests.memory",\n "rule": "integer",\n "message": "This VM requires more memory.",\n "min": 1073741824\n }\n]\n', + }, + creationTimestamp: '2023-10-01T07:52:34Z', + finalizers: ['kubevirt.io/virtualMachineControllerFinalize'], + generation: 5, + labels: { + app: 'fedora-rival-catshark', + 'vm.kubevirt.io/template': 'fedora-server-small', + 'vm.kubevirt.io/template.namespace': 'openshift', + 'vm.kubevirt.io/template.revision': '1', + 'vm.kubevirt.io/template.version': 'v0.25.0', + }, + name: 'fedora-rival-catshark', + namespace: NAMESPACE_FORKLIFT, + resourceVersion: '2002703392', + uid: '029b9890-259e-4ade-b22f-3991c3359062', + }, + spec: { + dataVolumeTemplates: [ + { + apiVersion: 'cdi.kubevirt.io/v1beta1', + kind: 'DataVolume', + metadata: { + creationTimestamp: null, + name: 'fedora-rival-catshark', + }, + spec: { + sourceRef: { + kind: 'DataSource', + name: 'fedora', + namespace: 'openshift-virtualization-os-images', + }, + storage: { + resources: { + requests: { + storage: '30Gi', + }, + }, + }, + }, + }, + ], + running: false, + template: { + metadata: { + annotations: { + 'vm.kubevirt.io/flavor': 'small', + 'vm.kubevirt.io/os': 'fedora', + 'vm.kubevirt.io/workload': 'server', + }, + creationTimestamp: null, + labels: { + 'kubevirt.io/domain': 'fedora-rival-catshark', + 'kubevirt.io/size': 'small', + }, + }, + spec: { + domain: { + cpu: { + cores: 1, + sockets: 1, + threads: 1, + }, + devices: { + disks: [ + { + disk: { + bus: 'virtio', + }, + name: 'rootdisk', + }, + { + disk: { + bus: 'virtio', + }, + name: 'cloudinitdisk', + }, + ], + gpus: [ + { + deviceName: 'nvidia.com/GM204GL_Tesla_M60', + name: 'gpu1', + }, + ], + interfaces: [ + { + macAddress: '02:26:19:00:00:70', + masquerade: {}, + model: 'virtio', + name: 'default', + }, + ], + networkInterfaceMultiqueue: true, + rng: {}, + }, + features: { + acpi: {}, + smm: { + enabled: true, + }, + }, + firmware: { + bootloader: { + efi: {}, + }, + }, + machine: { + type: 'pc-q35-rhel9.2.0', + }, + resources: { + requests: { + memory: '2Gi', + }, + }, + }, + evictionStrategy: 'LiveMigrate', + networks: [ + { + name: 'default', + pod: {}, + }, + ], + terminationGracePeriodSeconds: 180, + volumes: [ + { + dataVolume: { + name: 'fedora-rival-catshark', + }, + name: 'rootdisk', + }, + { + cloudInitNoCloud: { + userData: + '#cloud-config\nuser: foo\npassword: bar\nchpasswd: { expire: False }', + }, + name: 'cloudinitdisk', + }, + ], + }, + }, + }, + status: { + conditions: [ + { + lastProbeTime: '2023-10-01T11:11:18Z', + lastTransitionTime: '2023-10-01T11:11:18Z', + message: 'VMI does not exist', + reason: 'VMINotExists', + status: 'False', + type: 'Ready', + }, + { + lastProbeTime: null, + lastTransitionTime: null, + message: 'VMI uses a PCI host devices', + reason: 'HostDeviceNotLiveMigratable', + status: 'False', + type: 'LiveMigratable', + }, + ], + printableStatus: 'Stopped', + volumeSnapshotStatuses: [ + { + enabled: true, + name: 'rootdisk', + }, + { + enabled: false, + name: 'cloudinitdisk', + reason: 'Snapshot is not supported for this volumeSource type [cloudinitdisk]', + }, + ], + }, + }, + }, ], }; diff --git a/packages/types/src/types/k8s/V1VirtualMachine.ts b/packages/types/src/types/k8s/V1VirtualMachine.ts index da0544bad..66e8b5233 100644 --- a/packages/types/src/types/k8s/V1VirtualMachine.ts +++ b/packages/types/src/types/k8s/V1VirtualMachine.ts @@ -28,7 +28,7 @@ export interface V1VirtualMachine { kind: 'DataVolume'; metadata: IoK8sApimachineryPkgApisMetaV1ObjectMeta; spec: { - pvc: { + pvc?: { accessModes: string[]; resources: { requests: { @@ -36,12 +36,24 @@ export interface V1VirtualMachine { }; }; }; - source: { + source?: { pvc: { name: string; namespace: string; }; }; + sourceRef?: { + kind: string; + name: string; + namespace: string; + }; + storage?: { + resources: { + requests: { + storage: string; + }; + }; + }; }; }[]; }; @@ -59,6 +71,15 @@ export interface V1VirtualMachine { printableStatus: VirtualMachinePrintableStatus; // Hold the state information of the VirtualMachine and its VirtualMachineInstance // Conditions []VirtualMachineCondition `json:"conditions,omitempty" optional:"true"` + conditions?: { + lastProbeTime: string; + lastTransitionTime: string; + message: string; + reason: string; + status: string; + type: string; + }[]; + // StateChangeRequests indicates a list of actions that should be taken on a VMI // e.g. stop a specific VMI then start a new one. // StateChangeRequests []VirtualMachineStateChangeRequest `json:"stateChangeRequests,omitempty" optional:"true"` @@ -70,6 +91,11 @@ export interface V1VirtualMachine { // VolumeSnapshotStatuses indicates a list of statuses whether snapshotting is // supported by each volume. // VolumeSnapshotStatuses []VolumeSnapshotStatus `json:"volumeSnapshotStatuses,omitempty" optional:"true"` + volumeSnapshotStatuses?: { + enabled: boolean; + name: string; + reason?: string; + }[]; }; } @@ -114,7 +140,7 @@ interface V1VirtualMachineInstanceSpec { // Specification of the desired behavior of the VirtualMachineInstance on the host. // Domain DomainSpec `json:"domain"` - domain: DomainSpec; + domain: V1DomainSpec; // NodeSelector is a selector which must be true for the vmi to fit on a node. // Selector which must match a node's labels for the vmi to be scheduled on that node. @@ -138,6 +164,7 @@ interface V1VirtualMachineInstanceSpec { // // +optional // EvictionStrategy *EvictionStrategy `json:"evictionStrategy,omitempty"` + evictionStrategy?: string; // StartStrategy can be set to "Paused" if Virtual Machine should be started in paused state. // @@ -161,12 +188,15 @@ interface V1VirtualMachineInstanceSpec { // VolumeSource represents the location and type of the mounted volume. // Defaults to Disk, if no type is specified. // VolumeSource `json:",inline"` + cloudInitNoCloud?: { + userData: string; + }; // DataVolume represents the dynamic creation a PVC for this volume as well as // the process of populating that PVC with a disk image. // +optional // DataVolume *DataVolumeSource `json:"dataVolume,omitempty"` - dataVolume: { + dataVolume?: { // Name represents the name of the DataVolume in the same namespace // Name string `json:"name"` name: string; @@ -235,7 +265,7 @@ interface V1VirtualMachineInstanceSpec { //AccessCredentials []AccessCredential `json:"accessCredentials,omitempty"` } -interface DomainSpec { +export interface V1DomainSpec { // Resources describes the Compute Resources required by this vmi. // Resources ResourceRequirements `json:"resources,omitempty"` resources?: { @@ -251,6 +281,8 @@ interface DomainSpec { cores: number; sockets: number; threads: number; + dedicatedCpuPlacement?: boolean; + numa?: unknown; }; // Memory allow specifying the VMI memory features. @@ -260,10 +292,20 @@ interface DomainSpec { // Machine type. // +optional // Machine *Machine `json:"machine,omitempty"` + machine?: { + type: string; + }; // Firmware. // +optional // Firmware *Firmware `json:"firmware,omitempty"` + firmware?: { + bootloader?: { + efi?: { + persistent?: boolean; + }; + }; + }; // Clock sets the clock and timers of the vmi. // +optional @@ -272,6 +314,10 @@ interface DomainSpec { // Features like acpi, apic, hyperv, smm. // +optional // Features *Features `json:"features,omitempty"` + features?: { + acpi?: unknown; + smm?: unknown; + }; // Devices allows adding disks, network interfaces, and others // Devices Devices `json:"devices"` @@ -314,6 +360,8 @@ interface Devices { interfaces: { name: string; masquerade?: object; + macAddress?: string; + model?: string; }[]; // Inputs describe input devices @@ -355,6 +403,7 @@ interface Devices { // +optional // +listType=atomic // GPUs []GPU `json:"gpus,omitempty"` + gpus?: unknown[]; // Filesystems describes filesystem which is connected to the vmi. // +optional @@ -365,4 +414,9 @@ interface Devices { // +optional // +listType=atomic // HostDevices []HostDevice `json:"hostDevices,omitempty"` + hostDevices?: unknown[]; + + tpm?: { + persistent?: boolean; + }; } diff --git a/packages/types/src/types/k8s/index.ts b/packages/types/src/types/k8s/index.ts index 5ccaae0b0..d7939b817 100644 --- a/packages/types/src/types/k8s/index.ts +++ b/packages/types/src/types/k8s/index.ts @@ -5,4 +5,5 @@ export * from './K8sResourceCondition'; export * from './V1Namespace'; export * from './V1NetworkAttachmentDefinition'; export * from './V1Secret'; +export * from './V1VirtualMachine'; // @endindex