Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 2099556: Adding the option to enable Rdp service in one click to windows vm #832

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion locales/en/plugin__kubevirt-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"<0>List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.</0><1><0>Template</0><1>metadata</1><2>ownerReferences</2></1>": "<0>List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.</0><1><0>Template</0><1>metadata</1><2>ownerReferences</2></1>",
"<0>Store the key in a project secret.</0><1>The key will be stored after the machine is created</1>": "<0>Store the key in a project secret.</0><1>The key will be stored after the machine is created</1>",
"<0>The descheduler can be used to evict a running pod to allow the pod to be rescheduled onto a more suitable node.</0><1></1><2>Note: if VirtualMachine have LiveMigration=False condition, edit is disabled.</2>": "<0>The descheduler can be used to evict a running pod to allow the pod to be rescheduled onto a more suitable node.</0><1></1><2>Note: if VirtualMachine have LiveMigration=False condition, edit is disabled.</2>",
"<0>This is a Windows VirtualMachine but no Service for the RDP (Remote Desktop Protocol) can be found.</0><1></1><2>For better experience accessing Windows console, it is recommended to use the RDP. To do so, create a service:<1><0>exposing the <2>{DEFAULT_RDP_PORT}/tcp</2> port of the VirtualMachine</0><1>using selector: <2>{TEMPLATE_VM_NAME_LABEL}: {name}</2></1><2>Example: virtctl expose VirtualMachine {name} --name {name}-rdp --port [UNIQUE_PORT] --target-port {DEFAULT_RDP_PORT} --type NodePort</2></1>Make sure, the VirtualMachine object has <3>spec.template.metadata.labels</3> set to <6>{TEMPLATE_VM_NAME_LABEL}: {name}</6></2>": "<0>This is a Windows VirtualMachine but no Service for the RDP (Remote Desktop Protocol) can be found.</0><1></1><2>For better experience accessing Windows console, it is recommended to use the RDP. To do so, create a service:<1><0>exposing the <2>{DEFAULT_RDP_PORT}/tcp</2> port of the VirtualMachine</0><1>using selector: <2>{TEMPLATE_VM_NAME_LABEL}: {name}</2></1><2>Example: virtctl expose VirtualMachine {name} --name {name}-rdp --port [UNIQUE_PORT] --target-port {DEFAULT_RDP_PORT} --type NodePort</2></1>Make sure, the VirtualMachine object has <3>spec.template.metadata.labels</3> set to <6>{TEMPLATE_VM_NAME_LABEL}: {name}</6></2>",
"<0>This is a Windows VirtualMachine but no Service for the RDP (Remote Desktop Protocol) can be found.</0><1></1><2>For better experience accessing Windows console, it is recommended to use the RDP.<1>Create RDP Service</1></2>": "<0>This is a Windows VirtualMachine but no Service for the RDP (Remote Desktop Protocol) can be found.</0><1></1><2>For better experience accessing Windows console, it is recommended to use the RDP.<1>Create RDP Service</1></2>",
"<0>Unattend.xml</0><1>Unattend can be used to configure windows setup and can be picked up several times during windows setup/configuration.</1><2><0>{t('Learn more')}</0></2>": "<0>Unattend.xml</0><1>Unattend can be used to configure windows setup and can be picked up several times during windows setup/configuration.</1><2><0>{t('Learn more')}</0></2>",
"3 (default)": "3 (default)",
"5 min": "5 min",
Expand Down Expand Up @@ -366,6 +366,7 @@
"Example: For Windows, get a link to the ": "Example: For Windows, get a link to the ",
"Example: your company name": "Example: your company name",
"Explore {{kind}} list": "Explore {{kind}} list",
"Expose RDP Service": "Expose RDP Service",
"Expose SSH access": "Expose SSH access",
"Feature highlights": "Feature highlights",
"Fedora cloud image list ": "Fedora cloud image list ",
Expand Down Expand Up @@ -646,6 +647,8 @@
"Quick Starts": "Quick Starts",
"RDP Address": "RDP Address",
"RDP Port": "RDP Port",
"RDP Service": "RDP Service",
"RDP Service is using a node port. Node port requires additional port resources.": "RDP Service is using a node port. Node port requires additional port resources.",
"Read about the latest information and key virtualization features on the Virtualization highlights.": "Read about the latest information and key virtualization features on the Virtualization highlights.",
"Read more": "Read more",
"Read only (ROX)": "Read only (ROX)",
Expand Down
12 changes: 9 additions & 3 deletions src/utils/components/Charts/ComponentReady/ComponentReady.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ import React from 'react';
import MutedTextSpan from '@kubevirt-utils/components/MutedTextSpan/MutedTextSpan';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { Bullseye } from '@patternfly/react-core';
import Loading from '@kubevirt-utils/components/Loading/Loading';

type ComponentReadyProps = React.PropsWithChildren<{ isReady: boolean; text?: string }>;
type ComponentReadyProps = React.PropsWithChildren<{
isReady: boolean;
text?: string;
spinner?: boolean;
}>;

const ComponentReady: React.FC<ComponentReadyProps> = ({ isReady, children, text }) => {
const ComponentReady: React.FC<ComponentReadyProps> = ({ isReady, children, text, spinner }) => {
const { t } = useKubevirtTranslation();
return isReady ? (
<>{children}</>
) : (
<Bullseye className="ComponentReady">
<MutedTextSpan text={text || t('No data available')} />
{(!spinner || text) && <MutedTextSpan text={text || t('No data available')} />}
{spinner && <Loading />}
</Bullseye>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import ComponentReady from '@kubevirt-utils/components/Charts/ComponentReady/ComponentReady';
import React from 'react';

import { RDPConnectorProps } from '../utils/types';

import RDP from './RDP';
import RdpServiceNotConfigured from './RdpServiceNotConfigured';
import RDPServiceNotConfigured from './RDPServiceNotConfigured';

const RDPConnector: React.FC<RDPConnectorProps> = ({ rdpServiceAddressPort, vm }) => {
return rdpServiceAddressPort ? (
<RDP rdp={rdpServiceAddressPort} />
) : (
<RdpServiceNotConfigured vm={vm} />
const RDPConnector: React.FC<RDPConnectorProps> = ({
rdpServiceAddressPort,
vm,
vmi,
isLoading,
}) => {
return (
<ComponentReady isReady={!isLoading} spinner>
{rdpServiceAddressPort ? (
<RDP rdp={rdpServiceAddressPort} />
) : (
<RDPServiceNotConfigured vm={vm} vmi={vmi} />
)}
</ComponentReady>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as React from 'react';

import { V1VirtualMachine, V1VirtualMachineInstance } from '@kubevirt-ui/kubevirt-api/kubevirt';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { Alert, Checkbox, ModalVariant, Stack, StackItem } from '@patternfly/react-core';
import TabModal from '@kubevirt-utils/components/TabModal/TabModal';
import ExternalLink from '@kubevirt-utils/components/ExternalLink/ExternalLink';
import { NODE_PORTS_LINK } from '../utils/constants';
import { createRDPService } from '../utils/utils';

type RDPServiceModalProps = {
vmi: V1VirtualMachineInstance;
vm: V1VirtualMachine;
isOpen: boolean;
onClose: () => void;
};

const RDPServiceModal: React.FC<RDPServiceModalProps> = ({ vmi, vm, isOpen, onClose }) => {
const { t } = useKubevirtTranslation();
const [isChecked, setChecked] = React.useState<boolean>(false);

return (
<TabModal
isOpen={isOpen}
isDisabled={!isChecked}
onClose={onClose}
onSubmit={() => createRDPService(vm, vmi)}
headerText={t('RDP Service')}
modalVariant={ModalVariant.medium}
>
<Stack hasGutter>
<StackItem>
<Checkbox
id="rdp-service-checkbox"
className="kv-rdp-service-checkbox--main"
label={t('Expose RDP Service')}
isChecked={isChecked}
data-checked-state={isChecked}
onChange={setChecked}
/>
</StackItem>
<StackItem>
<Alert variant="info" isInline title={t('Node port')}>
<div>
{t('RDP Service is using a node port. Node port requires additional port resources.')}
<div>
<ExternalLink text={t('Learn more')} href={NODE_PORTS_LINK} />
</div>
</div>
</Alert>
</StackItem>
</Stack>
</TabModal>
);
};

export default RDPServiceModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { Trans } from 'react-i18next';

import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { RDPServiceNotConfiguredProps } from '../utils/types';
import { Button, ButtonVariant } from '@patternfly/react-core';
import RDPServiceModal from './RDPServiceModal';
import { useModal } from '@kubevirt-utils/components/ModalProvider/ModalProvider';
import './rdp-service.scss';
const RDPServiceNotConfigured: React.FC<RDPServiceNotConfiguredProps> = ({ vm, vmi }) => {
const { t } = useKubevirtTranslation();
const { createModal } = useModal();
return (
<Trans t={t} ns="plugin__kubevirt-plugin">
<span>
This is a Windows VirtualMachine but no Service for the RDP (Remote Desktop Protocol) can be
found.
</span>
<br />
<span>
For better experience accessing Windows console, it is recommended to use the RDP.
<Button
className="kv-create-rdp-service-button"
variant={ButtonVariant.secondary}
onClick={() => createModal((props) => <RDPServiceModal vm={vm} vmi={vmi} {...props} />)}
>
Create RDP Service
</Button>
</span>
</Trans>
);
};

export default RDPServiceNotConfigured;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.kv-create-rdp-service-button {
display: block;
margin-top: var(--pf-global--spacer--md)
;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';

import { modelToGroupVersionKind, PodModel, ServiceModel } from '@kubevirt-ui/kubevirt-api/console';
import { IoK8sApiCoreV1Pod, IoK8sApiCoreV1Service } from '@kubevirt-ui/kubevirt-api/kubernetes';
Expand All @@ -15,19 +15,19 @@ import { getDefaultNetwork, getRdpAddressPort, getVmRdpNetworks } from './utils/
const DesktopViewer: React.FC<DesktopViewerProps> = ({ vm, vmi }) => {
const { t } = useKubevirtTranslation();
const [isDropdownOpen, setIsDropdownOpen] = React.useState<boolean>(false);
const [pods] = useK8sWatchResource<IoK8sApiCoreV1Pod[]>({
const [pods, podsLoaded] = useK8sWatchResource<IoK8sApiCoreV1Pod[]>({
groupVersionKind: modelToGroupVersionKind(PodModel),
isList: true,
namespace: vm.metadata.namespace,
});

const vmPod = getVMIPod(vmi, pods);
const vmPod = useMemo(() => getVMIPod(vmi, pods), [vmi, pods]);

const [services] = useK8sWatchResource<IoK8sApiCoreV1Service[]>({
const [services, servicesLoaded] = useK8sWatchResource<IoK8sApiCoreV1Service[]>({
groupVersionKind: modelToGroupVersionKind(ServiceModel),
isList: true,
namespace: vm?.metadata?.namespace,
});

const rdpServiceAddressPort = getRdpAddressPort(vmi, services, vmPod);
const networks = getVmRdpNetworks(vm, vmi);
const [selectedNetwork, setSelectedNetwork] = React.useState<Network>(
Expand Down Expand Up @@ -72,7 +72,12 @@ const DesktopViewer: React.FC<DesktopViewerProps> = ({ vm, vmi }) => {
</FormGroup>
</Form>
{networkType === 'POD' && (
<RDPConnector rdpServiceAddressPort={rdpServiceAddressPort} vm={vm} />
<RDPConnector
rdpServiceAddressPort={rdpServiceAddressPort}
isLoading={!podsLoaded || !servicesLoaded}
vm={vm}
vmi={vmi}
/>
)}
{networkType === 'MULTUS' && <MultusNetwork vmi={vmi} selectedNetwork={selectedNetwork} />}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ export const DEFAULT_VV_FILENAME = 'console.vv';
export const DEFAULT_VV_MIMETYPE = 'application/x-virt-viewer';
export const DEFAULT_RDP_FILENAME = 'console.rdp';
export const DEFAULT_RDP_MIMETYPE = 'application/rdp';
export const VMI_LABEL_AS_RDP_SERVICE_SELECTOR = 'vm.kubevirt.io/name';
export const NODE_PORTS_LINK =
'https://access.redhat.com/documentation/en-us/openshift_container_platform/4.10/html/networking/configuring-ingress-cluster-traffic#nw-using-nodeport_configuring-ingress-cluster-traffic-nodeport';
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export type RDPProps = ConnectWithRemoteViewerProps & {
textMoreRDPInfo?: string;
/** The information content appearing above the description list for guidelines to install virt-viewer */
textMoreRDPInfoContent?: string | React.ReactNode;
isLoading?: boolean;
};

export type ManualConnectionProps = React.HTMLProps<HTMLDivElement> & {
Expand Down Expand Up @@ -157,8 +158,9 @@ export type DesktopViewerProps = {
type: string;
};

export type RdpServiceNotConfiguredProps = {
export type RDPServiceNotConfiguredProps = {
vm: V1VirtualMachine;
vmi: V1VirtualMachineInstance;
};

export type Network = {
Expand All @@ -170,6 +172,8 @@ export type Network = {
export type RDPConnectorProps = {
rdpServiceAddressPort: ConsoleDetailPropType;
vm: V1VirtualMachine;
vmi: V1VirtualMachineInstance;
isLoading: boolean;
};

export type MultusNetworkProps = {
Expand Down
Loading