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

Introduce Metal3 plugin for openshift console #1539

Merged
merged 1 commit into from
Jun 6, 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
1 change: 1 addition & 0 deletions frontend/packages/console-shared/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DASH = '-';
3 changes: 2 additions & 1 deletion frontend/packages/console-shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export default {};
export * from './selectors';
export * from './constants';
6 changes: 6 additions & 0 deletions frontend/packages/console-shared/src/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as _ from 'lodash-es';

import { K8sResourceKind } from '@console/internal/module/k8s';

export const getName = (value: K8sResourceKind): string => _.get(value, 'metadata.name');
export const getNamespace = (value: K8sResourceKind): string => _.get(value, 'metadata.namespace');
10 changes: 10 additions & 0 deletions frontend/packages/metal3-plugin/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
reviewers:
- jtomasek
- honza
- flofuchs
- knowncitizen
- dantrainor
approvers:
- jtomasek
- honza
- flofuchs
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: sources should have one trailing newline by default.

Copy link
Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Done

Looks like it's still missing the final newline

13 changes: 13 additions & 0 deletions frontend/packages/metal3-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@console/metal3-plugin",
"version": "0.0.0-fixed",
"description": "Metal Kubed - Bare Metal Host Provisioning for Kubernetes",
"private": true,
"dependencies": {
"@console/plugin-sdk": "0.0.0-fixed",
"@console/shared": "0.0.0-fixed"
},
"consolePlugin": {
"entry": "src/plugin.ts"
}
}
130 changes: 130 additions & 0 deletions frontend/packages/metal3-plugin/src/components/host.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as React from 'react';

import { getName, getNamespace } from '@console/shared';
// TODO(jtomasek): update import once models are moved to console-shared package
// import { MachineModel, NodeModel } from '@console/internal/models';
import { referenceForModel } from '@console/internal/module/k8s';

import {
ListHeader,
ColHead,
List,
ListPage,
ResourceRow,
} from '@console/internal/components/factory';

import { ResourceLink } from '@console/internal/components/utils';
// import { WithResources } from '@console/shared';

import { BaremetalHostModel } from '../models';
import { getHostBMCAddress } from '../selectors';
import MachineCell from './machine-cell';

// const nameColumnClasses = 'col-lg-2 col-md-4 col-sm-6 col-xs-6';
// const statusColumnClasses = 'col-lg-2 col-md-4 hidden-sm hidden-xs';
// const machineColumnClasses = 'col-lg-3 visible-lg';
// const roleColumnClasses = 'col-lg-2 visible-lg';
// const addressColumnClasses = 'col-lg-2 visible-lg';
const columnClasses = 'col-sm-4';

const HostHeader = (props: React.ComponentProps<typeof ColHead>) => (
<ListHeader>
Copy link
Member

Choose a reason for hiding this comment

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

Heads up that we're in the process of updating all tables to PF4 (#1465)

@priley86 FYI

<ColHead {...props} className={columnClasses} sortField="metadata.name">
Name
</ColHead>
{/* <ColHead {...props} className={statusColumnClasses}>
Status
</ColHead> */}
<ColHead {...props} className={columnClasses}>
Machine
</ColHead>
{/* <ColHead {...props} className={roleColumnClasses}>
Role
</ColHead> */}
<ColHead {...props} className={columnClasses} sortField="spec.bmc.address">
Management Address
</ColHead>
</ListHeader>
);

const HostRow = ({ obj: host }: React.ComponentProps<typeof ResourceRow>) => {
Copy link
Author

Choose a reason for hiding this comment

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

I had to switch from using React.FC as it did not satisfy react/prop-types lint rule.
(getting error 'obj' is missing in props validation react/prop-types)

const name = getName(host);
const namespace = getNamespace(host);
// const machineName = getHostMachineName(host);
const address = getHostBMCAddress(host);

// TODO(jtomasek): other resource references will be updated as a subsequent change
// const machineResource = {
// kind: referenceForModel(MachineModel),
// name: machineName,
// namespaced: true,
// namespace,
// isList: false,
// prop: 'machine',
// };

// const hostResourceMap = {
// machine: {
// resource: machineResource,
// },
// nodes: {
// resource: getResource(NodeModel, { namespaced: false }),
// },
// };

return (
<ResourceRow obj={host}>
<div className={columnClasses}>
<ResourceLink
kind={referenceForModel(BaremetalHostModel)}
name={name}
namespace={namespace}
/>
</div>
{/* <div className={statusColumnClasses}>
<WithResources resourceMap={machineName ? hostResourceMap : {}}>
<BaremetalHostStatus host={host} />
</WithResources>
</div> */}
<div className={columnClasses}>
<MachineCell host={host} />
</div>
{/* <div className={roleColumnClasses}>
<WithResources resourceMap={machineName ? hostResourceMap : {}}>
<BaremetalHostRole />
</WithResources>
</div> */}
<div className={columnClasses}>{address}</div>
</ResourceRow>
);
};

const HostList = (props: React.ComponentProps<typeof List>) => (
<List {...props} Header={HostHeader} Row={HostRow} />
);

// TODO(jtomasek): re-enable filters once the extension point for list.tsx is in place
const filters = [];
// const filters = [
// {
// type: 'baremetalhost-status',
// selected: ['online', 'offline'],
// reducer: getSimpleHostStatus,
// items: [{ id: 'online', title: 'online' }, { id: 'offline', title: 'offline' }],
// },
// ];

type BaremetalHostsPageProps = {
namespace: string;
};

export const BaremetalHostsPage = (props: BaremetalHostsPageProps) => (
<ListPage
{...props}
canCreate
rowFilters={filters}
createButtonText="Add Host"
kind={referenceForModel(BaremetalHostModel)}
ListComponent={HostList}
/>
);
51 changes: 51 additions & 0 deletions frontend/packages/metal3-plugin/src/components/machine-cell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from 'react';
// import { Link } from 'react-router-dom';
// import { Icon } from 'patternfly-react';

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

import { getHostMachineName } from '../selectors';

interface MachineCellProps {
host: K8sResourceKind;
}

const MachineCell = ({ host }: MachineCellProps) => {
const machineName = getHostMachineName(host);

const {
metadata: { namespace },
} = host;

if (machineName) {
return (
<ResourceLink
kind={referenceForModel(MachineModel)}
name={machineName}
namespace={namespace}
title={machineName}
/>
);
}
// 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>
// );
// }
return <>{DASH}</>;
};

export default MachineCell;
15 changes: 15 additions & 0 deletions frontend/packages/metal3-plugin/src/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { K8sKind } from '@console/internal/module/k8s';

export const BaremetalHostModel: K8sKind = {
Copy link
Contributor

Choose a reason for hiding this comment

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

See my comment on KubeVirt models - shouldn't we use crd: true here?

@spadgett In kubevirt/web-ui fork, any additional models are declared without the crd flag, which is a mistake IMO.

Copy link
Author

Choose a reason for hiding this comment

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

I agree, although when I add the crd flag, I am getting kindObj: no model for kind BareMetalHost in the console and the ListPage won't show up. It seems like there is a mismatch in keys of the resources in redux store and how crd resources are retrieved.

Copy link
Author

Choose a reason for hiding this comment

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

Fixed the problem and added crd: true flag to the model.

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like there is a mismatch in keys of the resources in redux store and how crd resources are retrieved.

There's currently a duality in how Console references models. Non-CRD models are typically referenced only by their kind, CRD models are referenced via referenceForModel function that takes their apiGroup, apiVersion and kind into account.

Getting kindObj: no model for kind BareMetalHost could mean you aren't referencing your CRD model via referenceForModel function.

label: 'Bare Metal Host',
labelPlural: 'Bare Metal Hosts',
apiVersion: 'v1alpha1',
path: 'baremetalhosts',
apiGroup: 'metalkube.org',
plural: 'baremetalhosts',
abbr: 'BMH',
namespaced: true,
kind: 'BareMetalHost',
id: 'baremetalhost',
crd: true,
};
53 changes: 53 additions & 0 deletions frontend/packages/metal3-plugin/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
Plugin,
ResourceNSNavItem,
ResourceListPage,
ModelFeatureFlag,
ModelDefinition,
} from '@console/plugin-sdk';

import { BaremetalHostModel } from './models';

type ConsumedExtensions = ResourceNSNavItem | ResourceListPage | ModelFeatureFlag | ModelDefinition;

const METAL3_FLAG = 'METAL3';

const plugin: Plugin<ConsumedExtensions> = [
{
type: 'ModelDefinition',
properties: {
models: [BaremetalHostModel],
Copy link
Contributor

Choose a reason for hiding this comment

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

You can also do

-import { BaremetalHostModel } from './models';
+import * as models from './models';
-models: [BaremetalHostModel],
+models: _.values(models),

like in #1592 to avoid manually importing individual models (if you expect to have lots of them).

Copy link
Author

Choose a reason for hiding this comment

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

I don't expect there to be many more models, so I listed it explicitly here

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, I'm fine with that.

},
},
{
type: 'FeatureFlag/Model',
properties: {
model: BaremetalHostModel,
flag: METAL3_FLAG,
},
},
{
type: 'NavItem/ResourceNS',
properties: {
section: 'Compute',
mergeAfter: 'Machine Autoscalers',
componentProps: {
name: 'Bare Metal Hosts',
resource: 'baremetalhosts',
required: METAL3_FLAG,
},
Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to put your nav link after an existing one, you can use mergeAfter.

Copy link
Author

Choose a reason for hiding this comment

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

Done

},
},
{
type: 'Page/Resource/List',
properties: {
model: BaremetalHostModel,
loader: () =>
import('./components/host' /* webpackChunkName: "metal3-baremetalhost" */).then(
(m) => m.BaremetalHostsPage,
),
},
},
];

export default plugin;
16 changes: 16 additions & 0 deletions frontend/packages/metal3-plugin/src/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as _ from 'lodash-es';

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 getHostCPUs = (host) => _.get(host, 'status.hardware.cpus', []);
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);