Skip to content

Commit

Permalink
add users list to vm details page
Browse files Browse the repository at this point in the history
  • Loading branch information
yaacov committed Jun 9, 2020
1 parent 5124c9f commit c67dc41
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import * as React from 'react';
import AlertsBody from '@console/shared/src/components/dashboard/status-card/AlertsBody';
import { StatusItem } from '@console/shared/src/components/dashboard/status-card/AlertItem';
import { BlueInfoCircleIcon } from '@console/shared/src/components/status';
import { NO_GUEST_AGENT_MESSAGE } from '../../../constants/vm/constants';
import { VMIKind } from '../../../types';
import { getVMIConditionsByType } from '../../../selectors/vmi';

// Based on: https://github.com/kubevirt/kubevirt/blob/f71e9c9615a6c36178169d66814586a93ba515b5/staging/src/kubevirt.io/client-go/api/v1/types.go#L337
const VMI_CONDITION_AGENT_CONNECTED = 'AgentConnected';

const isGuestAgentInstalled = (vmi: VMIKind) => {
export const isGuestAgentInstalled = (vmi: VMIKind) => {
// the condition type is unique
const conditions = getVMIConditionsByType(vmi, VMI_CONDITION_AGENT_CONNECTED);
return conditions && conditions.length > 0 && conditions[0].status === 'True';
Expand All @@ -17,10 +18,7 @@ const isGuestAgentInstalled = (vmi: VMIKind) => {
export const VMAlerts: React.FC<VMAlertsProps> = ({ vmi }) => (
<AlertsBody>
{vmi && vmi.status && !isGuestAgentInstalled(vmi) && (
<StatusItem
Icon={BlueInfoCircleIcon}
message="This VM does not have guest agent installed. Some metrics and management features will not be available."
/>
<StatusItem Icon={BlueInfoCircleIcon} message={NO_GUEST_AGENT_MESSAGE} />
)}
</AlertsBody>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getLoadedData, getResource } from '../../utils';
import { VirtualMachineInstanceModel, VirtualMachineModel } from '../../models';
import { getServicesForVmi } from '../../selectors/service';
import { VMResourceSummary, VMDetailsList, VMSchedulingList } from './vm-resource';
import { VMUsersList } from './vm-users';
import { VMTabProps } from './types';
import { getVMStatus } from '../../statuses/vm/vm-status';
import { VMStatusBundle } from '../../statuses/vm/types';
Expand Down Expand Up @@ -115,6 +116,10 @@ export const VMDetails: React.FC<VMDetailsProps> = (props) => {
<SectionHeading text="Services" />
<ServicesList {...restProps} data={vmServicesData} label="Services" />
</div>
<div className="co-m-pane__body">
<SectionHeading text="Logged in users" />
<VMUsersList {...restProps} vmi={vmi} vmStatusBundle={vmStatusBundle} />
</div>
</StatusBox>
);
};
Expand Down
152 changes: 152 additions & 0 deletions frontend/packages/kubevirt-plugin/src/components/vms/vm-users.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { watchURL, stopWatchURL } from '@console/internal/actions/dashboards';
import { RESULTS_TYPE } from '@console/internal/reducers/dashboards';
import { RootState } from '@console/internal/redux';
import * as classNames from 'classnames';
import { sortable } from '@patternfly/react-table';
import { fromNow } from '@console/internal/components/utils/datetime';
import { Table, TableRow, TableData } from '@console/internal/components/factory';
import { Timestamp } from '@console/internal/components/utils';
import { StatusItem } from '@console/shared/src/components/dashboard/status-card/AlertItem';
import { BlueInfoCircleIcon } from '@console/shared/src/components/status';
import { getName, getNamespace } from '@console/shared/src/selectors/common';
import {
VIRTUAL_MACHINE_IS_NOT_RUNNING,
NO_GUEST_AGENT_MESSAGE,
} from '../../constants/vm/constants';
import { VMStatus } from '../../constants/vm/vm-status';
import { isGuestAgentInstalled } from '../dashboards-page/vm-dashboard/vm-alerts';
import { VMStatusBundle } from '../../statuses/vm/types';
import { VMIKind } from '../../types';

const guestAgentURL = (name: string, namespace: string) =>
`apis/subresources.kubevirt.io/v1alpha3/namespaces/${namespace}/virtualmachineinstances/${name}/guestosinfo`;

const tableColumnClasses = [
classNames('col-lg-3', 'col-md-3', 'col-sm-4', 'col-sm-4'),
classNames('col-lg-3', 'col-md-3', 'col-sm-4', 'col-sm-4'),
classNames('col-lg-3', 'col-md-3', 'col-sm-4', 'col-sm-4'),
classNames('col-lg-3', 'col-md-3', 'hidden-sm', 'hidden-xs'),
];

const UsersTableHeader = () => {
return [
{
title: 'User Name',
sortField: 'metadata.userName',
transforms: [sortable],
props: { className: tableColumnClasses[0] },
},
{
title: 'Domain',
sortField: 'metadata.domain',
transforms: [sortable],
props: { className: tableColumnClasses[1] },
},
{
title: 'Login time',
sortField: 'metadata.loginTime',
transforms: [sortable],
props: { className: tableColumnClasses[2] },
},
{
title: 'Elapsed logged in time',
props: { className: tableColumnClasses[3] },
},
];
};
UsersTableHeader.displayName = 'UsersTableHeader';

const UsersTableRow = ({ obj: user, index, key, style }) => {
return (
<TableRow id={user?.metadata?.uid} index={index} trKey={key} style={style}>
<TableData className={tableColumnClasses[0]}>{user?.metadata?.userName}</TableData>
<TableData className={classNames(tableColumnClasses[1], 'co-break-word')}>
{user?.metadata?.domain}
</TableData>
<TableData className={tableColumnClasses[2]}>
<Timestamp timestamp={new Date(user?.metadata?.loginTime).toUTCString()} />
</TableData>
<TableData className={tableColumnClasses[3]}>{fromNow(user?.metadata?.loginTime)}</TableData>
</TableRow>
);
};

export const VMUsersListWrapper: React.FC<VMUsersListProps> = ({
watchURLAction,
stopWatchURLAction,
urlResults,
vmi,
vmStatusBundle,
}) => {
if (vmStatusBundle.status !== VMStatus.RUNNING) {
return <div className="text-center">{VIRTUAL_MACHINE_IS_NOT_RUNNING}</div>;
}

if (!isGuestAgentInstalled(vmi)) {
return <StatusItem Icon={BlueInfoCircleIcon} message={NO_GUEST_AGENT_MESSAGE} />;
}

const name = getName(vmi);
const namespace = getNamespace(vmi);
const url = guestAgentURL(name, namespace);

const usersResult = urlResults.getIn([url, 'data']);
const usersResultError = urlResults.getIn([url, 'loadError']);

const data =
usersResult &&
usersResult?.userList &&
usersResult?.userList.map((user, uid) => ({
metadata: {
uid,
userName: user?.userName,
domain: user?.domain,
loginTime: user?.loginTime && user.loginTime * 1000,
},
}));

// eslint-disable-next-line react-hooks/rules-of-hooks
React.useEffect(() => {
watchURLAction(url);
return () => {
stopWatchURLAction(url);
};
}, [watchURLAction, stopWatchURLAction, url]);

return (
<Table
aria-label="Users"
Header={UsersTableHeader}
Row={UsersTableRow}
data={data}
loadError={usersResultError?.message}
loaded={!!usersResult && !usersResultError}
EmptyMsg={() => <div className="text-center">No Logged In Users</div>}
virtualize
/>
);
};

type WatchURL = (url: string) => void;
type StopWatchURL = (url: string) => void;

type VMUsersListProps = {
vmi?: VMIKind;
vmStatusBundle?: VMStatusBundle;
urlResults: any;
watchURLAction: WatchURL;
stopWatchURLAction: StopWatchURL;
};

const stateToProps = (state: RootState) => ({
urlResults: state.dashboards.get(RESULTS_TYPE.URL),
});

const dispatchToProps = (dispatch: any) => ({
watchURLAction: (url) => dispatch(watchURL(url)),
stopWatchURLAction: (url) => dispatch(stopWatchURL(url)),
});

export const VMUsersList = connect(stateToProps, dispatchToProps)(VMUsersListWrapper);
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ export const VM_DETAIL_EVENTS_HREF = 'events';

export const PAUSED_VM_MODAL_MESSAGE =
'This VM has been paused. If you wish to unpause it, please click the Unpause button below. For further details, please check with your system administrator.';

export const VIRTUAL_MACHINE_IS_NOT_RUNNING = 'Virtual Machine is not running';
export const NO_GUEST_AGENT_MESSAGE =
'This VM does not have guest agent installed. Some metrics and management features will not be available';

0 comments on commit c67dc41

Please sign in to comment.