Skip to content

Commit

Permalink
Fixes 1837677: open the VNC console in a new window
Browse files Browse the repository at this point in the history
  • Loading branch information
Gilad Lekner committed Jun 4, 2020
1 parent 5d06977 commit 120a8c5
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 15 deletions.
@@ -0,0 +1,28 @@
import * as React from 'react';
import { connectToFlags, FlagsObject } from '@console/internal/reducers/features';
import ConnectedVMConsole from '@console/kubevirt-plugin/src/components/connected-vm-console/connected-vm-console';
import { ConsoleType } from '@console/kubevirt-plugin/src/constants/vm/console-type';
import { FLAG_KUBEVIRT } from '@console/kubevirt-plugin/src/plugin';

const CloudVMConsole: React.FC<CloudVMConsoleProps> = ({ location, flags }) => {
const params = new URLSearchParams(location.search);
const type = ConsoleType.fromString(params.get('console'));
const namespace = params.get('namespace');
const name = params.get('name');

return (
<ConnectedVMConsole
isKubevirt={flags.KUBEVIRT}
type={type || ConsoleType.VNC}
namespace={namespace}
name={name}
/>
);
};

type CloudVMConsoleProps = {
flags: FlagsObject;
location: Location;
};

export default connectToFlags(FLAG_KUBEVIRT)(CloudVMConsole);
@@ -0,0 +1,138 @@
import * as React from 'react';
import { K8sResourceKind, PodKind } from '@console/internal/module/k8s';
import { PodModel, ServiceModel } from '@console/internal/models';
import { getVMStatus } from '../../statuses/vm/vm-status';
import { VMImportKind } from '../../types/vm-import/ovirt/vm-import';
import { V1alpha1DataVolume } from '../../types/vm/disk/V1alpha1DataVolume';
import {
DataVolumeModel,
VirtualMachineImportModel,
VirtualMachineInstanceMigrationModel,
VirtualMachineInstanceModel,
VirtualMachineModel,
} from '../../models';
import { VMIKind, VMKind } from '../../types/vm';
import { Firehose, FirehoseResult } from '@console/internal/components/utils';
import { VMConsolesWrapper } from '../vms/vm-console';
import { getLoadedData } from '../../utils';
import { ConsoleEmptyState } from './vm-console-empty-state';
import { ConsoleType } from '../../constants/vm/console-type';

const CloudVMConsole: React.FC<CloudVMConsoleProps> = ({
type,
vm,
vmis,
pods,
migrations,
dataVolumes,
vmImports,
}) => {
const loadedVM = getLoadedData(vm);
const loadedVMIs = getLoadedData(vmis);
const loadedPods = getLoadedData(pods);
const loadedMigrations = getLoadedData(migrations);
const loadedDataVolumes = getLoadedData(dataVolumes);
const loadedImports = getLoadedData(vmImports);
const vmi = loadedVMIs?.[0];

const vmStatusBundle = getVMStatus({
vm: loadedVM,
vmi,
pods: loadedPods,
migrations: loadedMigrations,
dataVolumes: loadedDataVolumes,
vmImports: loadedImports,
});

return (
<VMConsolesWrapper
vm={loadedVM}
vmi={vmi}
vmStatusBundle={vmStatusBundle}
pods={loadedPods}
type={type}
showActions={false}
/>
);
};

type CloudVMConsoleProps = {
type: ConsoleType;
vm?: FirehoseResult<VMKind>;
vmis?: FirehoseResult<VMIKind[]>;
pods?: FirehoseResult<PodKind[]>;
migrations?: FirehoseResult<K8sResourceKind[]>;
dataVolumes?: FirehoseResult<V1alpha1DataVolume[]>;
vmImports?: FirehoseResult<VMImportKind[]>;
services?: FirehoseResult;
};

const ConnectedVMConsole: React.FC<ConnectedVMConsoleProps> = ({
type,
namespace,
name,
isKubevirt,
}) => {
const resources = [
{
kind: VirtualMachineModel.kind,
name,
namespace,
prop: 'vm',
},
{
kind: VirtualMachineInstanceModel.kind,
namespace,
isList: true,
prop: 'vmis',
optional: true,
fieldSelector: `metadata.name=${name}`,
},
{
kind: PodModel.kind,
namespace,
isList: true,
prop: 'pods',
},
{
kind: VirtualMachineInstanceMigrationModel.kind,
isList: true,
namespace,
prop: 'migrations',
},
{
kind: VirtualMachineImportModel.kind,
isList: true,
namespace,
prop: 'vmImports',
optional: true,
},
{
kind: DataVolumeModel.kind,
isList: true,
namespace,
prop: 'dataVolumes',
},
{ kind: ServiceModel.kind, namespace, prop: 'services' },
];
return isKubevirt && name && namespace ? (
<Firehose resources={resources}>
<CloudVMConsole type={type} />
</Firehose>
) : (
<ConsoleEmptyState
isKubevirt={isKubevirt}
isMissingName={!name}
isMissingNamespace={!namespace}
/>
);
};

type ConnectedVMConsoleProps = {
type: ConsoleType;
namespace: string;
name: string;
isKubevirt: boolean;
};

export default ConnectedVMConsole;
@@ -0,0 +1,4 @@
.kv-cloud-vm-console-empty {
display: flex;
justify-content: center;
}
@@ -0,0 +1,94 @@
import * as React from 'react';
import {
Title,
EmptyState,
EmptyStateIcon,
TextContent,
EmptyStateBody,
TextList,
TextListItem,
TextListVariants,
TextListItemVariants,
} from '@patternfly/react-core';
import { PlugIcon, ErrorCircleOIcon } from '@patternfly/react-icons';
import './vm-console-empty-state.scss';

export interface ConsoleEmptyStateProps {
isKubevirt: boolean;
isMissingName: boolean;
isMissingNamespace: boolean;
}

const Spinner = () => (
<span className="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
<span className="pf-c-spinner__clipper" />
<span className="pf-c-spinner__lead-ball" />
<span className="pf-c-spinner__tail-ball" />
</span>
);

export const ConsoleEmptyState: React.SFC<ConsoleEmptyStateProps> = ({
isKubevirt,
isMissingName,
isMissingNamespace,
}) => {
if (isMissingName || isMissingNamespace) {
return (
<div className="kv-cloud-vm-console-empty">
<EmptyState>
<EmptyStateIcon icon={ErrorCircleOIcon} />
<Title size="lg" headingLevel="h4">
Missing Required URL Parameters
</Title>
<EmptyStateBody>
<TextContent>
<TextList component={TextListVariants.dl}>
{isMissingName && (
<>
<TextListItem component={TextListItemVariants.dt}>name</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Name of the VM/VMI
</TextListItem>
</>
)}
{isMissingNamespace && (
<>
<TextListItem component={TextListItemVariants.dt}>namespace</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Namespace of the VM/VMI
</TextListItem>
</>
)}
</TextList>
</TextContent>
</EmptyStateBody>
</EmptyState>
</div>
);
}

return (
<div className="kv-cloud-vm-console-empty">
<EmptyState>
{isKubevirt === undefined ? (
<>
<EmptyStateIcon variant="container" component={Spinner} />
<Title size="lg" headingLevel="h4">
Loading
</Title>
</>
) : (
<>
<EmptyStateIcon icon={PlugIcon} />
<Title size="lg" headingLevel="h4">
Kubevirt Plugin was not found
</Title>
<EmptyStateBody>
Accessing the VNC Console is not possible. Please install Kubevirt
</EmptyStateBody>
</>
)}
</EmptyState>
</div>
);
};
3 changes: 3 additions & 0 deletions frontend/packages/kubevirt-plugin/src/components/vms/types.ts
Expand Up @@ -3,6 +3,7 @@ import { VMIKind, VMKind } from '../../types/vm';
import { VMGenericLikeEntityKind, VMILikeEntityKind } from '../../types/vmLike';
import { VMImportKind } from '../../types/vm-import/ovirt/vm-import';
import { V1alpha1DataVolume } from '../../types/vm/disk/V1alpha1DataVolume';
import { ConsoleType } from '../../constants/vm/console-type';

export type VMTabProps = {
obj?: VMILikeEntityKind;
Expand All @@ -16,6 +17,8 @@ export type VMTabProps = {
customData: {
kindObj: K8sKind;
};
type?: ConsoleType;
showActions?: boolean;
};

export type VMLikeEntityTabProps = {
Expand Down
@@ -0,0 +1,7 @@
.vm-console-actions {
padding: 0 30px;
}

.vm-console-bottom-gutter {
margin-bottom: var(--pf-global--spacer--md) !important;
}

0 comments on commit 120a8c5

Please sign in to comment.