mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 09:27:36 -02:30
feature hop node topology updates (#14142)
This commit is contained in:
@@ -148,6 +148,7 @@ function InstanceForm({
|
|||||||
node_state: instance.node_state || 'installed',
|
node_state: instance.node_state || 'installed',
|
||||||
listener_port: instance.listener_port || 27199,
|
listener_port: instance.listener_port || 27199,
|
||||||
enabled: instance.enabled || true,
|
enabled: instance.enabled || true,
|
||||||
|
managed_by_policy: instance.managed_by_policy || true,
|
||||||
peers_from_control_nodes: instance.peers_from_control_nodes
|
peers_from_control_nodes: instance.peers_from_control_nodes
|
||||||
? true
|
? true
|
||||||
: !isEdit,
|
: !isEdit,
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ function RemoveInstanceButton({ itemsToRemove, onRemove, isK8s }) {
|
|||||||
const [removeDetails, setRemoveDetails] = useState(null);
|
const [removeDetails, setRemoveDetails] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const cannotRemove = (item) => !(item.node_type === 'execution' || item.node_type === 'hop');
|
const cannotRemove = (item) =>
|
||||||
|
!(item.node_type === 'execution' || item.node_type === 'hop');
|
||||||
|
|
||||||
const toggleModal = async (isOpen) => {
|
const toggleModal = async (isOpen) => {
|
||||||
setRemoveDetails(null);
|
setRemoveDetails(null);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
SearchPlusIcon,
|
SearchPlusIcon,
|
||||||
ExpandArrowsAltIcon,
|
ExpandArrowsAltIcon,
|
||||||
ExpandIcon,
|
ExpandIcon,
|
||||||
|
RedoAltIcon,
|
||||||
} from '@patternfly/react-icons';
|
} from '@patternfly/react-icons';
|
||||||
|
|
||||||
const Header = ({
|
const Header = ({
|
||||||
@@ -26,6 +27,7 @@ const Header = ({
|
|||||||
zoomOut,
|
zoomOut,
|
||||||
resetZoom,
|
resetZoom,
|
||||||
zoomFit,
|
zoomFit,
|
||||||
|
refresh,
|
||||||
showZoomControls,
|
showZoomControls,
|
||||||
}) => {
|
}) => {
|
||||||
const { light } = PageSectionVariants;
|
const { light } = PageSectionVariants;
|
||||||
@@ -48,6 +50,18 @@ const Header = ({
|
|||||||
</Title>
|
</Title>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<Tooltip content={t`Refresh`} position="top">
|
||||||
|
<Button
|
||||||
|
ouiaId="refresh-button"
|
||||||
|
aria-label={t`Refresh`}
|
||||||
|
variant="plain"
|
||||||
|
icon={<RedoAltIcon />}
|
||||||
|
onClick={refresh}
|
||||||
|
isDisabled={!showZoomControls}
|
||||||
|
>
|
||||||
|
<RedoAltIcon />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
<Tooltip content={t`Zoom in`} position="top">
|
<Tooltip content={t`Zoom in`} position="top">
|
||||||
<Button
|
<Button
|
||||||
ouiaId="zoom-in-button"
|
ouiaId="zoom-in-button"
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ function Legend() {
|
|||||||
y1="9"
|
y1="9"
|
||||||
x2="20"
|
x2="20"
|
||||||
y2="9"
|
y2="9"
|
||||||
stroke="#666"
|
stroke="#6A6E73"
|
||||||
strokeWidth="4"
|
strokeWidth="4"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -260,8 +260,9 @@ function Legend() {
|
|||||||
y1="9"
|
y1="9"
|
||||||
x2="20"
|
x2="20"
|
||||||
y2="9"
|
y2="9"
|
||||||
stroke="#c9700b"
|
stroke="#ccc"
|
||||||
strokeWidth="4"
|
strokeWidth="4"
|
||||||
|
strokeDasharray="6"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</DescriptionListTerm>
|
</DescriptionListTerm>
|
||||||
@@ -275,7 +276,7 @@ function Legend() {
|
|||||||
y1="9"
|
y1="9"
|
||||||
x2="20"
|
x2="20"
|
||||||
y2="9"
|
y2="9"
|
||||||
stroke="#666"
|
stroke="#3E8635"
|
||||||
strokeWidth="4"
|
strokeWidth="4"
|
||||||
strokeDasharray="6"
|
strokeDasharray="6"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import Tooltip from './Tooltip';
|
|||||||
import ContentLoading from './ContentLoading';
|
import ContentLoading from './ContentLoading';
|
||||||
import {
|
import {
|
||||||
renderStateColor,
|
renderStateColor,
|
||||||
|
renderLinkStatusColor,
|
||||||
renderLabelText,
|
renderLabelText,
|
||||||
renderNodeType,
|
renderNodeType,
|
||||||
renderNodeIcon,
|
renderNodeIcon,
|
||||||
@@ -177,7 +178,13 @@ function MeshGraph({
|
|||||||
mesh
|
mesh
|
||||||
.append('defs')
|
.append('defs')
|
||||||
.selectAll('marker')
|
.selectAll('marker')
|
||||||
.data(['end', 'end-active'])
|
.data([
|
||||||
|
'end',
|
||||||
|
'end-active',
|
||||||
|
'end-disconnected',
|
||||||
|
'end-adding',
|
||||||
|
'end-removing',
|
||||||
|
])
|
||||||
.join('marker')
|
.join('marker')
|
||||||
.attr('id', String)
|
.attr('id', String)
|
||||||
.attr('viewBox', '0 -5 10 10')
|
.attr('viewBox', '0 -5 10 10')
|
||||||
@@ -187,8 +194,10 @@ function MeshGraph({
|
|||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M0,-5L10,0L0,5');
|
.attr('d', 'M0,-5L10,0L0,5');
|
||||||
|
mesh.select('#end').attr('refX', 23).attr('fill', '#6A6E73');
|
||||||
mesh.select('#end').attr('refX', 23).attr('fill', '#ccc');
|
mesh.select('#end-removing').attr('refX', 23).attr('fill', '#C9190B');
|
||||||
|
mesh.select('#end-adding').attr('refX', 23).attr('fill', '#3E8635');
|
||||||
|
mesh.select('#end-disconnected').attr('refX', 23).attr('fill', '#CCC');
|
||||||
mesh.select('#end-active').attr('refX', 18).attr('fill', '#0066CC');
|
mesh.select('#end-active').attr('refX', 18).attr('fill', '#0066CC');
|
||||||
|
|
||||||
// Add links
|
// Add links
|
||||||
@@ -204,24 +213,27 @@ function MeshGraph({
|
|||||||
.attr('y1', (d) => d.source.y)
|
.attr('y1', (d) => d.source.y)
|
||||||
.attr('x2', (d) => d.target.x)
|
.attr('x2', (d) => d.target.x)
|
||||||
.attr('y2', (d) => d.target.y)
|
.attr('y2', (d) => d.target.y)
|
||||||
.attr('marker-end', 'url(#end)')
|
.attr('marker-end', (d) => {
|
||||||
|
if (d.link_state === 'disconnected') {
|
||||||
|
return 'url(#end-disconnected)';
|
||||||
|
}
|
||||||
|
if (d.link_state === 'adding') {
|
||||||
|
return 'url(#end-adding)';
|
||||||
|
}
|
||||||
|
if (d.link_state === 'removing') {
|
||||||
|
return 'url(#end-removing)';
|
||||||
|
}
|
||||||
|
return 'url(#end)';
|
||||||
|
})
|
||||||
.attr('class', (_, i) => `link-${i}`)
|
.attr('class', (_, i) => `link-${i}`)
|
||||||
.attr('data-cy', (d) => `${d.source.hostname}-${d.target.hostname}`)
|
.attr('data-cy', (d) => `${d.source.hostname}-${d.target.hostname}`)
|
||||||
.style('fill', 'none')
|
.style('fill', 'none')
|
||||||
.style('stroke', (d) => {
|
.style('stroke', (d) => renderLinkStatusColor(d.link_state))
|
||||||
if (d.link_state === 'removing') {
|
|
||||||
return '#C9190B';
|
|
||||||
}
|
|
||||||
if (d.link_state === 'disconnected') {
|
|
||||||
return '#c9700b';
|
|
||||||
}
|
|
||||||
return '#CCC';
|
|
||||||
})
|
|
||||||
.style('stroke-width', '2px')
|
.style('stroke-width', '2px')
|
||||||
.style('stroke-dasharray', (d) => renderLinkState(d.link_state))
|
.style('stroke-dasharray', (d) => renderLinkState(d.link_state))
|
||||||
.attr('pointer-events', 'none')
|
.attr('pointer-events', 'none')
|
||||||
.on('mouseover', function showPointer() {
|
.on('mouseover', function showPointer() {
|
||||||
d3.select(this).transition().style('cursor', 'pointer');
|
d3.select(this).style('cursor', 'pointer');
|
||||||
});
|
});
|
||||||
// add nodes
|
// add nodes
|
||||||
const node = mesh
|
const node = mesh
|
||||||
@@ -234,7 +246,7 @@ function MeshGraph({
|
|||||||
.append('g')
|
.append('g')
|
||||||
.attr('data-cy', (d) => `node-${d.id}`)
|
.attr('data-cy', (d) => `node-${d.id}`)
|
||||||
.on('mouseenter', function handleNodeHover(_, d) {
|
.on('mouseenter', function handleNodeHover(_, d) {
|
||||||
d3.select(this).transition().style('cursor', 'pointer');
|
d3.select(this).style('cursor', 'pointer');
|
||||||
highlightSiblings(d);
|
highlightSiblings(d);
|
||||||
})
|
})
|
||||||
.on('mouseleave', (_, d) => {
|
.on('mouseleave', (_, d) => {
|
||||||
@@ -245,7 +257,8 @@ function MeshGraph({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// node circles
|
// node circles
|
||||||
node
|
const nodeCircles = node.append('g');
|
||||||
|
nodeCircles
|
||||||
.append('circle')
|
.append('circle')
|
||||||
.attr('r', DEFAULT_RADIUS)
|
.attr('r', DEFAULT_RADIUS)
|
||||||
.attr('cx', (d) => d.x)
|
.attr('cx', (d) => d.x)
|
||||||
@@ -254,7 +267,8 @@ function MeshGraph({
|
|||||||
.attr('class', (d) => `id-${d.id}`)
|
.attr('class', (d) => `id-${d.id}`)
|
||||||
.attr('fill', DEFAULT_NODE_COLOR)
|
.attr('fill', DEFAULT_NODE_COLOR)
|
||||||
.attr('stroke-dasharray', (d) => (d.enabled ? `1 0` : `5`))
|
.attr('stroke-dasharray', (d) => (d.enabled ? `1 0` : `5`))
|
||||||
.attr('stroke', DEFAULT_NODE_STROKE_COLOR);
|
.attr('stroke', (d) => renderStateColor(d.node_state));
|
||||||
|
|
||||||
// node type labels
|
// node type labels
|
||||||
node
|
node
|
||||||
.append('text')
|
.append('text')
|
||||||
@@ -265,64 +279,62 @@ function MeshGraph({
|
|||||||
.attr('dominant-baseline', 'central')
|
.attr('dominant-baseline', 'central')
|
||||||
.attr('fill', DEFAULT_NODE_SYMBOL_TEXT_COLOR);
|
.attr('fill', DEFAULT_NODE_SYMBOL_TEXT_COLOR);
|
||||||
|
|
||||||
const placeholder = node.append('g').attr('class', 'placeholder');
|
// node hostname labels
|
||||||
|
const hostNames = node.append('g').attr('class', 'node-state-label');
|
||||||
placeholder
|
|
||||||
.append('text')
|
|
||||||
.text((d) => renderLabelText(d.node_state, d.hostname))
|
|
||||||
.attr('x', (d) => d.x)
|
|
||||||
.attr('y', (d) => d.y + 40)
|
|
||||||
.attr('fill', 'black')
|
|
||||||
.attr('font-size', '18px')
|
|
||||||
.attr('text-anchor', 'middle')
|
|
||||||
.each(function calculateLabelWidth() {
|
|
||||||
// eslint-disable-next-line react/no-this-in-sfc
|
|
||||||
const bbox = this.getBBox();
|
|
||||||
// eslint-disable-next-line react/no-this-in-sfc
|
|
||||||
d3.select(this.parentNode)
|
|
||||||
.append('path')
|
|
||||||
.attr('d', (d) => renderLabelIcons(d.node_state))
|
|
||||||
.attr('transform', (d) => renderIconPosition(d.node_state, bbox))
|
|
||||||
.style('fill', 'black');
|
|
||||||
});
|
|
||||||
|
|
||||||
placeholder.each(function calculateLabelWidth() {
|
|
||||||
// eslint-disable-next-line react/no-this-in-sfc
|
|
||||||
const bbox = this.getBBox();
|
|
||||||
// eslint-disable-next-line react/no-this-in-sfc
|
|
||||||
d3.select(this.parentNode)
|
|
||||||
.append('rect')
|
|
||||||
.attr('x', (d) => d.x - bbox.width / 2)
|
|
||||||
.attr('y', bbox.y + 5)
|
|
||||||
.attr('width', bbox.width)
|
|
||||||
.attr('height', bbox.height)
|
|
||||||
.attr('rx', 8)
|
|
||||||
.attr('ry', 8)
|
|
||||||
.style('fill', (d) => renderStateColor(d.node_state));
|
|
||||||
});
|
|
||||||
|
|
||||||
const hostNames = node.append('g');
|
|
||||||
hostNames
|
hostNames
|
||||||
.append('text')
|
.append('text')
|
||||||
|
.attr('x', (d) => d.x)
|
||||||
|
.attr('y', (d) => d.y + 40)
|
||||||
.text((d) => renderLabelText(d.node_state, d.hostname))
|
.text((d) => renderLabelText(d.node_state, d.hostname))
|
||||||
.attr('x', (d) => d.x + 6)
|
.attr('class', 'placeholder')
|
||||||
.attr('y', (d) => d.y + 42)
|
|
||||||
.attr('fill', 'white')
|
.attr('fill', 'white')
|
||||||
.attr('font-size', DEFAULT_FONT_SIZE)
|
|
||||||
.attr('text-anchor', 'middle')
|
.attr('text-anchor', 'middle')
|
||||||
.each(function calculateLabelWidth() {
|
.each(function calculateLabelWidth() {
|
||||||
// eslint-disable-next-line react/no-this-in-sfc
|
// eslint-disable-next-line react/no-this-in-sfc
|
||||||
const bbox = this.getBBox();
|
const bbox = this.getBBox();
|
||||||
|
const padding = 10;
|
||||||
// eslint-disable-next-line react/no-this-in-sfc
|
// eslint-disable-next-line react/no-this-in-sfc
|
||||||
d3.select(this.parentNode)
|
d3.select(this.parentNode)
|
||||||
.append('path')
|
.append('rect')
|
||||||
.attr('class', (d) => `icon-${d.node_state}`)
|
.attr('x', bbox.x - padding / 2)
|
||||||
.attr('d', (d) => renderLabelIcons(d.node_state))
|
.attr('y', bbox.y)
|
||||||
.attr('transform', (d) => renderIconPosition(d.node_state, bbox))
|
.attr('width', bbox.width + padding)
|
||||||
.attr('fill', 'white');
|
.attr('height', bbox.height)
|
||||||
|
.style('stroke-width', 1)
|
||||||
|
.attr('rx', 4)
|
||||||
|
.attr('ry', 4)
|
||||||
|
.attr('fill', 'white')
|
||||||
|
.style('stroke', DEFAULT_NODE_STROKE_COLOR);
|
||||||
});
|
});
|
||||||
svg.selectAll('g.placeholder').remove();
|
svg.selectAll('text.placeholder').remove();
|
||||||
|
hostNames
|
||||||
|
.append('text')
|
||||||
|
.attr('x', (d) => d.x)
|
||||||
|
.attr('y', (d) => d.y + 38)
|
||||||
|
.text((d) => renderLabelText(d.node_state, d.hostname))
|
||||||
|
.attr('font-size', DEFAULT_FONT_SIZE)
|
||||||
|
.attr('fill', 'black')
|
||||||
|
.attr('text-anchor', 'middle');
|
||||||
|
|
||||||
|
// add badge icons
|
||||||
|
const badges = nodeCircles.append('g').attr('class', 'node-state-badge');
|
||||||
|
badges.each(function drawStateBadge() {
|
||||||
|
// eslint-disable-next-line react/no-this-in-sfc
|
||||||
|
const bbox = this.parentNode.getBBox();
|
||||||
|
// eslint-disable-next-line react/no-this-in-sfc
|
||||||
|
d3.select(this)
|
||||||
|
.append('circle')
|
||||||
|
.attr('r', 9)
|
||||||
|
.attr('cx', bbox.x)
|
||||||
|
.attr('cy', bbox.y)
|
||||||
|
.attr('fill', (d) => renderStateColor(d.node_state));
|
||||||
|
d3.select(this)
|
||||||
|
.append('path')
|
||||||
|
.attr('class', (d) => `icon-${d.node_state}`)
|
||||||
|
.attr('d', (d) => renderLabelIcons(d.node_state))
|
||||||
|
.attr('transform', (d) => renderIconPosition(d.node_state, bbox))
|
||||||
|
.attr('fill', 'white');
|
||||||
|
});
|
||||||
svg.call(zoom);
|
svg.call(zoom);
|
||||||
|
|
||||||
function highlightSiblings(n) {
|
function highlightSiblings(n) {
|
||||||
@@ -336,7 +348,6 @@ function MeshGraph({
|
|||||||
immediate.forEach((s) => {
|
immediate.forEach((s) => {
|
||||||
svg
|
svg
|
||||||
.selectAll(`.link-${s.index}`)
|
.selectAll(`.link-${s.index}`)
|
||||||
.transition()
|
|
||||||
.style('stroke', '#0066CC')
|
.style('stroke', '#0066CC')
|
||||||
.style('stroke-width', '3px')
|
.style('stroke-width', '3px')
|
||||||
.attr('marker-end', 'url(#end-active)');
|
.attr('marker-end', 'url(#end-active)');
|
||||||
@@ -352,19 +363,20 @@ function MeshGraph({
|
|||||||
immediate.forEach((s) => {
|
immediate.forEach((s) => {
|
||||||
svg
|
svg
|
||||||
.selectAll(`.link-${s.index}`)
|
.selectAll(`.link-${s.index}`)
|
||||||
.transition()
|
.style('stroke', (d) => renderLinkStatusColor(d.link_state))
|
||||||
.duration(50)
|
|
||||||
.style('stroke', (d) => {
|
|
||||||
if (d.link_state === 'removing') {
|
|
||||||
return '#C9190B';
|
|
||||||
}
|
|
||||||
if (d.link_state === 'disconnected') {
|
|
||||||
return '#c9700b';
|
|
||||||
}
|
|
||||||
return '#CCC';
|
|
||||||
})
|
|
||||||
.style('stroke-width', '2px')
|
.style('stroke-width', '2px')
|
||||||
.attr('marker-end', 'url(#end)');
|
.attr('marker-end', (d) => {
|
||||||
|
if (d.link_state === 'disconnected') {
|
||||||
|
return 'url(#end-disconnected)';
|
||||||
|
}
|
||||||
|
if (d.link_state === 'adding') {
|
||||||
|
return 'url(#end-adding)';
|
||||||
|
}
|
||||||
|
if (d.link_state === 'removing') {
|
||||||
|
return 'url(#end-removing)';
|
||||||
|
}
|
||||||
|
return 'url(#end)';
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +385,7 @@ function MeshGraph({
|
|||||||
// toggle rings
|
// toggle rings
|
||||||
svg
|
svg
|
||||||
.select(`circle.id-${n.id}`)
|
.select(`circle.id-${n.id}`)
|
||||||
.attr('stroke', '#ccc')
|
.attr('stroke', (d) => renderStateColor(d.node_state))
|
||||||
.attr('stroke-width', null);
|
.attr('stroke-width', null);
|
||||||
// show default empty state of tooltip
|
// show default empty state of tooltip
|
||||||
setIsNodeSelected(false);
|
setIsNodeSelected(false);
|
||||||
@@ -382,7 +394,7 @@ function MeshGraph({
|
|||||||
}
|
}
|
||||||
svg
|
svg
|
||||||
.selectAll('circle')
|
.selectAll('circle')
|
||||||
.attr('stroke', '#ccc')
|
.attr('stroke', (d) => renderStateColor(d.node_state))
|
||||||
.attr('stroke-width', null);
|
.attr('stroke-width', null);
|
||||||
svg
|
svg
|
||||||
.select(`circle.id-${n.id}`)
|
.select(`circle.id-${n.id}`)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ function TopologyView() {
|
|||||||
zoomIn={zoomIn}
|
zoomIn={zoomIn}
|
||||||
zoomOut={zoomOut}
|
zoomOut={zoomOut}
|
||||||
zoomFit={zoomFit}
|
zoomFit={zoomFit}
|
||||||
|
refresh={fetchMeshVisualizer}
|
||||||
resetZoom={resetZoom}
|
resetZoom={resetZoom}
|
||||||
showZoomControls={showZoomControls}
|
showZoomControls={showZoomControls}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const DEFAULT_NODE_COLOR = 'white';
|
|||||||
export const DEFAULT_NODE_HIGHLIGHT_COLOR = '#eee';
|
export const DEFAULT_NODE_HIGHLIGHT_COLOR = '#eee';
|
||||||
export const DEFAULT_NODE_LABEL_TEXT_COLOR = 'white';
|
export const DEFAULT_NODE_LABEL_TEXT_COLOR = 'white';
|
||||||
export const DEFAULT_NODE_SYMBOL_TEXT_COLOR = 'black';
|
export const DEFAULT_NODE_SYMBOL_TEXT_COLOR = 'black';
|
||||||
export const DEFAULT_NODE_STROKE_COLOR = '#ccc';
|
export const DEFAULT_NODE_STROKE_COLOR = '#6A6E73';
|
||||||
export const DEFAULT_FONT_SIZE = '12px';
|
export const DEFAULT_FONT_SIZE = '12px';
|
||||||
export const LABEL_TEXT_MAX_LENGTH = 15;
|
export const LABEL_TEXT_MAX_LENGTH = 15;
|
||||||
export const MARGIN = 15;
|
export const MARGIN = 15;
|
||||||
@@ -27,6 +27,13 @@ export const NODE_STATE_COLOR_KEY = {
|
|||||||
deprovisioning: '#666',
|
deprovisioning: '#666',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const LINK_STATE_COLOR_KEY = {
|
||||||
|
established: '#6A6E73',
|
||||||
|
disconnected: '#CCC',
|
||||||
|
adding: '#3E8635',
|
||||||
|
removing: '#C9190B',
|
||||||
|
};
|
||||||
|
|
||||||
export const NODE_TYPE_SYMBOL_KEY = {
|
export const NODE_TYPE_SYMBOL_KEY = {
|
||||||
hop: 'h',
|
hop: 'h',
|
||||||
execution: 'Ex',
|
execution: 'Ex',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { truncateString } from '../../../util/strings';
|
|||||||
import {
|
import {
|
||||||
NODE_STATE_COLOR_KEY,
|
NODE_STATE_COLOR_KEY,
|
||||||
NODE_TYPE_SYMBOL_KEY,
|
NODE_TYPE_SYMBOL_KEY,
|
||||||
|
LINK_STATE_COLOR_KEY,
|
||||||
LABEL_TEXT_MAX_LENGTH,
|
LABEL_TEXT_MAX_LENGTH,
|
||||||
ICONS,
|
ICONS,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
@@ -20,6 +21,12 @@ export function renderStateColor(nodeState) {
|
|||||||
return NODE_STATE_COLOR_KEY[nodeState] ? NODE_STATE_COLOR_KEY[nodeState] : '';
|
return NODE_STATE_COLOR_KEY[nodeState] ? NODE_STATE_COLOR_KEY[nodeState] : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderLinkStatusColor(linkState) {
|
||||||
|
return LINK_STATE_COLOR_KEY[linkState]
|
||||||
|
? LINK_STATE_COLOR_KEY[linkState]
|
||||||
|
: '#ccc';
|
||||||
|
}
|
||||||
|
|
||||||
export function renderLabelText(nodeState, name) {
|
export function renderLabelText(nodeState, name) {
|
||||||
if (typeof nodeState === 'string' && typeof name === 'string') {
|
if (typeof nodeState === 'string' && typeof name === 'string') {
|
||||||
return `${truncateString(name, LABEL_TEXT_MAX_LENGTH)}`;
|
return `${truncateString(name, LABEL_TEXT_MAX_LENGTH)}`;
|
||||||
@@ -45,8 +52,8 @@ export function renderLabelIcons(nodeState) {
|
|||||||
ready: 'checkmark',
|
ready: 'checkmark',
|
||||||
installed: 'clock',
|
installed: 'clock',
|
||||||
unavailable: 'exclaimation',
|
unavailable: 'exclaimation',
|
||||||
'provision-fail': 'exclaimation',
|
|
||||||
'deprovision-fail': 'exclaimation',
|
'deprovision-fail': 'exclaimation',
|
||||||
|
'provision-fail': 'exclaimation',
|
||||||
provisioning: 'plus',
|
provisioning: 'plus',
|
||||||
deprovisioning: 'minus',
|
deprovisioning: 'minus',
|
||||||
};
|
};
|
||||||
@@ -59,15 +66,17 @@ export function renderLabelIcons(nodeState) {
|
|||||||
export function renderIconPosition(nodeState, bbox) {
|
export function renderIconPosition(nodeState, bbox) {
|
||||||
if (nodeState) {
|
if (nodeState) {
|
||||||
const iconPositionMapper = {
|
const iconPositionMapper = {
|
||||||
ready: `translate(${bbox.x - 15}, ${bbox.y + 3}), scale(0.02)`,
|
ready: `translate(${bbox.x - 4.5}, ${bbox.y - 4.5}), scale(0.02)`,
|
||||||
installed: `translate(${bbox.x - 18}, ${bbox.y + 1}), scale(0.03)`,
|
installed: `translate(${bbox.x - 6.5}, ${bbox.y - 6.5}), scale(0.025)`,
|
||||||
unavailable: `translate(${bbox.x - 9}, ${bbox.y + 3}), scale(0.02)`,
|
unavailable: `translate(${bbox.x - 2}, ${bbox.y - 4.4}), scale(0.02)`,
|
||||||
'provision-fail': `translate(${bbox.x - 9}, ${bbox.y + 3}), scale(0.02)`,
|
'provision-fail': `translate(${bbox.x - 2}, ${bbox.y - 4}), scale(0.02)`,
|
||||||
'deprovision-fail': `translate(${bbox.x - 9}, ${
|
'deprovision-fail': `translate(${bbox.x - 2}, ${
|
||||||
bbox.y + 3
|
bbox.y - 4
|
||||||
|
}), scale(0.02)`,
|
||||||
|
provisioning: `translate(${bbox.x - 4.5}, ${bbox.y - 4.5}), scale(0.02)`,
|
||||||
|
deprovisioning: `translate(${bbox.x - 4.5}, ${
|
||||||
|
bbox.y - 4.5
|
||||||
}), scale(0.02)`,
|
}), scale(0.02)`,
|
||||||
provisioning: `translate(${bbox.x - 12}, ${bbox.y + 3}), scale(0.02)`,
|
|
||||||
deprovisioning: `translate(${bbox.x - 12}, ${bbox.y + 3}), scale(0.02)`,
|
|
||||||
};
|
};
|
||||||
return iconPositionMapper[nodeState] ? iconPositionMapper[nodeState] : ``;
|
return iconPositionMapper[nodeState] ? iconPositionMapper[nodeState] : ``;
|
||||||
}
|
}
|
||||||
@@ -86,6 +95,7 @@ export function redirectToDetailsPage(selectedNode, history) {
|
|||||||
export function renderLinkState(linkState) {
|
export function renderLinkState(linkState) {
|
||||||
const linkPattern = {
|
const linkPattern = {
|
||||||
established: null,
|
established: null,
|
||||||
|
disconnected: 3,
|
||||||
adding: 3,
|
adding: 3,
|
||||||
removing: 3,
|
removing: 3,
|
||||||
};
|
};
|
||||||
@@ -101,7 +111,9 @@ export function getRandomInt(min, max) {
|
|||||||
const generateRandomLinks = (n, r) => {
|
const generateRandomLinks = (n, r) => {
|
||||||
const links = [];
|
const links = [];
|
||||||
function getRandomLinkState() {
|
function getRandomLinkState() {
|
||||||
return ['established', 'adding', 'removing'][getRandomInt(0, 2)];
|
return ['established', 'adding', 'removing', 'disconnected'][
|
||||||
|
getRandomInt(0, 3)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
for (let i = 0; i < r; i++) {
|
for (let i = 0; i < r; i++) {
|
||||||
const link = {
|
const link = {
|
||||||
@@ -142,7 +154,7 @@ export const generateRandomNodes = (n) => {
|
|||||||
hostname: `node-${id}`,
|
hostname: `node-${id}`,
|
||||||
node_type: randomType,
|
node_type: randomType,
|
||||||
node_state: randomState,
|
node_state: randomState,
|
||||||
enabled: Math.random() < 0.5,
|
enabled: Math.random() < 0.9,
|
||||||
};
|
};
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ describe('renderIconPosition', () => {
|
|||||||
const bbox = { x: 400, y: 400, width: 10, height: 20 };
|
const bbox = { x: 400, y: 400, width: 10, height: 20 };
|
||||||
test('returns correct label icon', () => {
|
test('returns correct label icon', () => {
|
||||||
expect(renderIconPosition('ready', bbox)).toBe(
|
expect(renderIconPosition('ready', bbox)).toBe(
|
||||||
`translate(${bbox.x - 15}, ${bbox.y + 3}), scale(0.02)`
|
`translate(${bbox.x - 4.5}, ${bbox.y - 4.5}), scale(0.02)`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
test('returns empty string if state is not found', () => {
|
test('returns empty string if state is not found', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user