Feat: Map Node Stats (Version, Operating System etc) to each Node in Feed (#591)

* added functionality for specifying node details per each node

* Backend done
Added new item in Ranking
node_map, mapping node id to node detail

Co-authored-by: Cyndie Kamau <cyndiekamaa@gmail.com>

* feat: last frontend working version

* chore: Clean up unused code

* fix(frontend): update node details to carry 10 fields

* chore: remove unnecessary code

* chore: run cargo fmt for formatting

* chore: run prettier to format frontend

* fixed e2e tests added missing struct params

* remoted .idea file

* Hide new columns by default, default to - if no data, and remove .idea folder

---------

Co-authored-by: MrishoLukamba <abdulrazzaqlukamba@gmail.com>
Co-authored-by: Cyndie Kamau <cyndiekamaa@gmail.com>
This commit is contained in:
James Wilson
2024-09-25 14:01:42 +01:00
committed by GitHub
parent bb4c7272d8
commit 0cd8726ce5
34 changed files with 611 additions and 53 deletions
+17 -3
View File
@@ -46,6 +46,7 @@ export default class App extends React.Component {
constructor(props: Record<string, unknown>) {
super(props);
//todo! Check what's important to set to true, false
this.settings = new PersistentObject(
'settings',
{
@@ -55,8 +56,6 @@ export default class App extends React.Component {
networkId: false,
peers: true,
txs: true,
cpu: true,
mem: true,
upload: false,
download: false,
stateCacheSize: false,
@@ -65,12 +64,27 @@ export default class App extends React.Component {
diskWrite: false,
blocknumber: true,
blockhash: true,
blocktime: true,
finalized: false,
finalizedhash: false,
blocktime: true,
blockpropagation: true,
blocklasttime: false,
uptime: false,
version: false,
target_os: false,
target_arch: false,
cpu: false,
cpu_hashrate_score: true,
cpu_vendor: true,
core_count: false,
mem: true,
memory: false,
linux_distro: false,
linux_kernel: false,
memory_memcpy_score: true,
disk_sequential_write_score: true,
disk_random_write_score: true,
is_virtual_machine: false,
},
(settings) => {
const selectedColumns = this.selectedColumns(settings);
-1
View File
@@ -269,7 +269,6 @@ export class Connection {
case ACTIONS.NodeStats: {
const [id, nodeStats] = message.payload;
nodes.mutAndMaybeSort(
id,
(node) => node.updateStats(nodeStats),
+24 -1
View File
@@ -25,6 +25,15 @@ export type NodeId = Id<'Node'>;
export type NodeName = Opaque<string, 'NodeName'>;
export type NodeImplementation = Opaque<string, 'NodeImplementation'>;
export type NodeVersion = Opaque<string, 'NodeVersion'>;
export type OperatingSystem = Opaque<string, 'OperatingSystem'>;
export type CpuArchitecture = Opaque<string, 'CpuArchitecture'>;
export type Cpu = string;
export type CpuCores = number;
export type TargetEnv = string;
export type Memory = number;
export type VirtualMachine = boolean;
export type LinuxKernel = string;
export type LinuxDistro = string;
export type BlockNumber = Opaque<number, 'BlockNumber'>;
export type BlockHash = Opaque<string, 'BlockHash'>;
export type Address = Opaque<string, 'Address'>;
@@ -43,6 +52,15 @@ export type Bytes = Opaque<number, 'Bytes'>;
export type BytesPerSecond = Opaque<number, 'BytesPerSecond'>;
export type NetworkId = Opaque<string, 'NetworkId'>;
export type NodeSysInfo = {
cpu: string;
memory: number;
core_count: number;
linux_kernel: string;
linux_distro: string;
is_virtual_machine: boolean;
};
export type BlockDetails = [
BlockNumber,
BlockHash,
@@ -56,8 +74,13 @@ export type NodeDetails = [
NodeVersion,
Maybe<Address>,
Maybe<NetworkId>,
Maybe<string>
OperatingSystem,
CpuArchitecture,
TargetEnv,
undefined,
NodeSysInfo
];
export type NodeStats = [PeerCount, TransactionCount];
export type NodeIO = [Array<Bytes>];
export type NodeHardware = [
+19 -1
View File
@@ -38,6 +38,15 @@ import {
BlockPropagationColumn,
LastBlockColumn,
UptimeColumn,
CpuArchitectureColumn, //extra columns added
CpuColumn,
CpuCoresColumn,
LinuxKernelColumn,
IsVirtualMachineColumn,
MemoryColumn,
OperatingSystemColumn,
VersionColumn,
LinuxDistroColumn,
} from './';
export type Column =
@@ -58,7 +67,16 @@ export type Column =
| typeof BlockTimeColumn
| typeof BlockPropagationColumn
| typeof LastBlockColumn
| typeof UptimeColumn;
| typeof UptimeColumn
| typeof CpuArchitectureColumn
| typeof CpuColumn
| typeof CpuCoresColumn
| typeof LinuxDistroColumn
| typeof LinuxKernelColumn
| typeof IsVirtualMachineColumn
| typeof MemoryColumn
| typeof OperatingSystemColumn
| typeof VersionColumn;
export interface ColumnProps {
node: Node;
@@ -0,0 +1,43 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which CPU Architecture the node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class CpuArchitectureColumn extends React.Component<ColumnProps> {
public static readonly label = 'CPU Architecture';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'target_arch';
public static readonly sortBy = ({ target_arch }: Node) => target_arch || '';
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.hash;
}
render() {
const { target_arch } = this.props.node;
this.data = target_arch;
return <td className="Column">{target_arch || '-'}</td>;
}
}
@@ -0,0 +1,44 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which CPU type the node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class CpuColumn extends React.Component<ColumnProps> {
public static readonly label = 'CPU Column';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'cpu';
public static readonly sortBy = ({ cpu }: Node) => cpu || 0;
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.cpu;
}
render() {
const { cpu } = this.props.node;
this.data = cpu;
return <td className="Column">{cpu || '-'}</td>;
}
}
@@ -0,0 +1,41 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class CpuCoresColumn extends React.Component<ColumnProps> {
public static readonly label = 'CPU Cores';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'core_count';
public static readonly sortBy = ({ core_count }: Node) => core_count || 0;
private data: number;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.core_count;
}
render() {
const { core_count } = this.props.node;
this.data = core_count;
return <td className="Column">{core_count || '-'}</td>;
}
}
@@ -0,0 +1,44 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying whether each node runs a VM or not
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class IsVirtualMachineColumn extends React.Component<ColumnProps> {
public static readonly label = 'Virtual Machine';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'is_virtual_machine';
public static readonly sortBy = ({ is_virtual_machine }: Node) =>
is_virtual_machine || false;
private data: boolean;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.is_virtual_machine;
}
render() {
const { is_virtual_machine } = this.props.node;
this.data = is_virtual_machine;
return <td className="Column">{is_virtual_machine ? 'Yes' : 'No'}</td>;
}
}
@@ -0,0 +1,45 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying type of distro each node is running (Linux Distribution)
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class LinuxDistroColumn extends React.Component<ColumnProps> {
public static readonly label = 'Linux Distro';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'linux_distro';
public static readonly sortBy = ({ linux_distro }: Node) =>
linux_distro || '';
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.linux_distro;
}
render() {
const { linux_distro } = this.props.node;
this.data = linux_distro;
return <td className="Column">{linux_distro || '-'}</td>;
}
}
@@ -0,0 +1,43 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which kernel each node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class LinuxKernelColumn extends React.Component<ColumnProps> {
public static readonly label = 'Linux Kernel';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'linux_kernel';
public static readonly sortBy = ({ linux_kernel }: Node) => linux_kernel || 0;
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.linux_kernel;
}
render() {
const { linux_kernel } = this.props.node;
this.data = linux_kernel;
return <td className="Column">{linux_kernel || '-'}</td>;
}
}
@@ -0,0 +1,43 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column specifying type of memory each node has
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class MemoryColumn extends React.Component<ColumnProps> {
public static readonly label = 'memory';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'memory';
public static readonly sortBy = ({ memory }: Node) => memory || '';
private data: number;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.memory;
}
render() {
const { memory } = this.props.node;
this.data = memory;
return <td className="Column">{memory || '-'}</td>;
}
}
@@ -0,0 +1,44 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which OS the node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class OperatingSystemColumn extends React.Component<ColumnProps> {
public static readonly label = 'OS';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'target_os';
public static readonly sortBy = ({ target_os }: Node) => target_os || '';
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.hash;
}
render() {
const { target_os } = this.props.node;
this.data = target_os;
return <td className="Column">{target_os || '-'}</td>;
}
}
@@ -0,0 +1,44 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which polkadot version node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class VersionColumn extends React.Component<ColumnProps> {
public static readonly label = 'version';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'version';
public static readonly sortBy = ({ version }: Node) => version || '';
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.version;
}
render() {
const { version } = this.props.node;
this.data = version;
return <td className="Column">{version || '-'}</td>;
}
}
@@ -33,3 +33,12 @@ export * from './BlockTimeColumn';
export * from './BlockPropagationColumn';
export * from './LastBlockColumn';
export * from './UptimeColumn';
export * from './CpuArchitectureColumn'; //extra columns added
export * from './CpuCoresColumn';
export * from './LinuxDistroColumn';
export * from './IsVirtualMachineColumn';
export * from './MemoryColumn';
export * from './CpuColumn';
export * from './OperatingSystemColumn';
export * from './VersionColumn';
export * from './LinuxKernelColumn';
+19 -1
View File
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// Each Row in the List Page
import * as React from 'react';
import { Types } from '../../common';
import { Node } from '../../state';
@@ -38,6 +39,15 @@ import {
BlockPropagationColumn,
LastBlockColumn,
UptimeColumn,
CpuArchitectureColumn,
CpuColumn,
CpuCoresColumn,
MemoryColumn,
OperatingSystemColumn,
VersionColumn,
IsVirtualMachineColumn,
LinuxDistroColumn,
LinuxKernelColumn,
} from './';
import './Row.css';
@@ -72,8 +82,16 @@ export class Row extends React.Component<RowProps, RowState> {
BlockPropagationColumn,
LastBlockColumn,
UptimeColumn,
VersionColumn,
OperatingSystemColumn,
CpuArchitectureColumn,
CpuColumn,
CpuCoresColumn,
MemoryColumn,
LinuxDistroColumn,
LinuxKernelColumn,
IsVirtualMachineColumn,
];
private renderedChangeRef = 0;
public shouldComponentUpdate(nextProps: RowProps): boolean {
+63 -3
View File
@@ -26,8 +26,8 @@ export const PINNED_CHAINS = {
};
export function comparePinnedChains(a: string, b: string) {
const aWeight = PINNED_CHAINS[a] || 1024;
const bWeight = PINNED_CHAINS[b] || 1024;
const aWeight: number = (PINNED_CHAINS as any)[a] || 1024;
const bWeight: number = (PINNED_CHAINS as any)[b] || 1024;
return aWeight - bWeight;
}
@@ -62,6 +62,17 @@ export class Node {
public readonly validator: Maybe<Types.Address>;
public readonly networkId: Maybe<Types.NetworkId>;
public readonly startupTime: Maybe<Types.Timestamp>;
public readonly target_os: Types.OperatingSystem;
public readonly target_arch: Types.CpuArchitecture;
public readonly target_env: Types.TargetEnv;
public readonly sysInfo: Types.NodeSysInfo;
public readonly cpu: Types.Cpu;
public readonly memory: Types.Memory;
public readonly core_count: Types.CpuCores;
public readonly linux_kernel: Types.LinuxKernel;
public readonly linux_distro: Types.LinuxDistro;
public readonly is_virtual_machine: Types.VirtualMachine;
public readonly sortableName: string;
public readonly sortableVersion: number;
@@ -102,7 +113,18 @@ export class Node {
location: Maybe<Types.NodeLocation>,
startupTime: Maybe<Types.Timestamp>
) {
const [name, implementation, version, validator, networkId] = nodeDetails;
const [
name,
implementation,
version,
validator,
networkId,
target_os,
target_arch,
target_env,
customNullField,
sysInfo,
] = nodeDetails; //step 4
this.pinned = pinned;
@@ -113,6 +135,30 @@ export class Node {
this.validator = validator;
this.networkId = networkId;
this.startupTime = startupTime;
this.target_os = target_os;
this.target_arch = target_arch;
this.target_env = target_env;
this.sysInfo = {
cpu: sysInfo?.cpu ?? '', // Default value or some global constant
memory: sysInfo?.memory ?? 0,
core_count: sysInfo?.core_count ?? 0,
linux_kernel: sysInfo?.linux_kernel ?? '',
linux_distro: sysInfo?.linux_distro ?? '',
is_virtual_machine: sysInfo?.is_virtual_machine ?? false,
};
this.cpu = sysInfo && sysInfo.cpu ? sysInfo.cpu : '';
this.memory = sysInfo && sysInfo.memory ? Number(sysInfo.memory) : 0;
this.core_count =
sysInfo && sysInfo.core_count ? Number(sysInfo.core_count) : 0;
this.linux_kernel =
sysInfo && sysInfo.linux_kernel ? sysInfo.linux_kernel : '';
this.linux_distro =
sysInfo && sysInfo.linux_distro ? sysInfo.linux_distro : '';
this.is_virtual_machine =
sysInfo && sysInfo.is_virtual_machine
? Boolean(sysInfo.is_virtual_machine)
: false;
const [major = 0, minor = 0, patch = 0] = (version || '0.0.0')
.split('.')
@@ -255,6 +301,20 @@ export interface StateSettings {
blockpropagation: boolean;
blocklasttime: boolean;
uptime: boolean;
version: boolean;
target_os: boolean;
target_arch: boolean;
cpu: boolean;
core_count: boolean;
memory: boolean;
is_virtual_machine: boolean;
linux_distro: boolean;
linux_kernel: boolean;
cpu_hashrate_score: boolean;
memory_memcpy_score: boolean;
disk_sequential_write_score: boolean;
disk_random_write_score: boolean;
cpu_vendor: boolean;
}
export interface State {