Show Node Introspection Data
* Fetches introspection data on expanding node * If introspection data are available, display them in NodeExtendedInfo Change-Id: I5feac2b64803e1cf2c31f0bbc259940a80ddae3c
This commit is contained in:
parent
7e65fba52f
commit
046188f13a
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Nodes List view now fetches and displays introspection data in expanded
|
||||||
|
Node view
|
@ -121,6 +121,12 @@ let createResolvingPromise = data => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let createRejectingPromise = error => {
|
||||||
|
return () => {
|
||||||
|
return when.reject(error);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
describe('Asynchronous Nodes Actions', () => {
|
describe('Asynchronous Nodes Actions', () => {
|
||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
spyOn(utils, 'getAuthTokenId').and.returnValue('mock-auth-token');
|
spyOn(utils, 'getAuthTokenId').and.returnValue('mock-auth-token');
|
||||||
@ -184,6 +190,72 @@ describe('Asynchronous Nodes Actions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Fetching Introspection data success', () => {
|
||||||
|
const response = { interfaces: { eth0: { mac: '00:00:00:00:00:11' } } };
|
||||||
|
const nodeId = '598612eb-f21b-435e-a868-7bb74e576cc2';
|
||||||
|
|
||||||
|
beforeEach(done => {
|
||||||
|
spyOn(utils, 'getAuthTokenId').and.returnValue('mock-auth-token');
|
||||||
|
spyOn(utils, 'getServiceUrl').and.returnValue('mock-url');
|
||||||
|
spyOn(NodesActions, 'fetchNodeIntrospectionDataSuccess');
|
||||||
|
spyOn(IronicInspectorApiService, 'getIntrospectionData').and.callFake(
|
||||||
|
createResolvingPromise(response)
|
||||||
|
);
|
||||||
|
|
||||||
|
NodesActions.fetchNodeIntrospectionData(nodeId)(() => {}, () => {});
|
||||||
|
setTimeout(() => {
|
||||||
|
done();
|
||||||
|
}, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches fetchNodeIntrospectionDataSuccess', () => {
|
||||||
|
expect(IronicInspectorApiService.getIntrospectionData).toHaveBeenCalledWith(
|
||||||
|
nodeId
|
||||||
|
);
|
||||||
|
expect(NodesActions.fetchNodeIntrospectionDataSuccess).toHaveBeenCalledWith(
|
||||||
|
nodeId,
|
||||||
|
response
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Fetching Introspection data error', () => {
|
||||||
|
const nodeId = '598612eb-f21b-435e-a868-7bb74e576cc2';
|
||||||
|
const message = 'Data for specified node not available';
|
||||||
|
const error = {
|
||||||
|
status: 404,
|
||||||
|
responseText: `{ "error": { "message": "${message}" } }`
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(done => {
|
||||||
|
spyOn(utils, 'getAuthTokenId').and.returnValue('mock-auth-token');
|
||||||
|
spyOn(utils, 'getServiceUrl').and.returnValue('mock-url');
|
||||||
|
spyOn(NodesActions, 'fetchNodeIntrospectionDataFailed');
|
||||||
|
spyOn(NotificationActions, 'notify');
|
||||||
|
spyOn(IronicInspectorApiService, 'getIntrospectionData').and.callFake(
|
||||||
|
createRejectingPromise(error)
|
||||||
|
);
|
||||||
|
|
||||||
|
NodesActions.fetchNodeIntrospectionData(nodeId)(() => {}, () => {});
|
||||||
|
setTimeout(() => {
|
||||||
|
done();
|
||||||
|
}, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches fetchNodeIntrospectionDataFailed', () => {
|
||||||
|
expect(IronicInspectorApiService.getIntrospectionData).toHaveBeenCalledWith(
|
||||||
|
nodeId
|
||||||
|
);
|
||||||
|
expect(NodesActions.fetchNodeIntrospectionDataFailed).toHaveBeenCalledWith(
|
||||||
|
nodeId
|
||||||
|
);
|
||||||
|
expect(NotificationActions.notify).toHaveBeenCalledWith({
|
||||||
|
title: 'Introspection data not found',
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Asynchronous Introspect Nodes Action', () => {
|
describe('Asynchronous Introspect Nodes Action', () => {
|
||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
spyOn(utils, 'getAuthTokenId').and.returnValue('mock-auth-token');
|
spyOn(utils, 'getAuthTokenId').and.returnValue('mock-auth-token');
|
||||||
|
@ -14,31 +14,25 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Map, Set } from 'immutable';
|
import { fromJS, Map, Set } from 'immutable';
|
||||||
|
|
||||||
import NodesConstants from '../../js/constants/NodesConstants';
|
import NodesConstants from '../../js/constants/NodesConstants';
|
||||||
import nodesReducer from '../../js/reducers/nodesReducer';
|
import nodesReducer from '../../js/reducers/nodesReducer';
|
||||||
|
import { NodesState } from '../../js/immutableRecords/nodes';
|
||||||
|
|
||||||
describe('nodesReducer', () => {
|
describe('nodesReducer', () => {
|
||||||
const initialState = Map({
|
const initialState = new NodesState({
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
nodesInProgress: Set(),
|
nodesInProgress: Set(),
|
||||||
allFilter: '',
|
all: Map(),
|
||||||
registeredFilter: '',
|
ports: Map(),
|
||||||
introspectedFilter: '',
|
introspectionStatuses: Map(),
|
||||||
deployedFilter: '',
|
introspectionData: Map()
|
||||||
maintenanceFilter: '',
|
|
||||||
all: Map()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const updatedState = Map({
|
const updatedState = new NodesState({
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
nodesInProgress: Set(),
|
nodesInProgress: Set(),
|
||||||
allFilter: '',
|
|
||||||
registeredFilter: '',
|
|
||||||
introspectedFilter: '',
|
|
||||||
deployedFilter: '',
|
|
||||||
maintenanceFilter: '',
|
|
||||||
all: Map({
|
all: Map({
|
||||||
uuid1: Map({
|
uuid1: Map({
|
||||||
uuid: 'uuid1'
|
uuid: 'uuid1'
|
||||||
@ -46,17 +40,15 @@ describe('nodesReducer', () => {
|
|||||||
uuid2: Map({
|
uuid2: Map({
|
||||||
uuid: 'uuid2'
|
uuid: 'uuid2'
|
||||||
})
|
})
|
||||||
})
|
}),
|
||||||
|
ports: Map(),
|
||||||
|
introspectionStatuses: Map(),
|
||||||
|
introspectionData: Map()
|
||||||
});
|
});
|
||||||
|
|
||||||
const updatedNodeState = Map({
|
const updatedNodeState = new NodesState({
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
nodesInProgress: Set(),
|
nodesInProgress: Set(),
|
||||||
allFilter: '',
|
|
||||||
registeredFilter: '',
|
|
||||||
introspectedFilter: '',
|
|
||||||
deployedFilter: '',
|
|
||||||
maintenanceFilter: '',
|
|
||||||
all: Map({
|
all: Map({
|
||||||
uuid1: Map({
|
uuid1: Map({
|
||||||
uuid: 'uuid1',
|
uuid: 'uuid1',
|
||||||
@ -67,7 +59,10 @@ describe('nodesReducer', () => {
|
|||||||
uuid2: Map({
|
uuid2: Map({
|
||||||
uuid: 'uuid2'
|
uuid: 'uuid2'
|
||||||
})
|
})
|
||||||
})
|
}),
|
||||||
|
ports: Map(),
|
||||||
|
introspectionStatuses: Map(),
|
||||||
|
introspectionData: Map()
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return initial state', () => {
|
it('should return initial state', () => {
|
||||||
@ -144,4 +139,27 @@ describe('nodesReducer', () => {
|
|||||||
const newState = nodesReducer(initialState, action);
|
const newState = nodesReducer(initialState, action);
|
||||||
expect(newState).toEqual(updatedState);
|
expect(newState).toEqual(updatedState);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle FETCH_NODE_INTROSPECTION_DATA_SUCCESS action', () => {
|
||||||
|
const action = {
|
||||||
|
type: NodesConstants.FETCH_NODE_INTROSPECTION_DATA_SUCCESS,
|
||||||
|
payload: {
|
||||||
|
nodeId: 'uuid1',
|
||||||
|
data: { interfaces: { eth0: { mac: '00:00:00:00:00:11' } } }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const newState = nodesReducer(updatedState, action);
|
||||||
|
expect(newState.introspectionData.get('uuid1')).toEqual(
|
||||||
|
fromJS(action.payload.data)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle FETCH_NODE_INTROSPECTION_DATA_FAILED action', () => {
|
||||||
|
const action = {
|
||||||
|
type: NodesConstants.FETCH_NODE_INTROSPECTION_DATA_FAILED,
|
||||||
|
payload: 'uuid1'
|
||||||
|
};
|
||||||
|
const newState = nodesReducer(updatedState, action);
|
||||||
|
expect(newState.introspectionData.get('uuid1')).toEqual(undefined);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -20,6 +20,8 @@ import when from 'when';
|
|||||||
|
|
||||||
import { getNodesByIds } from '../selectors/nodes';
|
import { getNodesByIds } from '../selectors/nodes';
|
||||||
import IronicApiErrorHandler from '../services/IronicApiErrorHandler';
|
import IronicApiErrorHandler from '../services/IronicApiErrorHandler';
|
||||||
|
import IronicInspectorApiErrorHandler
|
||||||
|
from '../services/IronicInspectorApiErrorHandler';
|
||||||
import IronicApiService from '../services/IronicApiService';
|
import IronicApiService from '../services/IronicApiService';
|
||||||
import IronicInspectorApiService from '../services/IronicInspectorApiService';
|
import IronicInspectorApiService from '../services/IronicInspectorApiService';
|
||||||
import MistralApiService from '../services/MistralApiService';
|
import MistralApiService from '../services/MistralApiService';
|
||||||
@ -119,6 +121,40 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
fetchNodeIntrospectionDataSuccess(nodeId, data) {
|
||||||
|
return {
|
||||||
|
type: NodesConstants.FETCH_NODE_INTROSPECTION_DATA_SUCCESS,
|
||||||
|
payload: { nodeId, data }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchNodeIntrospectionDataFailed(nodeId) {
|
||||||
|
return {
|
||||||
|
type: NodesConstants.FETCH_NODE_INTROSPECTION_DATA_FAILED,
|
||||||
|
payload: nodeId
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchNodeIntrospectionData(nodeId) {
|
||||||
|
return dispatch => {
|
||||||
|
IronicInspectorApiService.getIntrospectionData(nodeId)
|
||||||
|
.then(response => {
|
||||||
|
dispatch(this.fetchNodeIntrospectionDataSuccess(nodeId, response));
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
dispatch(this.fetchNodeIntrospectionDataFailed(nodeId));
|
||||||
|
logger.error(
|
||||||
|
'Error in NodesActions.fetchNodeIntrospectionData',
|
||||||
|
error.stack || error
|
||||||
|
);
|
||||||
|
let errorHandler = new IronicInspectorApiErrorHandler(error);
|
||||||
|
errorHandler.errors.forEach(error => {
|
||||||
|
dispatch(NotificationActions.notify(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Poll fetchNodes until no node is in progress
|
* Poll fetchNodes until no node is in progress
|
||||||
*/
|
*/
|
||||||
|
@ -69,6 +69,7 @@ class Nodes extends React.Component {
|
|||||||
? <NodesTableView />
|
? <NodesTableView />
|
||||||
: <NodesListForm>
|
: <NodesListForm>
|
||||||
<NodesListView
|
<NodesListView
|
||||||
|
fetchNodeIntrospectionData={this.props.fetchNodeIntrospectionData}
|
||||||
nodes={this.props.nodes}
|
nodes={this.props.nodes}
|
||||||
nodesInProgress={this.props.nodesInProgress}
|
nodesInProgress={this.props.nodesInProgress}
|
||||||
/>
|
/>
|
||||||
@ -118,6 +119,7 @@ class Nodes extends React.Component {
|
|||||||
Nodes.propTypes = {
|
Nodes.propTypes = {
|
||||||
contentView: PropTypes.string.isRequired,
|
contentView: PropTypes.string.isRequired,
|
||||||
currentPlanName: PropTypes.string.isRequired,
|
currentPlanName: PropTypes.string.isRequired,
|
||||||
|
fetchNodeIntrospectionData: PropTypes.func.isRequired,
|
||||||
fetchNodes: PropTypes.func.isRequired,
|
fetchNodes: PropTypes.func.isRequired,
|
||||||
fetchRoles: PropTypes.func.isRequired,
|
fetchRoles: PropTypes.func.isRequired,
|
||||||
fetchingNodes: PropTypes.bool.isRequired,
|
fetchingNodes: PropTypes.bool.isRequired,
|
||||||
@ -141,6 +143,8 @@ const mapStateToProps = state => ({
|
|||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
fetchNodes: () => dispatch(NodesActions.fetchNodes()),
|
fetchNodes: () => dispatch(NodesActions.fetchNodes()),
|
||||||
|
fetchNodeIntrospectionData: nodeId =>
|
||||||
|
dispatch(NodesActions.fetchNodeIntrospectionData(nodeId)),
|
||||||
fetchRoles: currentPlanName =>
|
fetchRoles: currentPlanName =>
|
||||||
dispatch(RolesActions.fetchRoles(currentPlanName))
|
dispatch(RolesActions.fetchRoles(currentPlanName))
|
||||||
});
|
});
|
||||||
|
@ -1,52 +1,251 @@
|
|||||||
import { FormattedDate, FormattedTime } from 'react-intl';
|
/**
|
||||||
|
* Copyright 2017 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
defineMessages,
|
||||||
|
FormattedDate,
|
||||||
|
FormattedMessage,
|
||||||
|
FormattedTime,
|
||||||
|
injectIntl
|
||||||
|
} from 'react-intl';
|
||||||
import { startCase } from 'lodash';
|
import { startCase } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Row, Col } from 'react-bootstrap';
|
import { Row, Col } from 'react-bootstrap';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
macAddresses: {
|
||||||
|
id: 'NodeExtendedInfo.macAddresses',
|
||||||
|
defaultMessage: 'Mac Addresses:'
|
||||||
|
},
|
||||||
|
interfaces: {
|
||||||
|
id: 'nodeExtendedinfo.interfaces',
|
||||||
|
defaultMessage: 'Interfaces:'
|
||||||
|
},
|
||||||
|
macAddress: {
|
||||||
|
id: 'nodeExtendedinfo.interfaceMacAddress',
|
||||||
|
defaultMessage: 'MAC Address'
|
||||||
|
},
|
||||||
|
ipAddress: {
|
||||||
|
id: 'nodeExtendedinfo.interfaceIpAddress',
|
||||||
|
defaultMessage: 'IP Address'
|
||||||
|
},
|
||||||
|
bios: {
|
||||||
|
id: 'nodeExtendedinfo.bios',
|
||||||
|
defaultMessage: 'Bios:'
|
||||||
|
},
|
||||||
|
rootDisk: {
|
||||||
|
id: 'nodeExtendedinfo.rootDisk',
|
||||||
|
defaultMessage: 'Root Disk:'
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
id: 'nodeExtendedinfo.product',
|
||||||
|
defaultMessage: 'Product:'
|
||||||
|
},
|
||||||
|
productName: {
|
||||||
|
id: 'nodeExtendedinfo.productName',
|
||||||
|
defaultMessage: 'Name'
|
||||||
|
},
|
||||||
|
productVendor: {
|
||||||
|
id: 'nodeExtendedinfo.productVendor',
|
||||||
|
defaultMessage: 'Vendor'
|
||||||
|
},
|
||||||
|
productVersion: {
|
||||||
|
id: 'nodeExtendedinfo.productVersion',
|
||||||
|
defaultMessage: 'Version'
|
||||||
|
},
|
||||||
|
kernel: {
|
||||||
|
id: 'nodeExtendedinfo.kernel',
|
||||||
|
defaultMessage: 'Kernel:'
|
||||||
|
},
|
||||||
|
uuid: {
|
||||||
|
id: 'nodeExtendedinfo.uuid',
|
||||||
|
defaultMessage: 'UUID:'
|
||||||
|
},
|
||||||
|
registered: {
|
||||||
|
id: 'nodeExtendedinfo.registered',
|
||||||
|
defaultMessage: 'Registered:'
|
||||||
|
},
|
||||||
|
architecture: {
|
||||||
|
id: 'nodeExtendedinfo.architecture',
|
||||||
|
defaultMessage: 'Architecture:'
|
||||||
|
},
|
||||||
|
driver: {
|
||||||
|
id: 'nodeExtendedinfo.driver',
|
||||||
|
defaultMessage: 'Driver:'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
class NodeExtendedInfo extends React.Component {
|
class NodeExtendedInfo extends React.Component {
|
||||||
|
componentDidMount() {
|
||||||
|
if (this.props.node.getIn(['introspectionStatus', 'finished'])) {
|
||||||
|
this.props.fetchNodeIntrospectionData(this.props.node.get('uuid'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInterfaces() {
|
||||||
|
const { intl, node } = this.props;
|
||||||
|
if (node.getIn(['introspectionData', 'interfaces']).isEmpty()) {
|
||||||
|
return (
|
||||||
|
<dl>
|
||||||
|
<dt><FormattedMessage {...messages.macAddresses} /></dt>
|
||||||
|
{node.get('macs').map(mac => <dd key={mac}>{mac}</dd>)}
|
||||||
|
</dl>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<dl>
|
||||||
|
<dt><FormattedMessage {...messages.interfaces} /></dt>
|
||||||
|
<dd>
|
||||||
|
{node
|
||||||
|
.getIn(['introspectionData', 'interfaces'])
|
||||||
|
.map((ifc, k) => {
|
||||||
|
return (
|
||||||
|
<div key={k}>
|
||||||
|
{k}
|
||||||
|
{' '} - {' '}
|
||||||
|
<span title={intl.formatMessage(messages.macAddress)}>
|
||||||
|
{ifc.get('mac')}
|
||||||
|
</span>
|
||||||
|
{' '} | {' '}
|
||||||
|
<span title={intl.formatMessage(messages.ipAddress)}>
|
||||||
|
{ifc.get('ip')}
|
||||||
|
</span>
|
||||||
|
{ifc.get('pxe') && '| PXE'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.toList()}
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBios() {
|
||||||
|
const bios = this.props.node.getIn(['introspectionData', 'bios']);
|
||||||
|
return (
|
||||||
|
!bios.isEmpty() &&
|
||||||
|
<div>
|
||||||
|
<dt><FormattedMessage {...messages.bios} /></dt>
|
||||||
|
<dd>
|
||||||
|
{bios
|
||||||
|
.map((i, k) => <span key={k} title={startCase(k)}>{i} </span>)
|
||||||
|
.toList()}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRootDisk() {
|
||||||
|
const rootDisk = this.props.node.getIn(['introspectionData', 'rootDisk']);
|
||||||
|
return (
|
||||||
|
rootDisk &&
|
||||||
|
<div>
|
||||||
|
<dt><FormattedMessage {...messages.rootDisk} /></dt>
|
||||||
|
<dd>{rootDisk}</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderProduct() {
|
||||||
|
const product = this.props.node.getIn(['introspectionData', 'product']);
|
||||||
|
return (
|
||||||
|
!product.isEmpty() &&
|
||||||
|
<div>
|
||||||
|
<dt><FormattedMessage {...messages.product} /></dt>
|
||||||
|
<dd>
|
||||||
|
<span title={this.props.intl.formatMessage(messages.productName)}>
|
||||||
|
{product.get('name')}
|
||||||
|
</span>
|
||||||
|
{' '} - {' '}
|
||||||
|
<span title={this.props.intl.formatMessage(messages.productVendor)}>
|
||||||
|
{product.get('vendor')}
|
||||||
|
</span>
|
||||||
|
{' '} | {' '}
|
||||||
|
<span title={this.props.intl.formatMessage(messages.productVersion)}>
|
||||||
|
{product.get('version')}
|
||||||
|
</span>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderKernel() {
|
||||||
|
const kernelVersion = this.props.node.getIn([
|
||||||
|
'introspectionData',
|
||||||
|
'kernelVersion'
|
||||||
|
]);
|
||||||
|
return (
|
||||||
|
kernelVersion &&
|
||||||
|
<div>
|
||||||
|
<dt><FormattedMessage {...messages.kernel} /></dt>
|
||||||
|
<dd>{kernelVersion}</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { node } = this.props;
|
const { node } = this.props;
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
<Col lg={3} md={6}>
|
<Col lg={4} md={6}>
|
||||||
<dl className="dl-horizontal dl-horizontal-condensed">
|
<dl className="dl-horizontal dl-horizontal-condensed">
|
||||||
<dt>UUID:</dt>
|
<dt><FormattedMessage {...messages.uuid} /></dt>
|
||||||
<dd>{node.uuid}</dd>
|
<dd>{node.get('uuid')}</dd>
|
||||||
<dt>Registered:</dt>
|
<dt><FormattedMessage {...messages.registered} /></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<FormattedDate value={node.created_at} />
|
<FormattedDate value={node.get('created_at')} />
|
||||||
|
|
||||||
<FormattedTime value={node.created_at} />
|
<FormattedTime value={node.get('created_at')} />
|
||||||
</dd>
|
</dd>
|
||||||
<dt>Architecture:</dt>
|
<dt><FormattedMessage {...messages.architecture} /></dt>
|
||||||
<dd>{node.properties.cpu_arch}</dd>
|
<dd>{node.getIn(['properties', 'cpu_arch'])}</dd>
|
||||||
</dl>
|
{this.renderRootDisk()}
|
||||||
</Col>
|
{this.renderBios()}
|
||||||
<Col lg={2} md={4}>
|
{this.renderProduct()}
|
||||||
<dl>
|
{this.renderKernel()}
|
||||||
<dt>Mac Addresses:</dt>
|
|
||||||
{node.macs.map(mac => <dd key={mac}>{mac}</dd>)}
|
|
||||||
</dl>
|
</dl>
|
||||||
</Col>
|
</Col>
|
||||||
<Col lg={4} md={6}>
|
<Col lg={4} md={6}>
|
||||||
<dl className="dl-horizontal dl-horizontal-condensed">
|
<dl className="dl-horizontal dl-horizontal-condensed">
|
||||||
<dt>Driver:</dt>
|
<dt><FormattedMessage {...messages.driver} /></dt>
|
||||||
<dd>{node.driver}</dd>
|
<dd>{node.get('driver')}</dd>
|
||||||
{Object.keys(node.driver_info).map(key => (
|
{node
|
||||||
<span key={key}>
|
.get('driver_info')
|
||||||
<dt>{startCase(key)}:</dt>
|
.map((dInfo, key) => (
|
||||||
<dd>{node.driver_info[key]}</dd>
|
<span key={key}>
|
||||||
</span>
|
<dt>{startCase(key)}:</dt>
|
||||||
))}
|
<dd>{dInfo}</dd>
|
||||||
|
</span>
|
||||||
|
))
|
||||||
|
.toList()}
|
||||||
</dl>
|
</dl>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col lg={3} md={6}>
|
||||||
|
{this.renderInterfaces()}
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeExtendedInfo.propTypes = {
|
NodeExtendedInfo.propTypes = {
|
||||||
|
fetchNodeIntrospectionData: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
node: PropTypes.object.isRequired
|
node: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NodeExtendedInfo;
|
export default injectIntl(NodeExtendedInfo);
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import ClassNames from 'classnames';
|
import ClassNames from 'classnames';
|
||||||
import { defineMessages, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ListViewAdditionalInfo,
|
ListViewAdditionalInfo,
|
||||||
@ -61,7 +78,7 @@ export default class NodeListItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { node, inProgress } = this.props;
|
const { fetchNodeIntrospectionData, node, inProgress } = this.props;
|
||||||
|
|
||||||
const iconClass = ClassNames({
|
const iconClass = ClassNames({
|
||||||
'pficon pficon-server': true,
|
'pficon pficon-server': true,
|
||||||
@ -73,7 +90,7 @@ export default class NodeListItem extends React.Component {
|
|||||||
<ListViewExpand expanded={this.state.expanded} />
|
<ListViewExpand expanded={this.state.expanded} />
|
||||||
<ListViewCheckbox
|
<ListViewCheckbox
|
||||||
disabled={inProgress}
|
disabled={inProgress}
|
||||||
name={`values.${node.uuid}`}
|
name={`values.${node.get('uuid')}`}
|
||||||
/>
|
/>
|
||||||
<ListViewMainInfo>
|
<ListViewMainInfo>
|
||||||
<ListViewLeft>
|
<ListViewLeft>
|
||||||
@ -82,23 +99,25 @@ export default class NodeListItem extends React.Component {
|
|||||||
<ListViewBody>
|
<ListViewBody>
|
||||||
<ListViewDescription>
|
<ListViewDescription>
|
||||||
<ListViewDescriptionHeading>
|
<ListViewDescriptionHeading>
|
||||||
{node.name || node.uuid}
|
{node.get('name') || node.get('uuid')}
|
||||||
</ListViewDescriptionHeading>
|
</ListViewDescriptionHeading>
|
||||||
<ListViewDescriptionText>
|
<ListViewDescriptionText>
|
||||||
<NodePowerState
|
<NodePowerState
|
||||||
powerState={node.power_state}
|
powerState={node.get('power_state')}
|
||||||
targetPowerState={node.target_power_state}
|
targetPowerState={node.get('target_power_state')}
|
||||||
/>
|
/>
|
||||||
<NodeMaintenanceState
|
<NodeMaintenanceState
|
||||||
maintenance={node.maintenance}
|
maintenance={node.get('maintenance')}
|
||||||
reason={node.maintenance_reason}
|
reason={node.get('maintenance_reason')}
|
||||||
/>
|
/>
|
||||||
{' | '}
|
{' | '}
|
||||||
<NodeIntrospectionStatus status={node.introspectionStatus} />
|
<NodeIntrospectionStatus
|
||||||
|
status={node.get('introspectionStatus').toJS()}
|
||||||
|
/>
|
||||||
{' | '}
|
{' | '}
|
||||||
<NodeProvisionState
|
<NodeProvisionState
|
||||||
provisionState={node.provision_state}
|
provisionState={node.get('provision_state')}
|
||||||
targetProvisionState={node.target_provision_state}
|
targetProvisionState={node.get('target_provision_state')}
|
||||||
/>
|
/>
|
||||||
</ListViewDescriptionText>
|
</ListViewDescriptionText>
|
||||||
</ListViewDescription>
|
</ListViewDescription>
|
||||||
@ -107,27 +126,30 @@ export default class NodeListItem extends React.Component {
|
|||||||
<span className="pficon pficon-flavor" />
|
<span className="pficon pficon-flavor" />
|
||||||
<FormattedMessage {...messages.profile} />
|
<FormattedMessage {...messages.profile} />
|
||||||
|
|
||||||
{parseNodeCapabilities(node.properties.capabilities)
|
{parseNodeCapabilities(
|
||||||
.profile || '-'}
|
node.getIn(['properties', 'capabilities'])
|
||||||
|
).profile || '-'}
|
||||||
</ListViewAdditionalInfoItem>
|
</ListViewAdditionalInfoItem>
|
||||||
<ListViewAdditionalInfoItem>
|
<ListViewAdditionalInfoItem>
|
||||||
<span className="pficon pficon-cpu" />
|
<span className="pficon pficon-cpu" />
|
||||||
<strong>{node.properties.cpus || '-'}</strong>
|
<strong>{node.getIn(['properties', 'cpus'], '-')}</strong>
|
||||||
|
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
{...messages.cpuCores}
|
{...messages.cpuCores}
|
||||||
values={{ cpuCores: node.properties.cpus }}
|
values={{ cpuCores: node.getIn(['properties', 'cpus']) }}
|
||||||
/>
|
/>
|
||||||
</ListViewAdditionalInfoItem>
|
</ListViewAdditionalInfoItem>
|
||||||
<ListViewAdditionalInfoItem>
|
<ListViewAdditionalInfoItem>
|
||||||
<span className="pficon pficon-memory" />
|
<span className="pficon pficon-memory" />
|
||||||
<strong>{node.properties.memory_mb || '-'}</strong>
|
<strong>
|
||||||
|
{node.getIn(['properties', 'memory_mb'], '-')}
|
||||||
|
</strong>
|
||||||
|
|
||||||
<FormattedMessage {...messages.ram} />
|
<FormattedMessage {...messages.ram} />
|
||||||
</ListViewAdditionalInfoItem>
|
</ListViewAdditionalInfoItem>
|
||||||
<ListViewAdditionalInfoItem>
|
<ListViewAdditionalInfoItem>
|
||||||
<span className="fa fa-database" />
|
<span className="fa fa-database" />
|
||||||
<strong>{node.properties.local_gb || '-'}</strong>
|
<strong>{node.getIn(['properties', 'local_gb'], '-')}</strong>
|
||||||
|
|
||||||
<FormattedMessage {...messages.disk} />
|
<FormattedMessage {...messages.disk} />
|
||||||
</ListViewAdditionalInfoItem>
|
</ListViewAdditionalInfoItem>
|
||||||
@ -139,13 +161,17 @@ export default class NodeListItem extends React.Component {
|
|||||||
onClose={this.toggleExpanded.bind(this)}
|
onClose={this.toggleExpanded.bind(this)}
|
||||||
expanded={this.state.expanded}
|
expanded={this.state.expanded}
|
||||||
>
|
>
|
||||||
<NodeExtendedInfo node={node} />
|
<NodeExtendedInfo
|
||||||
|
node={node}
|
||||||
|
fetchNodeIntrospectionData={fetchNodeIntrospectionData}
|
||||||
|
/>
|
||||||
</ListViewItemContainer>
|
</ListViewItemContainer>
|
||||||
</ListViewItem>
|
</ListViewItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeListItem.propTypes = {
|
NodeListItem.propTypes = {
|
||||||
|
fetchNodeIntrospectionData: PropTypes.func.isRequired,
|
||||||
inProgress: PropTypes.bool.isRequired,
|
inProgress: PropTypes.bool.isRequired,
|
||||||
node: PropTypes.object.isRequired
|
node: ImmutablePropTypes.map.isRequired
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import ClassNames from 'classnames';
|
import ClassNames from 'classnames';
|
||||||
import { defineMessages, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
@ -1,4 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { ListView } from '../../ui/ListView';
|
import { ListView } from '../../ui/ListView';
|
||||||
@ -10,12 +27,12 @@ export default class NodesListView extends React.Component {
|
|||||||
<ListView>
|
<ListView>
|
||||||
{this.props.nodes
|
{this.props.nodes
|
||||||
.toList()
|
.toList()
|
||||||
.toJS()
|
|
||||||
.map(node => (
|
.map(node => (
|
||||||
<NodeListItem
|
<NodeListItem
|
||||||
|
fetchNodeIntrospectionData={this.props.fetchNodeIntrospectionData}
|
||||||
node={node}
|
node={node}
|
||||||
key={node.uuid}
|
key={node.get('uuid')}
|
||||||
inProgress={this.props.nodesInProgress.includes(node.uuid)}
|
inProgress={this.props.nodesInProgress.includes(node.get('uuid'))}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ListView>
|
</ListView>
|
||||||
@ -23,6 +40,7 @@ export default class NodesListView extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodesListView.propTypes = {
|
NodesListView.propTypes = {
|
||||||
|
fetchNodeIntrospectionData: PropTypes.func.isRequired,
|
||||||
nodes: ImmutablePropTypes.map.isRequired,
|
nodes: ImmutablePropTypes.map.isRequired,
|
||||||
nodesInProgress: ImmutablePropTypes.set.isRequired
|
nodesInProgress: ImmutablePropTypes.set.isRequired
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,8 @@ export default keyMirror({
|
|||||||
REQUEST_NODES: null,
|
REQUEST_NODES: null,
|
||||||
RECEIVE_NODES: null,
|
RECEIVE_NODES: null,
|
||||||
FETCH_NODE_MACS_SUCCESS: null,
|
FETCH_NODE_MACS_SUCCESS: null,
|
||||||
|
FETCH_NODE_INTROSPECTION_DATA_SUCCESS: null,
|
||||||
|
FETCH_NODE_INTROSPECTION_DATA_FAILED: null,
|
||||||
START_NODES_OPERATION: null,
|
START_NODES_OPERATION: null,
|
||||||
FINISH_NODES_OPERATION: null,
|
FINISH_NODES_OPERATION: null,
|
||||||
UPDATE_NODE_PENDING: null,
|
UPDATE_NODE_PENDING: null,
|
||||||
|
@ -22,7 +22,8 @@ export const NodesState = Record({
|
|||||||
nodesInProgress: Set(),
|
nodesInProgress: Set(),
|
||||||
all: Map(),
|
all: Map(),
|
||||||
ports: Map(),
|
ports: Map(),
|
||||||
introspectionStatuses: Map()
|
introspectionStatuses: Map(),
|
||||||
|
introspectionData: Map()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const NodeToRegister = Record({
|
export const NodeToRegister = Record({
|
||||||
|
@ -43,6 +43,14 @@ export default function nodesReducer(state = initialState, action) {
|
|||||||
.set('isFetching', false);
|
.set('isFetching', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case NodesConstants.FETCH_NODE_INTROSPECTION_DATA_SUCCESS: {
|
||||||
|
const { nodeId, data } = action.payload;
|
||||||
|
return state.setIn(['introspectionData', nodeId], fromJS(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
case NodesConstants.FETCH_NODE_INTROSPECTION_DATA_FAILED:
|
||||||
|
return state.deleteIn(['introspectionData', action.payload]);
|
||||||
|
|
||||||
case NodesConstants.START_NODES_OPERATION:
|
case NodesConstants.START_NODES_OPERATION:
|
||||||
return state.update('nodesInProgress', nodesInProgress =>
|
return state.update('nodesInProgress', nodesInProgress =>
|
||||||
nodesInProgress.union(action.payload)
|
nodesInProgress.union(action.payload)
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { List, Set } from 'immutable';
|
import { List, Map, Set } from 'immutable';
|
||||||
|
|
||||||
import { getFilterByName } from './filters';
|
import { getFilterByName } from './filters';
|
||||||
import { getRoles } from './roles';
|
import { getRoles } from './roles';
|
||||||
@ -30,6 +30,8 @@ export const getNodesByIds = (state, nodeIds) =>
|
|||||||
.filter((v, k) => nodeIds.includes(k))
|
.filter((v, k) => nodeIds.includes(k))
|
||||||
.sortBy(n => n.get('uuid'));
|
.sortBy(n => n.get('uuid'));
|
||||||
export const getPorts = state => state.nodes.get('ports');
|
export const getPorts = state => state.nodes.get('ports');
|
||||||
|
export const getIntrospectionData = state =>
|
||||||
|
state.nodes.get('introspectionData');
|
||||||
export const getIntrospectionStatuses = state =>
|
export const getIntrospectionStatuses = state =>
|
||||||
state.nodes.get('introspectionStatuses');
|
state.nodes.get('introspectionStatuses');
|
||||||
export const nodesInProgress = state => state.nodes.get('nodesInProgress');
|
export const nodesInProgress = state => state.nodes.get('nodesInProgress');
|
||||||
@ -40,8 +42,8 @@ export const nodesToolbarFilter = state =>
|
|||||||
* Return Nodes including mac addresses as string at macs attribute
|
* Return Nodes including mac addresses as string at macs attribute
|
||||||
*/
|
*/
|
||||||
export const getDetailedNodes = createSelector(
|
export const getDetailedNodes = createSelector(
|
||||||
[getNodes, getPorts, getIntrospectionStatuses],
|
[getNodes, getPorts, getIntrospectionStatuses, getIntrospectionData],
|
||||||
(nodes, ports, introspectionStatuses) =>
|
(nodes, ports, introspectionStatuses, introspectionData) =>
|
||||||
nodes.map(node =>
|
nodes.map(node =>
|
||||||
node
|
node
|
||||||
.set(
|
.set(
|
||||||
@ -55,6 +57,38 @@ export const getDetailedNodes = createSelector(
|
|||||||
'introspectionStatus',
|
'introspectionStatus',
|
||||||
introspectionStatuses.get(node.get('uuid'), new IntrospectionStatus())
|
introspectionStatuses.get(node.get('uuid'), new IntrospectionStatus())
|
||||||
)
|
)
|
||||||
|
.setIn(
|
||||||
|
['introspectionData', 'interfaces'],
|
||||||
|
introspectionData.getIn([node.get('uuid'), 'interfaces'], Map())
|
||||||
|
)
|
||||||
|
.setIn(
|
||||||
|
['introspectionData', 'rootDisk'],
|
||||||
|
introspectionData.getIn([node.get('uuid'), 'root_disk', 'name'])
|
||||||
|
)
|
||||||
|
.setIn(
|
||||||
|
['introspectionData', 'product'],
|
||||||
|
introspectionData.getIn(
|
||||||
|
[node.get('uuid'), 'extra', 'system', 'product'],
|
||||||
|
Map()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setIn(
|
||||||
|
['introspectionData', 'kernelVersion'],
|
||||||
|
introspectionData.getIn([
|
||||||
|
node.get('uuid'),
|
||||||
|
'extra',
|
||||||
|
'system',
|
||||||
|
'kernel',
|
||||||
|
'version'
|
||||||
|
])
|
||||||
|
)
|
||||||
|
.setIn(
|
||||||
|
['introspectionData', 'bios'],
|
||||||
|
introspectionData.getIn(
|
||||||
|
[node.get('uuid'), 'extra', 'firmware', 'bios'],
|
||||||
|
Map()
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
74
src/js/services/IronicInspectorApiErrorHandler.js
Normal file
74
src/js/services/IronicInspectorApiErrorHandler.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import BaseHttpRequestErrorHandler
|
||||||
|
from '../components/utils/BaseHttpRequestErrorHandler';
|
||||||
|
import LoginActions from '../actions/LoginActions';
|
||||||
|
import store from '../store';
|
||||||
|
|
||||||
|
export default class IronicInspectorApiErrorHandler
|
||||||
|
extends BaseHttpRequestErrorHandler {
|
||||||
|
_generateErrors(errorObj) {
|
||||||
|
let errors = [];
|
||||||
|
let error;
|
||||||
|
// A weak check to find out if it's not an xhr object.
|
||||||
|
if (!errorObj.status && errorObj.message) {
|
||||||
|
errors.push({
|
||||||
|
title: 'Error',
|
||||||
|
message: errorObj.message
|
||||||
|
});
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
switch (errorObj.status) {
|
||||||
|
case 0:
|
||||||
|
errors.push({
|
||||||
|
title: 'Connection Error',
|
||||||
|
message: 'Connection to Ironic Inspector is not available'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
error = JSON.parse(errorObj.responseText).error.message;
|
||||||
|
errors.push({
|
||||||
|
title: 'Introspection data not found',
|
||||||
|
message: error
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
error = JSON.parse(errorObj.responseText).error.message;
|
||||||
|
errors.push({
|
||||||
|
title: 'Unauthorized',
|
||||||
|
message: error
|
||||||
|
});
|
||||||
|
store.dispatch(LoginActions.logoutUser());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = JSON.parse(errorObj.responseText).error.message;
|
||||||
|
status = errorObj.status;
|
||||||
|
errors.push({
|
||||||
|
title: `Error ${status}`,
|
||||||
|
message: error
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(jtomasek): remove this, I am leaving this here just for example reasons
|
||||||
|
// this function should be implemented by form related subclass.
|
||||||
|
_generateFormFieldErrors() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user