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

Add host status column #1774

Merged
merged 1 commit into from
Jul 1, 2019
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
59 changes: 59 additions & 0 deletions frontend/packages/metal3-plugin/src/components/host-status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from 'react';
import { Button } from 'patternfly-react';

import { K8sResourceKind } from '@console/internal/module/k8s';
import { StatusIconAndText } from '@console/internal/components/utils/status-icon';
import { RequireCreatePermission } from '@console/internal/components/utils';
import { getHostStatus } from '../utils/host-status';

import {
HOST_STATUS_DISCOVERED,
HOST_PROGRESS_STATES,
HOST_ERROR_STATES,
HOST_SUCCESS_STATES,
} from '../constants';
import { BaremetalHostModel } from '../models';

// TODO(jtomasek): Update this with onClick handler once add discovered host functionality
// is available
export const AddDiscoveredHostButton: React.FC<{ host: K8sResourceKind }> = (
{ host }, // eslint-disable-line @typescript-eslint/no-unused-vars
) => {
const {
metadata: { namespace },
} = host;

return (
<RequireCreatePermission model={BaremetalHostModel} namespace={namespace}>
<Button bsStyle="link">
<StatusIconAndText status="Add host" iconName="add-circle-o" />
</Button>
</RequireCreatePermission>
);
};

type BaremetalHostStatusProps = {
host: K8sResourceKind;
machine?: K8sResourceKind;
node?: K8sResourceKind;
};

const BaremetalHostStatus = ({ host }: BaremetalHostStatusProps) => {
const hostStatus = getHostStatus(host);
const { status, title } = hostStatus;

switch (true) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clever, but is this really better than...?

if (status === HOST_STATUS_DISCOVERED) {
  return <AddDiscoveredHostButton host={host} />;
}
if (HOST_PROGRESS_STATES.includes(status)) {
  return <StatusIconAndText status={title} iconName="refresh" />;
}
// ...

Copy link
Author

@jtomasek jtomasek Jun 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong opinion, but I find the switch statement a bit more readable

case status === HOST_STATUS_DISCOVERED:
return <AddDiscoveredHostButton host={host} />;
case HOST_PROGRESS_STATES.includes(status):
return <StatusIconAndText status={title} iconName="refresh" />;
case HOST_SUCCESS_STATES.includes(status):
return <StatusIconAndText status={title} iconName="ok" />;
case HOST_ERROR_STATES.includes(status):
return <StatusIconAndText status={title} iconName="error-circle-o" />;
default:
return <StatusIconAndText status={title} iconName="unknown" />;
}
};

export default BaremetalHostStatus;
26 changes: 14 additions & 12 deletions frontend/packages/metal3-plugin/src/components/host.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import { BaremetalHostModel } from '../models';
import { getHostBMCAddress, getHostMachine } from '../selectors';
import { BaremetalHostRole } from './host-role';
import MachineCell from './machine-cell';
import BaremetalHostStatus from './host-status';

const tableColumnClasses = [
classNames('col-lg-5', 'col-md-8', 'col-sm-12', 'col-xs-12'),
classNames('col-lg-3', 'col-md-4', 'col-sm-12', 'col-xs-12'),
classNames('col-lg-2', 'col-md-4', 'col-sm-6', 'hidden-xs'),
classNames('col-lg-3', 'col-md-4', 'hidden-sm', 'hidden-xs'),
classNames('col-lg-2', 'hidden-md', 'hidden-sm', 'hidden-xs'),
Expand All @@ -36,6 +37,13 @@ const HostsTableHeader = () => [
transforms: [sortable],
props: { className: tableColumnClasses[0] },
},
{
title: 'Status',
// TODO(jtomasek): enable this once it is possible to pass sort function
// sortFunc: getSimpleHostStatus,
// transforms: [sortable],
props: { className: tableColumnClasses[1] },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice if this is sortable, but I don't think there's a way to add sortFunc from a plugin yet.

},
{
title: 'Machine',
sortField: 'spec.machineRef.name',
Expand Down Expand Up @@ -72,7 +80,7 @@ const HostsTableRow: React.FC<HostsTableRowProps> = ({ obj: host, index, key, st
const namespace = getNamespace(host);
// const machineName = getHostMachineName(host);
const address = getHostBMCAddress(host);
const { machine } = host;
const { machine, node } = host;

// TODO(jtomasek): other resource references will be updated as a subsequent change
// const machineResource = {
Expand Down Expand Up @@ -102,9 +110,9 @@ const HostsTableRow: React.FC<HostsTableRowProps> = ({ obj: host, index, key, st
namespace={namespace}
/>
</TableData>
{/* <TableData className={tableColumnClasses[0]}>
<BaremetalHostStatus host={host} />
</TableData> */}
<TableData className={tableColumnClasses[1]}>
<BaremetalHostStatus host={host} machine={machine} node={node} />
</TableData>
<TableData className={tableColumnClasses[2]}>
<MachineCell host={host} />
</TableData>
Expand All @@ -120,13 +128,7 @@ const HostsTableRow: React.FC<HostsTableRowProps> = ({ obj: host, index, key, st
};

const HostList: React.FC<React.ComponentProps<typeof Table>> = (props) => (
<Table
{...props}
aria-label="Baremetal Hosts"
Header={HostsTableHeader}
Row={HostsTableRow}
virtualize
/>
<Table {...props} aria-label="Baremetal Hosts" Header={HostsTableHeader} Row={HostsTableRow} />
);

// TODO(jtomasek): re-enable filters once the extension point for list.tsx is in place
Expand Down
36 changes: 18 additions & 18 deletions frontend/packages/metal3-plugin/src/components/machine-cell.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import * as React from 'react';
// import { Link } from 'react-router-dom';
// import { Icon } from 'patternfly-react';
import { Link } from 'react-router-dom';

import { DASH } from '@console/shared';
import { MachineModel } from '@console/internal/models';
import { ResourceLink /* , RequireCreatePermission */ } from '@console/internal/components/utils';
import {
ResourceLink,
RequireCreatePermission,
StatusIconAndText,
} from '@console/internal/components/utils';
import { referenceForModel, K8sResourceKind } from '@console/internal/module/k8s';

import { getHostMachineName } from '../selectors';
import { canHostAddMachine } from '../utils/host-status';

interface MachineCellProps {
host: K8sResourceKind;
Expand All @@ -30,21 +34,17 @@ const MachineCell: React.FC<MachineCellProps> = ({ host }) => {
/>
);
}
// TODO(jtomasek): Re-enable this once host status is added
// if (canHostAddMachine(host)) {
// const ns = namespace || 'default';
// const href = `/k8s/ns/${ns}/${referenceForModel(MachineModel)}/~new`;
// return (
// <RequireCreatePermission model={MachineModel} namespace={ns}>
// <Link to={href}>
// <span className="co-icon-and-text">
// <Icon type="pf" name="add-circle-o" className="co-icon-and-text__icon" />
// Add machine
// </span>
// </Link>
// </RequireCreatePermission>
// );
// }
if (canHostAddMachine(host)) {
const ns = namespace || 'default';
const href = `/k8s/ns/${ns}/${referenceForModel(MachineModel)}/~new`;
return (
<RequireCreatePermission model={MachineModel} namespace={ns}>
<Link to={href}>
<StatusIconAndText status="Add machine" iconName="add-circle-o" />
</Link>
</RequireCreatePermission>
);
}
return <>{DASH}</>;
};

Expand Down
74 changes: 74 additions & 0 deletions frontend/packages/metal3-plugin/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
export const HOST_STATUS_READY = 'ready';
export const HOST_STATUS_DISCOVERED = 'discovered';
export const HOST_STATUS_OK = 'OK';
export const HOST_STATUS_EXTERNALLY_PROVISIONED = 'externally provisioned';
export const HOST_STATUS_PROVISIONED = 'provisioned';
export const HOST_STATUS_DEPROVISIONED = 'deprovisioned';
export const HOST_STATUS_REGISTERING = 'registering';
export const HOST_STATUS_INSPECTING = 'inspecting';
export const HOST_STATUS_PREPARING_TO_PROVISION = 'preparing to provision';
export const HOST_STATUS_PROVISIONING = 'provisioning';
export const HOST_STATUS_DEPROVISIONING = 'deprovisioning';
export const HOST_STATUS_MAKING_HOST_AVAILABLE = 'making host available';
export const HOST_STATUS_MATCH_PROFILE = 'match profile';
export const HOST_STATUS_STARTING_MAINTENANCE = 'starting maintenance';
export const HOST_STATUS_STOPPING_MAINTENANCE = 'stopping maintenance';
export const HOST_STATUS_MAINTENANCE = 'maintenance';
export const HOST_STATUS_VALIDATION_ERROR = 'validation error';
export const HOST_STATUS_REGISTRATION_ERROR = 'registration error';
export const HOST_STATUS_PROVISIONING_ERROR = 'provisioning error';
export const HOST_STATUS_POWER_MANAGEMENT_ERROR = 'power management error';

export const HOST_STATUS_TITLES = {
[HOST_STATUS_READY]: 'Ready',
[HOST_STATUS_DISCOVERED]: 'Discovered',
[HOST_STATUS_OK]: 'OK',
[HOST_STATUS_PROVISIONED]: 'Provisioned',
[HOST_STATUS_EXTERNALLY_PROVISIONED]: 'Externally provisioned',
[HOST_STATUS_DEPROVISIONED]: 'Deprovisioned',
[HOST_STATUS_REGISTERING]: 'Registering',
[HOST_STATUS_INSPECTING]: 'Inspecting',
[HOST_STATUS_PREPARING_TO_PROVISION]: 'Preparing to provision',
[HOST_STATUS_PROVISIONING]: 'Provisioning',
[HOST_STATUS_DEPROVISIONING]: 'Deprovisioning',
[HOST_STATUS_MAKING_HOST_AVAILABLE]: 'Making host available',
[HOST_STATUS_VALIDATION_ERROR]: 'Validation Error(s)',
[HOST_STATUS_REGISTRATION_ERROR]: 'Registration Error',
[HOST_STATUS_PROVISIONING_ERROR]: 'Provisioning Error',
[HOST_STATUS_POWER_MANAGEMENT_ERROR]: 'Power Management Error',
[HOST_STATUS_STARTING_MAINTENANCE]: 'Starting maintenance',
[HOST_STATUS_STOPPING_MAINTENANCE]: 'Stopping maintenance',
[HOST_STATUS_MAINTENANCE]: 'Maintenance',
[HOST_STATUS_MATCH_PROFILE]: 'Matching profile',
};

export const HOST_ERROR_STATES = [
HOST_STATUS_REGISTRATION_ERROR,
HOST_STATUS_PROVISIONING_ERROR,
HOST_STATUS_VALIDATION_ERROR,
HOST_STATUS_POWER_MANAGEMENT_ERROR,
];

export const HOST_WARN_STATES = [];

export const HOST_PROGRESS_STATES = [
HOST_STATUS_INSPECTING,
HOST_STATUS_PREPARING_TO_PROVISION,
HOST_STATUS_PREPARING_TO_PROVISION,
HOST_STATUS_PROVISIONING,
HOST_STATUS_DEPROVISIONING,
HOST_STATUS_MAKING_HOST_AVAILABLE,
HOST_STATUS_REGISTERING,
HOST_STATUS_STARTING_MAINTENANCE,
HOST_STATUS_STOPPING_MAINTENANCE,
HOST_STATUS_MATCH_PROFILE,
];

export const HOST_SUCCESS_STATES = [
HOST_STATUS_READY,
HOST_STATUS_DISCOVERED,
HOST_STATUS_OK,
HOST_STATUS_PROVISIONED,
HOST_STATUS_EXTERNALLY_PROVISIONED,
HOST_STATUS_DEPROVISIONED,
];
36 changes: 21 additions & 15 deletions frontend/packages/metal3-plugin/src/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,25 @@ import * as _ from 'lodash';
import { K8sResourceKind, MachineKind } from '@console/internal/module/k8s';
import { getName } from '@console/shared';

export const getOperationalStatus = (host) => _.get(host, 'status.operationalStatus');
export const getProvisioningState = (host) => _.get(host, 'status.provisioning.state');
export const getHostMachineName = (host) => _.get(host, 'spec.machineRef.name');
export const getHostBMCAddress = (host) => _.get(host, 'spec.bmc.address');
export const isHostOnline = (host) => _.get(host, 'spec.online', false);
export const getHostNICs = (host) => _.get(host, 'status.hardware.nics', []);
export const getHostStorage = (host) => _.get(host, 'status.hardware.storage', []);
export const getHostCPU = (host) => _.get(host, 'status.hardware.cpu', {});
export const getHostRAM = (host) => _.get(host, 'status.hardware.ramGiB');
export const getHostErrorMessage = (host) => _.get(host, 'status.errorMessage');
export const getHostDescription = (host) => _.get(host, 'spec.description', '');
export const isHostPoweredOn = (host) => _.get(host, 'status.poweredOn', false);
export const getHostTotalStorageCapacity = (host) =>
_.reduce(getHostStorage(host), (sum, disk) => sum + disk.sizeGiB, 0);
type BaremetalHostDisk = {
sizeGiB: number;
};

export const getHostOperationalStatus = (host: K8sResourceKind) =>
_.get(host, 'status.operationalStatus');
export const getHostProvisioningState = (host: K8sResourceKind) =>
_.get(host, 'status.provisioning.state');
export const getHostMachineName = (host: K8sResourceKind) => _.get(host, 'spec.machineRef.name');
export const getHostBMCAddress = (host: K8sResourceKind) => _.get(host, 'spec.bmc.address');
export const isHostOnline = (host: K8sResourceKind) => _.get(host, 'spec.online', false);
export const getHostNICs = (host: K8sResourceKind) => _.get(host, 'status.hardware.nics', []);
export const getHostStorage = (host: K8sResourceKind) => _.get(host, 'status.hardware.storage', []);
export const getHostCPU = (host: K8sResourceKind) => _.get(host, 'status.hardware.cpu', {});
export const getHostRAM = (host: K8sResourceKind) => _.get(host, 'status.hardware.ramGiB');
export const getHostErrorMessage = (host: K8sResourceKind) => _.get(host, 'status.errorMessage');
export const getHostDescription = (host: K8sResourceKind) => _.get(host, 'spec.description', '');
export const isHostPoweredOn = (host: K8sResourceKind) => _.get(host, 'status.poweredOn', false);
export const getHostTotalStorageCapacity = (host: K8sResourceKind) =>
_.reduce(getHostStorage(host), (sum: number, disk: BaremetalHostDisk) => sum + disk.sizeGiB, 0);
export const getHostMachine = (host: K8sResourceKind, machines: MachineKind[]) =>
machines.find((machine) => getHostMachineName(host) === getName(machine));
machines.find((machine: MachineKind) => getHostMachineName(host) === getName(machine));
59 changes: 59 additions & 0 deletions frontend/packages/metal3-plugin/src/utils/host-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { K8sResourceKind } from '@console/internal/module/k8s';

import {
getHostOperationalStatus,
getHostProvisioningState,
getHostErrorMessage,
// isNodeUnschedulable,
} from '../selectors';

import {
HOST_STATUS_TITLES,
HOST_STATUS_READY,
// HOST_STATUS_STARTING_MAINTENANCE,
} from '../constants';

// import { NOT_HANDLED } from '../common';

// const isStartingMaintenance = (node) => {
// if (isNodeUnschedulable(node)) {
// return {
// status: HOST_STATUS_STARTING_MAINTENANCE,
// text: HOST_STATUS_TITLES[HOST_STATUS_STARTING_MAINTENANCE],
// };
// }
// return NOT_HANDLED;
// };

const getBaremetalHostStatus = (host: K8sResourceKind) => {
const operationalStatus = getHostOperationalStatus(host);
const provisioningState = getHostProvisioningState(host);

const hostStatus = provisioningState || operationalStatus || undefined;
return {
status: hostStatus,
title: HOST_STATUS_TITLES[hostStatus] || hostStatus,
errorMessage: getHostErrorMessage(host),
};
};

export const getHostStatus = (
host: K8sResourceKind,
// machine?: MachineKind,
// node?: NodeKind,
) => {
// TODO(jtomasek): make this more robust by including node/machine status
// return isStartingMaintenance(node) || getBaremetalHostStatus(host);
return getBaremetalHostStatus(host);
};

export const getSimpleHostStatus = (
host: K8sResourceKind,
// machine?: MachineKind,
// node?: NodeKind,
): string => getHostStatus(host).status;

export const canHostAddMachine = (host: K8sResourceKind): boolean =>
[HOST_STATUS_READY].includes(getSimpleHostStatus(host));
// export const canHostStartMaintenance = (hostNode: NodeKind) => hostNode && !isNodeUnschedulable(hostNode);
// export const canHostStopMaintenance = (hostNode: NodeKind) => isNodeUnschedulable(hostNode);